mirror of
https://github.com/lwfinger/rtl8188eu.git
synced 2024-11-22 20:43:40 +00:00
rtl8188eu: Add hostapd-2.9 modified for the rtl871x driver
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
parent
13531f3844
commit
903b6fcb82
644 changed files with 378143 additions and 0 deletions
171
hostapd-2.9/CONTRIBUTIONS
Normal file
171
hostapd-2.9/CONTRIBUTIONS
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
Contributions to hostap.git
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
This software is distributed under a permissive open source license to
|
||||||
|
allow it to be used in any projects, whether open source or proprietary.
|
||||||
|
Contributions to the project are welcome and it is important to maintain
|
||||||
|
clear record of contributions and terms under which they are licensed.
|
||||||
|
To help with this, following procedure is used to allow acceptance and
|
||||||
|
recording of the terms.
|
||||||
|
|
||||||
|
All contributions are expected to be licensed under the modified BSD
|
||||||
|
license (see below). Acknowledgment of the terms is tracked through
|
||||||
|
inclusion of Signed-off-by tag in the contributions at the end of the
|
||||||
|
commit log message. This tag indicates that the contributor agrees with
|
||||||
|
the Developer Certificate of Origin (DCO) version 1.1 terms (see below;
|
||||||
|
also available from http://developercertificate.org/).
|
||||||
|
|
||||||
|
|
||||||
|
The current requirements for contributions to hostap.git
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
To indicate your acceptance of Developer's Certificate of Origin 1.1
|
||||||
|
terms, please add the following line to the end of the commit message
|
||||||
|
for each contribution you make to the project:
|
||||||
|
|
||||||
|
Signed-off-by: Your Name <your@email.example.org>
|
||||||
|
|
||||||
|
using your real name. Pseudonyms or anonymous contributions cannot
|
||||||
|
unfortunately be accepted.
|
||||||
|
|
||||||
|
|
||||||
|
The preferred method of submitting the contribution to the project is by
|
||||||
|
email to the hostap mailing list:
|
||||||
|
hostap@lists.infradead.org
|
||||||
|
Note that the list may require subscription before accepting message
|
||||||
|
without moderation. You can subscribe to the list at this address:
|
||||||
|
http://lists.infradead.org/mailman/listinfo/hostap
|
||||||
|
|
||||||
|
The message should contain an inlined patch against the current
|
||||||
|
development branch (i.e., the master branch of
|
||||||
|
git://w1.fi/hostap.git). Please make sure the software you use for
|
||||||
|
sending the patch does not corrupt whitespace. If that cannot be fixed
|
||||||
|
for some reason, it is better to include an attached version of the
|
||||||
|
patch file than just send a whitespace damaged version in the message
|
||||||
|
body.
|
||||||
|
|
||||||
|
The patches should be separate logical changes rather than doing
|
||||||
|
everything in a single patch. In other words, please keep cleanup, new
|
||||||
|
features, and bug fixes all in their own patches. Each patch needs a
|
||||||
|
commit log that describes the changes (what the changes fix, what
|
||||||
|
functionality is added, why the changes are useful, etc.).
|
||||||
|
|
||||||
|
Please try to follow the coding style used in the project.
|
||||||
|
|
||||||
|
In general, the best way of generating a suitable formatted patch file
|
||||||
|
is by committing the changes to a cloned git repository and using git
|
||||||
|
format-patch. The patch can then be sent, e.g., with git send-email.
|
||||||
|
|
||||||
|
|
||||||
|
History of license and contributions terms
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
Until February 11, 2012, in case of most files in hostap.git, "under the
|
||||||
|
open source license indicated in the file" means that the contribution
|
||||||
|
is licensed both under GPL v2 and modified BSD license (see below) and
|
||||||
|
the choice between these licenses is given to anyone who redistributes
|
||||||
|
or uses the software. As such, the contribution has to be licensed under
|
||||||
|
both options to allow this choice.
|
||||||
|
|
||||||
|
As of February 11, 2012, the project has chosen to use only the BSD
|
||||||
|
license option for future distribution. As such, the GPL v2 license
|
||||||
|
option is no longer used and the contributions are not required to be
|
||||||
|
licensed until GPL v2. In case of most files in hostap.git, "under the
|
||||||
|
open source license indicated in the file" means that the contribution
|
||||||
|
is licensed under the modified BSD license (see below).
|
||||||
|
|
||||||
|
Until February 13, 2014, the project used an extended version of the DCO
|
||||||
|
that included the identical items (a) through (d) from DCO 1.1 and an
|
||||||
|
additional item (e):
|
||||||
|
|
||||||
|
(e) The contribution can be licensed under the modified BSD license
|
||||||
|
as shown below even in case of files that are currently licensed
|
||||||
|
under other terms.
|
||||||
|
|
||||||
|
This was used during the period when some of the files included the old
|
||||||
|
license terms. Acceptance of this extended DCO version was indicated
|
||||||
|
with a Signed-hostap tag in the commit message. This additional item (e)
|
||||||
|
was used to collect explicit approval to license the contribution with
|
||||||
|
only the modified BSD license (see below), i.e., without the GPL v2
|
||||||
|
option. This was done to allow simpler licensing terms to be used in the
|
||||||
|
future. It should be noted that the modified BSD license is compatible
|
||||||
|
with GNU GPL and as such, this possible move to simpler licensing option
|
||||||
|
does not prevent use of this software in GPL projects.
|
||||||
|
|
||||||
|
|
||||||
|
===[ start quote from http://developercertificate.org/ ]=======================
|
||||||
|
|
||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
|
||||||
|
===[ end quote from http://developercertificate.org/ ]=========================
|
||||||
|
|
||||||
|
|
||||||
|
The license terms used for hostap.git files
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Modified BSD license (no advertisement clause):
|
||||||
|
|
||||||
|
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||||
|
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.
|
||||||
|
|
||||||
|
3. Neither the name(s) of the above-listed copyright holder(s) nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
hostapd-2.9/COPYING
Normal file
22
hostapd-2.9/COPYING
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
wpa_supplicant and hostapd
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
|
||||||
|
See the README file for the current license terms.
|
||||||
|
|
||||||
|
This software was previously distributed under BSD/GPL v2 dual license
|
||||||
|
terms that allowed either of those license alternatives to be
|
||||||
|
selected. As of February 11, 2012, the project has chosen to use only
|
||||||
|
the BSD license option for future distribution. As such, the GPL v2
|
||||||
|
license option is no longer used. It should be noted that the BSD
|
||||||
|
license option (the one with advertisement clause removed) is compatible
|
||||||
|
with GPL and as such, does not prevent use of this software in projects
|
||||||
|
that use GPL.
|
||||||
|
|
||||||
|
Some of the files may still include pointers to GPL version 2 license
|
||||||
|
terms. However, such copyright and license notifications are maintained
|
||||||
|
only for attribution purposes and any distribution of this software
|
||||||
|
after February 11, 2012 is no longer under the GPL v2 option.
|
56
hostapd-2.9/README
Normal file
56
hostapd-2.9/README
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
wpa_supplicant and hostapd
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
These programs are licensed under the BSD license (the one with
|
||||||
|
advertisement clause removed).
|
||||||
|
|
||||||
|
If you are submitting changes to the project, please see CONTRIBUTIONS
|
||||||
|
file for more instructions.
|
||||||
|
|
||||||
|
|
||||||
|
This package may include either wpa_supplicant, hostapd, or both. See
|
||||||
|
README file respective subdirectories (wpa_supplicant/README or
|
||||||
|
hostapd/README) for more details.
|
||||||
|
|
||||||
|
Source code files were moved around in v0.6.x releases and compared to
|
||||||
|
earlier releases, the programs are now built by first going to a
|
||||||
|
subdirectory (wpa_supplicant or hostapd) and creating build
|
||||||
|
configuration (.config) and running 'make' there (for Linux/BSD/cygwin
|
||||||
|
builds).
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
This software may be distributed, used, and modified under the terms of
|
||||||
|
BSD license:
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
3. Neither the name(s) of the above-listed copyright holder(s) nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
1148
hostapd-2.9/hostapd/Android.mk
Normal file
1148
hostapd-2.9/hostapd/Android.mk
Normal file
File diff suppressed because it is too large
Load diff
1278
hostapd-2.9/hostapd/ChangeLog
Normal file
1278
hostapd-2.9/hostapd/ChangeLog
Normal file
File diff suppressed because it is too large
Load diff
1380
hostapd-2.9/hostapd/Makefile
Normal file
1380
hostapd-2.9/hostapd/Makefile
Normal file
File diff suppressed because it is too large
Load diff
354
hostapd-2.9/hostapd/README
Normal file
354
hostapd-2.9/hostapd/README
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
|
||||||
|
Authenticator and RADIUS authentication server
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
This program is licensed under the BSD license (the one with
|
||||||
|
advertisement clause removed).
|
||||||
|
|
||||||
|
If you are submitting changes to the project, please see CONTRIBUTIONS
|
||||||
|
file for more instructions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
This software may be distributed, used, and modified under the terms of
|
||||||
|
BSD license:
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
3. Neither the name(s) of the above-listed copyright holder(s) nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
Originally, hostapd was an optional user space component for Host AP
|
||||||
|
driver. It adds more features to the basic IEEE 802.11 management
|
||||||
|
included in the kernel driver: using external RADIUS authentication
|
||||||
|
server for MAC address based access control, IEEE 802.1X Authenticator
|
||||||
|
and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN)
|
||||||
|
Authenticator and dynamic TKIP/CCMP keying.
|
||||||
|
|
||||||
|
The current version includes support for other drivers, an integrated
|
||||||
|
EAP server (i.e., allow full authentication without requiring
|
||||||
|
an external RADIUS authentication server), and RADIUS authentication
|
||||||
|
server for EAP authentication.
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
Current hardware/software requirements:
|
||||||
|
- drivers:
|
||||||
|
Host AP driver for Prism2/2.5/3.
|
||||||
|
(http://w1.fi/hostap-driver.html)
|
||||||
|
Please note that station firmware version needs to be 1.7.0 or newer
|
||||||
|
to work in WPA mode.
|
||||||
|
|
||||||
|
mac80211-based drivers that support AP mode (with driver=nl80211).
|
||||||
|
This includes drivers for Atheros (ath9k) and Broadcom (b43)
|
||||||
|
chipsets.
|
||||||
|
|
||||||
|
Any wired Ethernet driver for wired IEEE 802.1X authentication
|
||||||
|
(experimental code)
|
||||||
|
|
||||||
|
FreeBSD -current
|
||||||
|
BSD net80211 layer (e.g., Atheros driver)
|
||||||
|
|
||||||
|
|
||||||
|
Build configuration
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
In order to be able to build hostapd, you will need to create a build
|
||||||
|
time configuration file, .config that selects which optional
|
||||||
|
components are included. See defconfig file for example configuration
|
||||||
|
and list of available options.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
IEEE 802.1X
|
||||||
|
===========
|
||||||
|
|
||||||
|
IEEE Std 802.1X-2001 is a standard for port-based network access
|
||||||
|
control. In case of IEEE 802.11 networks, a "virtual port" is used
|
||||||
|
between each associated station and the AP. IEEE 802.11 specifies
|
||||||
|
minimal authentication mechanism for stations, whereas IEEE 802.1X
|
||||||
|
introduces a extensible mechanism for authenticating and authorizing
|
||||||
|
users.
|
||||||
|
|
||||||
|
IEEE 802.1X uses elements called Supplicant, Authenticator, Port
|
||||||
|
Access Entity, and Authentication Server. Supplicant is a component in
|
||||||
|
a station and it performs the authentication with the Authentication
|
||||||
|
Server. An access point includes an Authenticator that relays the packets
|
||||||
|
between a Supplicant and an Authentication Server. In addition, it has a
|
||||||
|
Port Access Entity (PAE) with Authenticator functionality for
|
||||||
|
controlling the virtual port authorization, i.e., whether to accept
|
||||||
|
packets from or to the station.
|
||||||
|
|
||||||
|
IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames
|
||||||
|
between a Supplicant and an Authenticator are sent using EAP over LAN
|
||||||
|
(EAPOL) and the Authenticator relays these frames to the Authentication
|
||||||
|
Server (and similarly, relays the messages from the Authentication
|
||||||
|
Server to the Supplicant). The Authentication Server can be colocated with the
|
||||||
|
Authenticator, in which case there is no need for additional protocol
|
||||||
|
for EAP frame transmission. However, a more common configuration is to
|
||||||
|
use an external Authentication Server and encapsulate EAP frame in the
|
||||||
|
frames used by that server. RADIUS is suitable for this, but IEEE
|
||||||
|
802.1X would also allow other mechanisms.
|
||||||
|
|
||||||
|
Host AP driver includes PAE functionality in the kernel driver. It
|
||||||
|
is a relatively simple mechanism for denying normal frames going to
|
||||||
|
or coming from an unauthorized port. PAE allows IEEE 802.1X related
|
||||||
|
frames to be passed between the Supplicant and the Authenticator even
|
||||||
|
on an unauthorized port.
|
||||||
|
|
||||||
|
User space daemon, hostapd, includes Authenticator functionality. It
|
||||||
|
receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap
|
||||||
|
device that is also used with IEEE 802.11 management frames. The
|
||||||
|
frames to the Supplicant are sent using the same device.
|
||||||
|
|
||||||
|
The normal configuration of the Authenticator would use an external
|
||||||
|
Authentication Server. hostapd supports RADIUS encapsulation of EAP
|
||||||
|
packets, so the Authentication Server should be a RADIUS server, like
|
||||||
|
FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd
|
||||||
|
relays the frames between the Supplicant and the Authentication
|
||||||
|
Server. It also controls the PAE functionality in the kernel driver by
|
||||||
|
controlling virtual port authorization, i.e., station-AP
|
||||||
|
connection, based on the IEEE 802.1X state.
|
||||||
|
|
||||||
|
When a station would like to use the services of an access point, it
|
||||||
|
will first perform IEEE 802.11 authentication. This is normally done
|
||||||
|
with open systems authentication, so there is no security. After
|
||||||
|
this, IEEE 802.11 association is performed. If IEEE 802.1X is
|
||||||
|
configured to be used, the virtual port for the station is set in
|
||||||
|
Unauthorized state and only IEEE 802.1X frames are accepted at this
|
||||||
|
point. The Authenticator will then ask the Supplicant to authenticate
|
||||||
|
with the Authentication Server. After this is completed successfully,
|
||||||
|
the virtual port is set to Authorized state and frames from and to the
|
||||||
|
station are accepted.
|
||||||
|
|
||||||
|
Host AP configuration for IEEE 802.1X
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
The user space daemon has its own configuration file that can be used to
|
||||||
|
define AP options. Distribution package contains an example
|
||||||
|
configuration file (hostapd/hostapd.conf) that can be used as a basis
|
||||||
|
for configuration. It includes examples of all supported configuration
|
||||||
|
options and short description of each option. hostapd should be started
|
||||||
|
with full path to the configuration file as the command line argument,
|
||||||
|
e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless
|
||||||
|
LAN card, you can use one hostapd process for multiple interfaces by
|
||||||
|
giving a list of configuration files (one per interface) in the command
|
||||||
|
line.
|
||||||
|
|
||||||
|
hostapd includes a minimal co-located IEEE 802.1X server which can be
|
||||||
|
used to test IEEE 802.1X authentication. However, it should not be
|
||||||
|
used in normal use since it does not provide any security. This can be
|
||||||
|
configured by setting ieee8021x and minimal_eap options in the
|
||||||
|
configuration file.
|
||||||
|
|
||||||
|
An external Authentication Server (RADIUS) is configured with
|
||||||
|
auth_server_{addr,port,shared_secret} options. In addition,
|
||||||
|
ieee8021x and own_ip_addr must be set for this mode. With such
|
||||||
|
configuration, the co-located Authentication Server is not used and EAP
|
||||||
|
frames will be relayed using EAPOL between the Supplicant and the
|
||||||
|
Authenticator and RADIUS encapsulation between the Authenticator and
|
||||||
|
the Authentication Server. Other than this, the functionality is similar
|
||||||
|
to the case with the co-located Authentication Server.
|
||||||
|
|
||||||
|
Authentication Server
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
|
||||||
|
Authentication Server with hostapd Authenticator. FreeRADIUS
|
||||||
|
(http://www.freeradius.org/) has been successfully tested with hostapd
|
||||||
|
Authenticator.
|
||||||
|
|
||||||
|
Automatic WEP key configuration
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
EAP/TLS generates a session key that can be used to send WEP keys from
|
||||||
|
an AP to authenticated stations. The Authenticator in hostapd can be
|
||||||
|
configured to automatically select a random default/broadcast key
|
||||||
|
(shared by all authenticated stations) with wep_key_len_broadcast
|
||||||
|
option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition,
|
||||||
|
wep_key_len_unicast option can be used to configure individual unicast
|
||||||
|
keys for stations. This requires support for individual keys in the
|
||||||
|
station driver.
|
||||||
|
|
||||||
|
WEP keys can be automatically updated by configuring rekeying. This
|
||||||
|
will improve security of the network since same WEP key will only be
|
||||||
|
used for a limited period of time. wep_rekey_period option sets the
|
||||||
|
interval for rekeying in seconds.
|
||||||
|
|
||||||
|
|
||||||
|
WPA/WPA2
|
||||||
|
========
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
Supported WPA/IEEE 802.11i features:
|
||||||
|
- WPA-PSK ("WPA-Personal")
|
||||||
|
- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
|
||||||
|
- key management for CCMP, TKIP, WEP104, WEP40
|
||||||
|
- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication
|
||||||
|
|
||||||
|
WPA
|
||||||
|
---
|
||||||
|
|
||||||
|
The original security mechanism of IEEE 802.11 standard was not
|
||||||
|
designed to be strong and has proved to be insufficient for most
|
||||||
|
networks that require some kind of security. Task group I (Security)
|
||||||
|
of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
|
||||||
|
to address the flaws of the base standard and has in practice
|
||||||
|
completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
|
||||||
|
802.11 standard was approved in June 2004 and this amendment was
|
||||||
|
published in July 2004.
|
||||||
|
|
||||||
|
Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
|
||||||
|
IEEE 802.11i work (draft 3.0) to define a subset of the security
|
||||||
|
enhancements that can be implemented with existing wlan hardware. This
|
||||||
|
is called Wi-Fi Protected Access<TM> (WPA). This has now become a
|
||||||
|
mandatory component of interoperability testing and certification done
|
||||||
|
by Wi-Fi Alliance.
|
||||||
|
|
||||||
|
IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
|
||||||
|
for protecting wireless networks. WEP uses RC4 with 40-bit keys,
|
||||||
|
24-bit initialization vector (IV), and CRC32 to protect against packet
|
||||||
|
forgery. All these choices have proven to be insufficient: key space is
|
||||||
|
too small against current attacks, RC4 key scheduling is insufficient
|
||||||
|
(beginning of the pseudorandom stream should be skipped), IV space is
|
||||||
|
too small and IV reuse makes attacks easier, there is no replay
|
||||||
|
protection, and non-keyed authentication does not protect against bit
|
||||||
|
flipping packet data.
|
||||||
|
|
||||||
|
WPA is an intermediate solution for the security issues. It uses
|
||||||
|
Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a
|
||||||
|
compromise on strong security and possibility to use existing
|
||||||
|
hardware. It still uses RC4 for the encryption like WEP, but with
|
||||||
|
per-packet RC4 keys. In addition, it implements replay protection,
|
||||||
|
keyed packet authentication mechanism (Michael MIC).
|
||||||
|
|
||||||
|
Keys can be managed using two different mechanisms. WPA can either use
|
||||||
|
an external authentication server (e.g., RADIUS) and EAP just like
|
||||||
|
IEEE 802.1X is using or pre-shared keys without need for additional
|
||||||
|
servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
|
||||||
|
respectively. Both mechanisms will generate a master session key for
|
||||||
|
the Authenticator (AP) and Supplicant (client station).
|
||||||
|
|
||||||
|
WPA implements a new key handshake (4-Way Handshake and Group Key
|
||||||
|
Handshake) for generating and exchanging data encryption keys between
|
||||||
|
the Authenticator and Supplicant. This handshake is also used to
|
||||||
|
verify that both Authenticator and Supplicant know the master session
|
||||||
|
key. These handshakes are identical regardless of the selected key
|
||||||
|
management mechanism (only the method for generating master session
|
||||||
|
key changes).
|
||||||
|
|
||||||
|
|
||||||
|
IEEE 802.11i / WPA2
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The design for parts of IEEE 802.11i that were not included in WPA has
|
||||||
|
finished (May 2004) and this amendment to IEEE 802.11 was approved in
|
||||||
|
June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
|
||||||
|
version of WPA called WPA2. This includes, e.g., support for more
|
||||||
|
robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
|
||||||
|
to replace TKIP and optimizations for handoff (reduced number of
|
||||||
|
messages in initial key handshake, pre-authentication, and PMKSA caching).
|
||||||
|
|
||||||
|
Some wireless LAN vendors are already providing support for CCMP in
|
||||||
|
their WPA products. There is no "official" interoperability
|
||||||
|
certification for CCMP and/or mixed modes using both TKIP and CCMP, so
|
||||||
|
some interoperability issues can be expected even though many
|
||||||
|
combinations seem to be working with equipment from different vendors.
|
||||||
|
Testing for WPA2 is likely to start during the second half of 2004.
|
||||||
|
|
||||||
|
hostapd configuration for WPA/WPA2
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
# Enable WPA. Setting this variable configures the AP to require WPA (either
|
||||||
|
# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
|
||||||
|
# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
|
||||||
|
# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
|
||||||
|
# RADIUS authentication server must be configured, and WPA-EAP must be included
|
||||||
|
# in wpa_key_mgmt.
|
||||||
|
# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
|
||||||
|
# and/or WPA2 (full IEEE 802.11i/RSN):
|
||||||
|
# bit0 = WPA
|
||||||
|
# bit1 = IEEE 802.11i/RSN (WPA2)
|
||||||
|
#wpa=1
|
||||||
|
|
||||||
|
# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
|
||||||
|
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
|
||||||
|
# (8..63 characters) that will be converted to PSK. This conversion uses SSID
|
||||||
|
# so the PSK changes when ASCII passphrase is used and the SSID is changed.
|
||||||
|
#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||||
|
#wpa_passphrase=secret passphrase
|
||||||
|
|
||||||
|
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
|
||||||
|
# entries are separated with a space.
|
||||||
|
#wpa_key_mgmt=WPA-PSK WPA-EAP
|
||||||
|
|
||||||
|
# Set of accepted cipher suites (encryption algorithms) for pairwise keys
|
||||||
|
# (unicast packets). This is a space separated list of algorithms:
|
||||||
|
# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i]
|
||||||
|
# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i]
|
||||||
|
# Group cipher suite (encryption algorithm for broadcast and multicast frames)
|
||||||
|
# is automatically selected based on this configuration. If only CCMP is
|
||||||
|
# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
|
||||||
|
# TKIP will be used as the group cipher.
|
||||||
|
#wpa_pairwise=TKIP CCMP
|
||||||
|
|
||||||
|
# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
|
||||||
|
# seconds.
|
||||||
|
#wpa_group_rekey=600
|
||||||
|
|
||||||
|
# Time interval for rekeying GMK (master key used internally to generate GTKs
|
||||||
|
# (in seconds).
|
||||||
|
#wpa_gmk_rekey=86400
|
||||||
|
|
||||||
|
# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
|
||||||
|
# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
|
||||||
|
# authentication and key handshake before actually associating with a new AP.
|
||||||
|
#rsn_preauth=1
|
||||||
|
#
|
||||||
|
# Space separated list of interfaces from which pre-authentication frames are
|
||||||
|
# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
|
||||||
|
# interface that are used for connections to other APs. This could include
|
||||||
|
# wired interfaces and WDS links. The normal wireless data interface towards
|
||||||
|
# associated stations (e.g., wlan0) should not be added, since
|
||||||
|
# pre-authentication is only used with APs other than the currently associated
|
||||||
|
# one.
|
||||||
|
#rsn_preauth_interfaces=eth0
|
160
hostapd-2.9/hostapd/README-MULTI-AP
Normal file
160
hostapd-2.9/hostapd/README-MULTI-AP
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
hostapd, wpa_supplicant and the Multi-AP Specification
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
This document describes how hostapd and wpa_supplicant can be configured to
|
||||||
|
support the Multi-AP Specification.
|
||||||
|
|
||||||
|
Introduction to Multi-AP
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The Wi-Fi Alliance Multi-AP Specification is the technical specification for
|
||||||
|
Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi Alliance® certification program for
|
||||||
|
Multi-AP. It defines control protocols between Wi-Fi® access points (APs) to
|
||||||
|
join them into a network with centralized control and operation. It is targeted
|
||||||
|
only at routers (repeaters, gateways, ...), not at clients. Clients are not
|
||||||
|
involved at all in the protocols.
|
||||||
|
|
||||||
|
Most of the Multi-AP specification falls outside of the scope of
|
||||||
|
hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items
|
||||||
|
summarized below. The rest of the protocol must be implemented by a separate
|
||||||
|
daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd,
|
||||||
|
e.g., to get a list of associated clients, but this can be done using the normal
|
||||||
|
hostapd interfaces.
|
||||||
|
|
||||||
|
hostapd/wpa_supplicant needs to be configured specifically to support:
|
||||||
|
- the WPS onboarding process;
|
||||||
|
- configuring backhaul links.
|
||||||
|
|
||||||
|
The text below refers to "Multi-AP Specification v1.0" [3].
|
||||||
|
|
||||||
|
|
||||||
|
Fronthaul and backhaul links
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
In a Multi-AP network, the central controller can configure the BSSs on the
|
||||||
|
devices that are joined into the network. These are called fronthaul BSSs.
|
||||||
|
From the point of view of hostapd, there is nothing special about these
|
||||||
|
fronthaul BSSs.
|
||||||
|
|
||||||
|
In addition to fronthaul BSSs, the controller can also configure backhaul
|
||||||
|
links. A backhaul link is a link between two access point devices, giving
|
||||||
|
internet access to access point devices that don't have a wired link. The
|
||||||
|
Multi-AP specification doesn't dictate this, but typically the backhaul link
|
||||||
|
will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the
|
||||||
|
wired Ethernet ports.
|
||||||
|
|
||||||
|
A backhaul link must be treated specially by hostapd and wpa_supplicant. One
|
||||||
|
side of the backhaul link is configured through the Multi-AP protocol as the
|
||||||
|
"backhaul STA", i.e., the client side of the link. A backhaul STA is like any
|
||||||
|
station and is handled appropriately by wpa_supplicant, but two additional
|
||||||
|
features are required. It must send an additional information element in each
|
||||||
|
(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it
|
||||||
|
must use 4-address mode for all frames sent over this link ([3], section 14).
|
||||||
|
Therefore, wpa_supplicant must be configured explicitly as the backhaul STA
|
||||||
|
role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block
|
||||||
|
or when configuring the network profile through the control interface. When
|
||||||
|
'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in
|
||||||
|
(Re)Association Request frame and verifies that it is included in the
|
||||||
|
(Re)Association Response frame. If it is not, association fails. If it is,
|
||||||
|
wpa_supplicant sets 4-address mode for this interface through a driver
|
||||||
|
callback.
|
||||||
|
|
||||||
|
The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must
|
||||||
|
be handled specially by hostapd, because it must add an additional information
|
||||||
|
element in each (Re)Association Response frame, but only to stations that have
|
||||||
|
identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6).
|
||||||
|
This is important because it is possible to use the same BSS and SSID for
|
||||||
|
fronthaul and backhaul at the same time. The additional information element must
|
||||||
|
only be used for frames sent to a backhaul STA, not to a normal STA. Also,
|
||||||
|
frames sent to a backhaul STA must use 4-address mode, while frames sent to a
|
||||||
|
normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use
|
||||||
|
3-address mode.
|
||||||
|
|
||||||
|
A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap'
|
||||||
|
configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3
|
||||||
|
(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd
|
||||||
|
parses the Multi-AP information element in the Association Request frame. If the
|
||||||
|
station is a backhaul STA and the BSS is configured as a backhaul BSS,
|
||||||
|
hostapd sets up 4-address mode. Since there may be multiple stations connected
|
||||||
|
simultaneously, and each of them has a different RA (receiver address), a VLAN
|
||||||
|
is created for each backhaul STA and it is automatically added to a bridge.
|
||||||
|
This is the same behavior as for WDS, and the relevant option ('bridge' or
|
||||||
|
'wds_bridge') applies here as well.
|
||||||
|
|
||||||
|
If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate
|
||||||
|
without the Multi-AP information element will be denied.
|
||||||
|
|
||||||
|
If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate
|
||||||
|
with the Multi-AP information element will be denied. That is also the only
|
||||||
|
difference with 'multi_ap' set to 0: in the latter case, the Multi-AP
|
||||||
|
information element is simply ignored.
|
||||||
|
|
||||||
|
In summary, this is the end-to-end behavior for a backhaul BSS (i.e.,
|
||||||
|
multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in
|
||||||
|
hostapd on AP). Note that point 1 means that hostapd must not be configured
|
||||||
|
with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for
|
||||||
|
that.
|
||||||
|
|
||||||
|
1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing
|
||||||
|
Multi-AP specific).
|
||||||
|
2. STA sends Authentication frame (nothing Multi-AP specific).
|
||||||
|
3. AP sends Authentication frame (nothing Multi-AP specific).
|
||||||
|
4. STA sends Association Request frame with Multi-AP IE.
|
||||||
|
5. AP sends Association Response frame with Multi-AP IE.
|
||||||
|
6. STA and AP both use 4-address mode for Data frames.
|
||||||
|
|
||||||
|
|
||||||
|
WPS support
|
||||||
|
-----------
|
||||||
|
|
||||||
|
WPS requires more special handling. WPS must only be advertised on fronthaul
|
||||||
|
BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only
|
||||||
|
BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS.
|
||||||
|
When a WPS M1 message has an additional subelement that indicates a request for
|
||||||
|
a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul
|
||||||
|
BSS credentials; instead, it should respond with the (potentially different)
|
||||||
|
backhaul BSS credentials.
|
||||||
|
|
||||||
|
To support this, hostapd has the 'multi_ap_backhaul_ssid',
|
||||||
|
'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options.
|
||||||
|
When these are set on an BSS with WPS, they are used instead of the normal
|
||||||
|
credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only
|
||||||
|
WPA2-Personal is supported in the Multi-AP specification, so there is no need
|
||||||
|
to specify authentication or encryption options. For the backhaul credentials,
|
||||||
|
per-device PSK is not supported.
|
||||||
|
|
||||||
|
If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to
|
||||||
|
specify the backhaul credentials, since the backhaul and fronthaul credentials
|
||||||
|
are identical.
|
||||||
|
|
||||||
|
To enable the Multi-AP backhaul STA feature when it performs WPS, a new
|
||||||
|
parameter has been introduced to the WPS_PBC control interface call. When this
|
||||||
|
"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the
|
||||||
|
Association Request frame and the M1 message. It then configures the new network
|
||||||
|
profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does
|
||||||
|
not follow the Multi-AP specification, wpa_supplicant will fail to associate.
|
||||||
|
|
||||||
|
In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e.,
|
||||||
|
multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2
|
||||||
|
and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or
|
||||||
|
multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS
|
||||||
|
in hostapd on Registrar AP).
|
||||||
|
|
||||||
|
1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP
|
||||||
|
specific).
|
||||||
|
2. Enrollee sends Authentication frame (nothing Multi-AP specific).
|
||||||
|
3. AP sends Authentication frame (nothing Multi-AP specific).
|
||||||
|
4. Enrollee sends Association Request frame with Multi-AP IE.
|
||||||
|
5. AP sends Association Response frame with Multi-AP IE.
|
||||||
|
6. Enrollee sends M1 with additional Multi-AP subelement.
|
||||||
|
7. AP sends M8 with backhaul instead of fronthaul credentials.
|
||||||
|
8. Enrollee sends Deauthentication frame.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
|
||||||
|
[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
|
||||||
|
[2] https://github.com/prplfoundation/prplMesh
|
||||||
|
[3] https://www.wi-fi.org/file/multi-ap-specification-v10
|
||||||
|
(requires registration)
|
352
hostapd-2.9/hostapd/README-WPS
Normal file
352
hostapd-2.9/hostapd/README-WPS
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
hostapd and Wi-Fi Protected Setup (WPS)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
This document describes how the WPS implementation in hostapd can be
|
||||||
|
configured and how an external component on an AP (e.g., web UI) is
|
||||||
|
used to enable enrollment of client devices.
|
||||||
|
|
||||||
|
|
||||||
|
Introduction to WPS
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a
|
||||||
|
wireless network. It allows automated generation of random keys (WPA
|
||||||
|
passphrase/PSK) and configuration of an access point and client
|
||||||
|
devices. WPS includes number of methods for setting up connections
|
||||||
|
with PIN method and push-button configuration (PBC) being the most
|
||||||
|
commonly deployed options.
|
||||||
|
|
||||||
|
While WPS can enable more home networks to use encryption in the
|
||||||
|
wireless network, it should be noted that the use of the PIN and
|
||||||
|
especially PBC mechanisms for authenticating the initial key setup is
|
||||||
|
not very secure. As such, use of WPS may not be suitable for
|
||||||
|
environments that require secure network access without chance for
|
||||||
|
allowing outsiders to gain access during the setup phase.
|
||||||
|
|
||||||
|
WPS uses following terms to describe the entities participating in the
|
||||||
|
network setup:
|
||||||
|
- access point: the WLAN access point
|
||||||
|
- Registrar: a device that control a network and can authorize
|
||||||
|
addition of new devices); this may be either in the AP ("internal
|
||||||
|
Registrar") or in an external device, e.g., a laptop, ("external
|
||||||
|
Registrar")
|
||||||
|
- Enrollee: a device that is being authorized to use the network
|
||||||
|
|
||||||
|
It should also be noted that the AP and a client device may change
|
||||||
|
roles (i.e., AP acts as an Enrollee and client device as a Registrar)
|
||||||
|
when WPS is used to configure the access point.
|
||||||
|
|
||||||
|
|
||||||
|
More information about WPS is available from Wi-Fi Alliance:
|
||||||
|
http://www.wi-fi.org/wifi-protected-setup
|
||||||
|
|
||||||
|
|
||||||
|
hostapd implementation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
hostapd includes an optional WPS component that can be used as an
|
||||||
|
internal WPS Registrar to manage addition of new WPS enabled clients
|
||||||
|
to the network. In addition, WPS Enrollee functionality in hostapd can
|
||||||
|
be used to allow external WPS Registrars to configure the access
|
||||||
|
point, e.g., for initial network setup. In addition, hostapd can proxy a
|
||||||
|
WPS registration between a wireless Enrollee and an external Registrar
|
||||||
|
(e.g., Microsoft Vista or Atheros JumpStart) with UPnP.
|
||||||
|
|
||||||
|
|
||||||
|
hostapd configuration
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
WPS is an optional component that needs to be enabled in hostapd build
|
||||||
|
configuration (.config). Here is an example configuration that
|
||||||
|
includes WPS support and uses nl80211 driver interface:
|
||||||
|
|
||||||
|
CONFIG_DRIVER_NL80211=y
|
||||||
|
CONFIG_WPS=y
|
||||||
|
CONFIG_WPS_UPNP=y
|
||||||
|
|
||||||
|
Following parameter can be used to enable support for NFC config method:
|
||||||
|
|
||||||
|
CONFIG_WPS_NFC=y
|
||||||
|
|
||||||
|
|
||||||
|
Following section shows an example runtime configuration
|
||||||
|
(hostapd.conf) that enables WPS:
|
||||||
|
|
||||||
|
# Configure the driver and network interface
|
||||||
|
driver=nl80211
|
||||||
|
interface=wlan0
|
||||||
|
|
||||||
|
# WPA2-Personal configuration for the AP
|
||||||
|
ssid=wps-test
|
||||||
|
wpa=2
|
||||||
|
wpa_key_mgmt=WPA-PSK
|
||||||
|
wpa_pairwise=CCMP
|
||||||
|
# Default WPA passphrase for legacy (non-WPS) clients
|
||||||
|
wpa_passphrase=12345678
|
||||||
|
# Enable random per-device PSK generation for WPS clients
|
||||||
|
# Please note that the file has to exists for hostapd to start (i.e., create an
|
||||||
|
# empty file as a starting point).
|
||||||
|
wpa_psk_file=/etc/hostapd.psk
|
||||||
|
|
||||||
|
# Enable control interface for PBC/PIN entry
|
||||||
|
ctrl_interface=/var/run/hostapd
|
||||||
|
|
||||||
|
# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup)
|
||||||
|
eap_server=1
|
||||||
|
|
||||||
|
# WPS configuration (AP configured, do not allow external WPS Registrars)
|
||||||
|
wps_state=2
|
||||||
|
ap_setup_locked=1
|
||||||
|
# If UUID is not configured, it will be generated based on local MAC address.
|
||||||
|
uuid=87654321-9abc-def0-1234-56789abc0000
|
||||||
|
wps_pin_requests=/var/run/hostapd.pin-req
|
||||||
|
device_name=Wireless AP
|
||||||
|
manufacturer=Company
|
||||||
|
model_name=WAP
|
||||||
|
model_number=123
|
||||||
|
serial_number=12345
|
||||||
|
device_type=6-0050F204-1
|
||||||
|
os_version=01020300
|
||||||
|
config_methods=label display push_button keypad
|
||||||
|
|
||||||
|
# if external Registrars are allowed, UPnP support could be added:
|
||||||
|
#upnp_iface=br0
|
||||||
|
#friendly_name=WPS Access Point
|
||||||
|
|
||||||
|
|
||||||
|
External operations
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
WPS requires either a device PIN code (usually, 8-digit number) or a
|
||||||
|
pushbutton event (for PBC) to allow a new WPS Enrollee to join the
|
||||||
|
network. hostapd uses the control interface as an input channel for
|
||||||
|
these events.
|
||||||
|
|
||||||
|
The PIN value used in the commands must be processed by an UI to
|
||||||
|
remove non-digit characters and potentially, to verify the checksum
|
||||||
|
digit. "hostapd_cli wps_check_pin <PIN>" can be used to do such
|
||||||
|
processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if
|
||||||
|
the checksum digit is incorrect, or the processed PIN (non-digit
|
||||||
|
characters removed) if the PIN is valid.
|
||||||
|
|
||||||
|
When a client device (WPS Enrollee) connects to hostapd (WPS
|
||||||
|
Registrar) in order to start PIN mode negotiation for WPS, an
|
||||||
|
identifier (Enrollee UUID) is sent. hostapd will need to be configured
|
||||||
|
with a device password (PIN) for this Enrollee. This is an operation
|
||||||
|
that requires user interaction (assuming there are no pre-configured
|
||||||
|
PINs on the AP for a set of Enrollee).
|
||||||
|
|
||||||
|
The PIN request with information about the device is appended to the
|
||||||
|
wps_pin_requests file (/var/run/hostapd.pin-req in this example). In
|
||||||
|
addition, hostapd control interface event is sent as a notification of
|
||||||
|
a new device. The AP could use, e.g., a web UI for showing active
|
||||||
|
Enrollees to the user and request a PIN for an Enrollee.
|
||||||
|
|
||||||
|
The PIN request file has one line for every Enrollee that connected to
|
||||||
|
the AP, but for which there was no PIN. Following information is
|
||||||
|
provided for each Enrollee (separated with tabulators):
|
||||||
|
- timestamp (seconds from 1970-01-01)
|
||||||
|
- Enrollee UUID
|
||||||
|
- MAC address
|
||||||
|
- Device name
|
||||||
|
- Manufacturer
|
||||||
|
- Model Name
|
||||||
|
- Model Number
|
||||||
|
- Serial Number
|
||||||
|
- Device category
|
||||||
|
|
||||||
|
Example line in the /var/run/hostapd.pin-req file:
|
||||||
|
1200188391 53b63a98-d29e-4457-a2ed-094d7e6a669c Intel(R) Centrino(R) Intel Corporation Intel(R) Centrino(R) - - 1-0050F204-1
|
||||||
|
|
||||||
|
Control interface data:
|
||||||
|
WPS-PIN-NEEDED [UUID-E|MAC Address|Device Name|Manufacturer|Model Name|Model Number|Serial Number|Device Category]
|
||||||
|
For example:
|
||||||
|
<2>WPS-PIN-NEEDED [53b63a98-d29e-4457-a2ed-094d7e6a669c|02:12:34:56:78:9a|Device|Manuf|Model|Model Number|Serial Number|1-0050F204-1]
|
||||||
|
|
||||||
|
When the user enters a PIN for a pending Enrollee, e.g., on the web
|
||||||
|
UI), hostapd needs to be notified of the new PIN over the control
|
||||||
|
interface. This can be done either by using the UNIX domain socket
|
||||||
|
-based control interface directly (src/common/wpa_ctrl.c provides
|
||||||
|
helper functions for using the interface) or by calling hostapd_cli.
|
||||||
|
|
||||||
|
Example command to add a PIN (12345670) for an Enrollee:
|
||||||
|
|
||||||
|
hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670
|
||||||
|
|
||||||
|
If the UUID-E is not available (e.g., Enrollee waits for the Registrar
|
||||||
|
to be selected before connecting), wildcard UUID may be used to allow
|
||||||
|
the PIN to be used once with any UUID:
|
||||||
|
|
||||||
|
hostapd_cli wps_pin any 12345670
|
||||||
|
|
||||||
|
To reduce likelihood of PIN being used with other devices or of
|
||||||
|
forgetting an active PIN available for potential attackers, expiration
|
||||||
|
time in seconds can be set for the new PIN (value 0 indicates no
|
||||||
|
expiration):
|
||||||
|
|
||||||
|
hostapd_cli wps_pin any 12345670 300
|
||||||
|
|
||||||
|
If the MAC address of the enrollee is known, it should be configured
|
||||||
|
to allow the AP to advertise list of authorized enrollees:
|
||||||
|
|
||||||
|
hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \
|
||||||
|
12345670 300 00:11:22:33:44:55
|
||||||
|
|
||||||
|
|
||||||
|
After this, the Enrollee can connect to the AP again and complete WPS
|
||||||
|
negotiation. At that point, a new, random WPA PSK is generated for the
|
||||||
|
client device and the client can then use that key to connect to the
|
||||||
|
AP to access the network.
|
||||||
|
|
||||||
|
|
||||||
|
If the AP includes a pushbutton, WPS PBC mode can be used. It is
|
||||||
|
enabled by pushing a button on both the AP and the client at about the
|
||||||
|
same time (2 minute window). hostapd needs to be notified about the AP
|
||||||
|
button pushed event over the control interface, e.g., by calling
|
||||||
|
hostapd_cli:
|
||||||
|
|
||||||
|
hostapd_cli wps_pbc
|
||||||
|
|
||||||
|
At this point, the client has two minutes to complete WPS negotiation
|
||||||
|
which will generate a new WPA PSK in the same way as the PIN method
|
||||||
|
described above.
|
||||||
|
|
||||||
|
|
||||||
|
When an external Registrar is used, the AP can act as an Enrollee and
|
||||||
|
use its AP PIN. A static AP PIN (e.g., one one a label in the AP
|
||||||
|
device) can be configured in hostapd.conf (ap_pin parameter). A more
|
||||||
|
secure option is to use hostapd_cli wps_ap_pin command to enable the
|
||||||
|
AP PIN only based on user action (and even better security by using a
|
||||||
|
random AP PIN for each session, i.e., by using "wps_ap_pin random"
|
||||||
|
command with a timeout value). Following commands are available for
|
||||||
|
managing the dynamic AP PIN operations:
|
||||||
|
|
||||||
|
hostapd_cli wps_ap_pin disable
|
||||||
|
- disable AP PIN (i.e., do not allow external Registrars to use it to
|
||||||
|
learn the current AP settings or to reconfigure the AP)
|
||||||
|
|
||||||
|
hostapd_cli wps_ap_pin random [timeout]
|
||||||
|
- generate a random AP PIN and enable it
|
||||||
|
- if the optional timeout parameter is given, the AP PIN will be enabled
|
||||||
|
for the specified number of seconds
|
||||||
|
|
||||||
|
hostapd_cli wps_ap_pin get
|
||||||
|
- fetch the current AP PIN
|
||||||
|
|
||||||
|
hostapd_cli wps_ap_pin set <PIN> [timeout]
|
||||||
|
- set the AP PIN and enable it
|
||||||
|
- if the optional timeout parameter is given, the AP PIN will be enabled
|
||||||
|
for the specified number of seconds
|
||||||
|
|
||||||
|
hostapd_cli get_config
|
||||||
|
- display the current configuration
|
||||||
|
|
||||||
|
hostapd_cli wps_config <new SSID> <auth> <encr> <new key>
|
||||||
|
examples:
|
||||||
|
hostapd_cli wps_config testing WPA2PSK CCMP 12345678
|
||||||
|
hostapd_cli wps_config "no security" OPEN NONE ""
|
||||||
|
|
||||||
|
<auth> must be one of the following: OPEN WPAPSK WPA2PSK
|
||||||
|
<encr> must be one of the following: NONE WEP TKIP CCMP
|
||||||
|
|
||||||
|
|
||||||
|
Credential generation and configuration changes
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
By default, hostapd generates credentials for Enrollees and processing
|
||||||
|
AP configuration updates internally. However, it is possible to
|
||||||
|
control these operations from external programs, if desired.
|
||||||
|
|
||||||
|
The internal credential generation can be disabled with
|
||||||
|
skip_cred_build=1 option in the configuration. extra_cred option will
|
||||||
|
then need to be used to provide pre-configured Credential attribute(s)
|
||||||
|
for hostapd to use. The exact data from this binary file will be sent,
|
||||||
|
i.e., it will have to include valid WPS attributes. extra_cred can
|
||||||
|
also be used to add additional networks if the Registrar is used to
|
||||||
|
configure credentials for multiple networks.
|
||||||
|
|
||||||
|
Processing of received configuration updates can be disabled with
|
||||||
|
wps_cred_processing=1 option. When this is used, an external program
|
||||||
|
is responsible for creating hostapd configuration files and processing
|
||||||
|
configuration updates based on messages received from hostapd over
|
||||||
|
control interface. This will also include the initial configuration on
|
||||||
|
first successful registration if the AP is initially set in
|
||||||
|
unconfigured state.
|
||||||
|
|
||||||
|
Following control interface messages are sent out for external programs:
|
||||||
|
|
||||||
|
WPS-REG-SUCCESS <Enrollee MAC address <UUID-E>
|
||||||
|
For example:
|
||||||
|
<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333
|
||||||
|
|
||||||
|
This can be used to trigger change from unconfigured to configured
|
||||||
|
state (random configuration based on the first successful WPS
|
||||||
|
registration). In addition, this can be used to update AP UI about the
|
||||||
|
status of WPS registration progress.
|
||||||
|
|
||||||
|
|
||||||
|
WPS-NEW-AP-SETTINGS <hexdump of AP Setup attributes>
|
||||||
|
For example:
|
||||||
|
<2>WPS-NEW-AP-SETTINGS 10260001011045000c6a6b6d2d7770732d74657374100300020020100f00020008102700403065346230343536633236366665306433396164313535346131663462663731323433376163666462376633393965353466316631623032306164343438623510200006024231cede15101e000844
|
||||||
|
|
||||||
|
This can be used to update the externally stored AP configuration and
|
||||||
|
then update hostapd configuration (followed by restarting of hostapd).
|
||||||
|
|
||||||
|
|
||||||
|
WPS with NFC
|
||||||
|
------------
|
||||||
|
|
||||||
|
WPS can be used with NFC-based configuration method. An NFC tag
|
||||||
|
containing a password token from the Enrollee can be used to
|
||||||
|
authenticate the connection instead of the PIN. In addition, an NFC tag
|
||||||
|
with a configuration token can be used to transfer AP settings without
|
||||||
|
going through the WPS protocol.
|
||||||
|
|
||||||
|
When the AP acts as an Enrollee, a local NFC tag with a password token
|
||||||
|
can be used by touching the NFC interface of an external Registrar. The
|
||||||
|
wps_nfc_token command is used to manage use of the NFC password token
|
||||||
|
from the AP. "wps_nfc_token enable" enables the use of the AP's NFC
|
||||||
|
password token (in place of AP PIN) and "wps_nfc_token disable" disables
|
||||||
|
the NFC password token.
|
||||||
|
|
||||||
|
The NFC password token that is either pre-configured in the
|
||||||
|
configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
|
||||||
|
wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
|
||||||
|
"wps_nfc_token <WPS|NDEF>" command. The nfc_pw_token tool from
|
||||||
|
wpa_supplicant can be used to generate NFC password tokens during
|
||||||
|
manufacturing (each AP needs to have its own random keys).
|
||||||
|
|
||||||
|
The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
|
||||||
|
NFC configuration token. The output value from this command is a hexdump
|
||||||
|
of the current AP configuration (WPS parameter requests this to include
|
||||||
|
only the WPS attributes; NDEF parameter requests additional NDEF
|
||||||
|
encapsulation to be included). This data needs to be written to an NFC
|
||||||
|
tag with an external program. Once written, the NFC configuration token
|
||||||
|
can be used to touch an NFC interface on a station to provision the
|
||||||
|
credentials needed to access the network.
|
||||||
|
|
||||||
|
When the NFC device on the AP reads an NFC tag with a MIME media type
|
||||||
|
"application/vnd.wfa.wsc", the NDEF message payload (with or without
|
||||||
|
NDEF encapsulation) can be delivered to hostapd using the
|
||||||
|
following hostapd_cli command:
|
||||||
|
|
||||||
|
wps_nfc_tag_read <hexdump of payload>
|
||||||
|
|
||||||
|
If the NFC tag contains a password token, the token is added to the
|
||||||
|
internal Registrar. This allows station Enrollee from which the password
|
||||||
|
token was received to run through WPS protocol to provision the
|
||||||
|
credential.
|
||||||
|
|
||||||
|
"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
|
||||||
|
contents of a Handover Select Message for connection handover when this
|
||||||
|
does not depend on the contents of the Handover Request Message. The
|
||||||
|
first argument selects the format of the output data and the second
|
||||||
|
argument selects which type of connection handover is requested (WPS =
|
||||||
|
Wi-Fi handover as specified in WSC 2.0).
|
||||||
|
|
||||||
|
"nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
|
||||||
|
<carrier from handover select>" is used to report completed NFC
|
||||||
|
connection handover. The first parameter indicates whether the local
|
||||||
|
device initiated or responded to the connection handover and the carrier
|
||||||
|
records are the selected carrier from the handover request and select
|
||||||
|
messages as a hexdump.
|
215
hostapd-2.9/hostapd/android.config
Normal file
215
hostapd-2.9/hostapd/android.config
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
# Example hostapd build time configuration
|
||||||
|
#
|
||||||
|
# This file lists the configuration options that are used when building the
|
||||||
|
# hostapd binary. All lines starting with # are ignored. Configuration option
|
||||||
|
# lines must be commented out complete, if they are not to be included, i.e.,
|
||||||
|
# just setting VARIABLE=n is not disabling that variable.
|
||||||
|
#
|
||||||
|
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
|
||||||
|
# be modified from here. In most cass, these lines should use += in order not
|
||||||
|
# to override previous values of the variables.
|
||||||
|
|
||||||
|
# Driver interface for Host AP driver
|
||||||
|
#CONFIG_DRIVER_HOSTAP=y
|
||||||
|
|
||||||
|
# Driver interface for wired authenticator
|
||||||
|
#CONFIG_DRIVER_WIRED=y
|
||||||
|
|
||||||
|
# Driver interface for drivers using the nl80211 kernel interface
|
||||||
|
#CONFIG_DRIVER_NL80211=y
|
||||||
|
# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
|
||||||
|
# shipped with your distribution yet. If that is the case, you need to build
|
||||||
|
# newer libnl version and point the hostapd build to use it.
|
||||||
|
#LIBNL=/usr/src/libnl
|
||||||
|
#CFLAGS += -I$(LIBNL)/include
|
||||||
|
#LIBS += -L$(LIBNL)/lib
|
||||||
|
CONFIG_LIBNL20=y
|
||||||
|
|
||||||
|
# QCA vendor extensions to nl80211
|
||||||
|
CONFIG_DRIVER_NL80211_QCA=y
|
||||||
|
|
||||||
|
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
|
||||||
|
#CONFIG_DRIVER_BSD=y
|
||||||
|
#CFLAGS += -I/usr/local/include
|
||||||
|
#LIBS += -L/usr/local/lib
|
||||||
|
#LIBS_p += -L/usr/local/lib
|
||||||
|
#LIBS_c += -L/usr/local/lib
|
||||||
|
|
||||||
|
# Driver interface for no driver (e.g., RADIUS server only)
|
||||||
|
#CONFIG_DRIVER_NONE=y
|
||||||
|
|
||||||
|
# IEEE 802.11F/IAPP
|
||||||
|
#CONFIG_IAPP=y
|
||||||
|
|
||||||
|
# WPA2/IEEE 802.11i RSN pre-authentication
|
||||||
|
#CONFIG_RSN_PREAUTH=y
|
||||||
|
|
||||||
|
# IEEE 802.11w (management frame protection)
|
||||||
|
# This version is an experimental implementation based on IEEE 802.11w/D1.0
|
||||||
|
# draft and is subject to change since the standard has not yet been finalized.
|
||||||
|
# Driver support is also needed for IEEE 802.11w.
|
||||||
|
CONFIG_IEEE80211W=y
|
||||||
|
|
||||||
|
# Support Operating Channel Validation
|
||||||
|
#CONFIG_OCV=y
|
||||||
|
|
||||||
|
# Integrated EAP server
|
||||||
|
#CONFIG_EAP=y
|
||||||
|
|
||||||
|
# EAP-MD5 for the integrated EAP server
|
||||||
|
#CONFIG_EAP_MD5=y
|
||||||
|
|
||||||
|
# EAP-TLS for the integrated EAP server
|
||||||
|
#CONFIG_EAP_TLS=y
|
||||||
|
|
||||||
|
# EAP-MSCHAPv2 for the integrated EAP server
|
||||||
|
#CONFIG_EAP_MSCHAPV2=y
|
||||||
|
|
||||||
|
# EAP-PEAP for the integrated EAP server
|
||||||
|
#CONFIG_EAP_PEAP=y
|
||||||
|
|
||||||
|
# EAP-GTC for the integrated EAP server
|
||||||
|
#CONFIG_EAP_GTC=y
|
||||||
|
|
||||||
|
# EAP-TTLS for the integrated EAP server
|
||||||
|
#CONFIG_EAP_TTLS=y
|
||||||
|
|
||||||
|
# EAP-SIM for the integrated EAP server
|
||||||
|
#CONFIG_EAP_SIM=y
|
||||||
|
|
||||||
|
# EAP-AKA for the integrated EAP server
|
||||||
|
#CONFIG_EAP_AKA=y
|
||||||
|
|
||||||
|
# EAP-AKA' for the integrated EAP server
|
||||||
|
# This requires CONFIG_EAP_AKA to be enabled, too.
|
||||||
|
#CONFIG_EAP_AKA_PRIME=y
|
||||||
|
|
||||||
|
# EAP-PAX for the integrated EAP server
|
||||||
|
#CONFIG_EAP_PAX=y
|
||||||
|
|
||||||
|
# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
|
||||||
|
#CONFIG_EAP_PSK=y
|
||||||
|
|
||||||
|
# EAP-SAKE for the integrated EAP server
|
||||||
|
#CONFIG_EAP_SAKE=y
|
||||||
|
|
||||||
|
# EAP-GPSK for the integrated EAP server
|
||||||
|
#CONFIG_EAP_GPSK=y
|
||||||
|
# Include support for optional SHA256 cipher suite in EAP-GPSK
|
||||||
|
#CONFIG_EAP_GPSK_SHA256=y
|
||||||
|
|
||||||
|
# EAP-FAST for the integrated EAP server
|
||||||
|
# Note: Default OpenSSL package does not include support for all the
|
||||||
|
# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
|
||||||
|
# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch)
|
||||||
|
# to add the needed functions.
|
||||||
|
#CONFIG_EAP_FAST=y
|
||||||
|
|
||||||
|
# Wi-Fi Protected Setup (WPS)
|
||||||
|
CONFIG_WPS=y
|
||||||
|
# Enable UPnP support for external WPS Registrars
|
||||||
|
#CONFIG_WPS_UPNP=y
|
||||||
|
|
||||||
|
# EAP-IKEv2
|
||||||
|
#CONFIG_EAP_IKEV2=y
|
||||||
|
|
||||||
|
# Trusted Network Connect (EAP-TNC)
|
||||||
|
#CONFIG_EAP_TNC=y
|
||||||
|
|
||||||
|
# PKCS#12 (PFX) support (used to read private key and certificate file from
|
||||||
|
# a file that usually has extension .p12 or .pfx)
|
||||||
|
CONFIG_PKCS12=y
|
||||||
|
|
||||||
|
# RADIUS authentication server. This provides access to the integrated EAP
|
||||||
|
# server from external hosts using RADIUS.
|
||||||
|
#CONFIG_RADIUS_SERVER=y
|
||||||
|
|
||||||
|
# Build IPv6 support for RADIUS operations
|
||||||
|
CONFIG_IPV6=y
|
||||||
|
|
||||||
|
# IEEE Std 802.11r-2008 (Fast BSS Transition)
|
||||||
|
#CONFIG_IEEE80211R=y
|
||||||
|
|
||||||
|
# Use the hostapd's IEEE 802.11 authentication (ACL), but without
|
||||||
|
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
|
||||||
|
#CONFIG_DRIVER_RADIUS_ACL=y
|
||||||
|
|
||||||
|
# IEEE 802.11n (High Throughput) support
|
||||||
|
CONFIG_IEEE80211N=y
|
||||||
|
|
||||||
|
# Remove debugging code that is printing out debug messages to stdout.
|
||||||
|
# This can be used to reduce the size of the hostapd considerably if debugging
|
||||||
|
# code is not needed.
|
||||||
|
#CONFIG_NO_STDOUT_DEBUG=y
|
||||||
|
|
||||||
|
# Add support for writing debug log to Android logcat instead of standard output
|
||||||
|
CONFIG_ANDROID_LOG=y
|
||||||
|
|
||||||
|
# Remove support for RADIUS accounting
|
||||||
|
#CONFIG_NO_ACCOUNTING=y
|
||||||
|
|
||||||
|
# Remove support for RADIUS
|
||||||
|
CONFIG_NO_RADIUS=y
|
||||||
|
|
||||||
|
# Remove support for VLANs
|
||||||
|
#CONFIG_NO_VLAN=y
|
||||||
|
|
||||||
|
# Remove support for dumping internal state through control interface commands
|
||||||
|
# This can be used to reduce binary size at the cost of disabling a debugging
|
||||||
|
# option.
|
||||||
|
#CONFIG_NO_DUMP_STATE=y
|
||||||
|
|
||||||
|
# Select wrapper for operatins system and C library specific functions
|
||||||
|
# unix = UNIX/POSIX like systems (default)
|
||||||
|
# win32 = Windows systems
|
||||||
|
# none = Empty template
|
||||||
|
CONFIG_OS=unix
|
||||||
|
|
||||||
|
# Enable tracing code for developer debugging
|
||||||
|
# This tracks use of memory allocations and other registrations and reports
|
||||||
|
# incorrect use with a backtrace of call (or allocation) location.
|
||||||
|
#CONFIG_WPA_TRACE=y
|
||||||
|
# For BSD, comment out these.
|
||||||
|
#LIBS += -lexecinfo
|
||||||
|
#LIBS_p += -lexecinfo
|
||||||
|
#LIBS_c += -lexecinfo
|
||||||
|
|
||||||
|
# Use libbfd to get more details for developer debugging
|
||||||
|
# This enables use of libbfd to get more detailed symbols for the backtraces
|
||||||
|
# generated by CONFIG_WPA_TRACE=y.
|
||||||
|
#CONFIG_WPA_TRACE_BFD=y
|
||||||
|
# For BSD, comment out these.
|
||||||
|
#LIBS += -lbfd -liberty -lz
|
||||||
|
#LIBS_p += -lbfd -liberty -lz
|
||||||
|
#LIBS_c += -lbfd -liberty -lz
|
||||||
|
|
||||||
|
# Should we use poll instead of select? Select is used by default.
|
||||||
|
#CONFIG_ELOOP_POLL=y
|
||||||
|
|
||||||
|
# Should we use epoll instead of select? Select is used by default.
|
||||||
|
#CONFIG_ELOOP_EPOLL=y
|
||||||
|
|
||||||
|
# Enable AP
|
||||||
|
CONFIG_AP=y
|
||||||
|
|
||||||
|
# Enable Fast Session Transfer (FST)
|
||||||
|
#CONFIG_FST=y
|
||||||
|
|
||||||
|
# Multiband Operation support
|
||||||
|
# These extentions facilitate efficient use of multiple frequency bands
|
||||||
|
# available to the AP and the devices that may associate with it.
|
||||||
|
#CONFIG_MBO=y
|
||||||
|
|
||||||
|
# Include internal line edit mode in hostapd_cli.
|
||||||
|
CONFIG_WPA_CLI_EDIT=y
|
||||||
|
|
||||||
|
# Opportunistic Wireless Encryption (OWE)
|
||||||
|
# Experimental implementation of draft-harkins-owe-07.txt
|
||||||
|
#CONFIG_OWE=y
|
||||||
|
|
||||||
|
# Wpa_supplicant's random pool is not necessary on Android. Randomness is
|
||||||
|
# already provided by the entropymixer service which ensures sufficient
|
||||||
|
# entropy is maintained across reboots. Commit b410eb1913 'Initialize
|
||||||
|
# /dev/urandom earlier in boot' seeds /dev/urandom with that entropy before
|
||||||
|
# either wpa_supplicant or hostapd are run.
|
||||||
|
CONFIG_NO_RANDOM_POOL=y
|
4633
hostapd-2.9/hostapd/config_file.c
Normal file
4633
hostapd-2.9/hostapd/config_file.c
Normal file
File diff suppressed because it is too large
Load diff
22
hostapd-2.9/hostapd/config_file.h
Normal file
22
hostapd-2.9/hostapd/config_file.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* hostapd / Configuration file parser
|
||||||
|
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_FILE_H
|
||||||
|
#define CONFIG_FILE_H
|
||||||
|
|
||||||
|
struct hostapd_config * hostapd_config_read(const char *fname);
|
||||||
|
int hostapd_set_iface(struct hostapd_config *conf,
|
||||||
|
struct hostapd_bss_config *bss, const char *field,
|
||||||
|
char *value);
|
||||||
|
int hostapd_acl_comp(const void *a, const void *b);
|
||||||
|
int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
|
||||||
|
int vlan_id, const u8 *addr);
|
||||||
|
void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
|
||||||
|
const u8 *addr);
|
||||||
|
|
||||||
|
#endif /* CONFIG_FILE_H */
|
4605
hostapd-2.9/hostapd/ctrl_iface.c
Normal file
4605
hostapd-2.9/hostapd/ctrl_iface.c
Normal file
File diff suppressed because it is too large
Load diff
39
hostapd-2.9/hostapd/ctrl_iface.h
Normal file
39
hostapd-2.9/hostapd/ctrl_iface.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* hostapd / UNIX domain socket -based control interface
|
||||||
|
* Copyright (c) 2004, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CTRL_IFACE_H
|
||||||
|
#define CTRL_IFACE_H
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_CTRL_IFACE
|
||||||
|
int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
|
||||||
|
void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
|
||||||
|
int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
|
||||||
|
void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
|
||||||
|
#else /* CONFIG_NO_CTRL_IFACE */
|
||||||
|
static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_CTRL_IFACE */
|
||||||
|
|
||||||
|
#endif /* CTRL_IFACE_H */
|
391
hostapd-2.9/hostapd/defconfig
Normal file
391
hostapd-2.9/hostapd/defconfig
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
# Example hostapd build time configuration
|
||||||
|
#
|
||||||
|
# This file lists the configuration options that are used when building the
|
||||||
|
# hostapd binary. All lines starting with # are ignored. Configuration option
|
||||||
|
# lines must be commented out complete, if they are not to be included, i.e.,
|
||||||
|
# just setting VARIABLE=n is not disabling that variable.
|
||||||
|
#
|
||||||
|
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
|
||||||
|
# be modified from here. In most cass, these lines should use += in order not
|
||||||
|
# to override previous values of the variables.
|
||||||
|
|
||||||
|
# Driver interface for Host AP driver
|
||||||
|
CONFIG_DRIVER_HOSTAP=y
|
||||||
|
|
||||||
|
# Driver interface for wired authenticator
|
||||||
|
#CONFIG_DRIVER_WIRED=y
|
||||||
|
|
||||||
|
# Driver interface for drivers using the nl80211 kernel interface
|
||||||
|
CONFIG_DRIVER_NL80211=y
|
||||||
|
|
||||||
|
# QCA vendor extensions to nl80211
|
||||||
|
#CONFIG_DRIVER_NL80211_QCA=y
|
||||||
|
|
||||||
|
# driver_nl80211.c requires libnl. If you are compiling it yourself
|
||||||
|
# you may need to point hostapd to your version of libnl.
|
||||||
|
#
|
||||||
|
#CFLAGS += -I$<path to libnl include files>
|
||||||
|
#LIBS += -L$<path to libnl library files>
|
||||||
|
|
||||||
|
# Use libnl v2.0 (or 3.0) libraries.
|
||||||
|
#CONFIG_LIBNL20=y
|
||||||
|
|
||||||
|
# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
|
||||||
|
CONFIG_LIBNL32=y
|
||||||
|
|
||||||
|
|
||||||
|
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
|
||||||
|
#CONFIG_DRIVER_BSD=y
|
||||||
|
#CFLAGS += -I/usr/local/include
|
||||||
|
#LIBS += -L/usr/local/lib
|
||||||
|
#LIBS_p += -L/usr/local/lib
|
||||||
|
#LIBS_c += -L/usr/local/lib
|
||||||
|
|
||||||
|
# Driver interface for no driver (e.g., RADIUS server only)
|
||||||
|
#CONFIG_DRIVER_NONE=y
|
||||||
|
|
||||||
|
# IEEE 802.11F/IAPP
|
||||||
|
CONFIG_IAPP=y
|
||||||
|
|
||||||
|
# WPA2/IEEE 802.11i RSN pre-authentication
|
||||||
|
CONFIG_RSN_PREAUTH=y
|
||||||
|
|
||||||
|
# IEEE 802.11w (management frame protection)
|
||||||
|
CONFIG_IEEE80211W=y
|
||||||
|
|
||||||
|
# Support Operating Channel Validation
|
||||||
|
#CONFIG_OCV=y
|
||||||
|
|
||||||
|
# Integrated EAP server
|
||||||
|
CONFIG_EAP=y
|
||||||
|
|
||||||
|
# EAP Re-authentication Protocol (ERP) in integrated EAP server
|
||||||
|
CONFIG_ERP=y
|
||||||
|
|
||||||
|
# EAP-MD5 for the integrated EAP server
|
||||||
|
CONFIG_EAP_MD5=y
|
||||||
|
|
||||||
|
# EAP-TLS for the integrated EAP server
|
||||||
|
CONFIG_EAP_TLS=y
|
||||||
|
|
||||||
|
# EAP-MSCHAPv2 for the integrated EAP server
|
||||||
|
CONFIG_EAP_MSCHAPV2=y
|
||||||
|
|
||||||
|
# EAP-PEAP for the integrated EAP server
|
||||||
|
CONFIG_EAP_PEAP=y
|
||||||
|
|
||||||
|
# EAP-GTC for the integrated EAP server
|
||||||
|
CONFIG_EAP_GTC=y
|
||||||
|
|
||||||
|
# EAP-TTLS for the integrated EAP server
|
||||||
|
CONFIG_EAP_TTLS=y
|
||||||
|
|
||||||
|
# EAP-SIM for the integrated EAP server
|
||||||
|
#CONFIG_EAP_SIM=y
|
||||||
|
|
||||||
|
# EAP-AKA for the integrated EAP server
|
||||||
|
#CONFIG_EAP_AKA=y
|
||||||
|
|
||||||
|
# EAP-AKA' for the integrated EAP server
|
||||||
|
# This requires CONFIG_EAP_AKA to be enabled, too.
|
||||||
|
#CONFIG_EAP_AKA_PRIME=y
|
||||||
|
|
||||||
|
# EAP-PAX for the integrated EAP server
|
||||||
|
#CONFIG_EAP_PAX=y
|
||||||
|
|
||||||
|
# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
|
||||||
|
#CONFIG_EAP_PSK=y
|
||||||
|
|
||||||
|
# EAP-pwd for the integrated EAP server (secure authentication with a password)
|
||||||
|
#CONFIG_EAP_PWD=y
|
||||||
|
|
||||||
|
# EAP-SAKE for the integrated EAP server
|
||||||
|
#CONFIG_EAP_SAKE=y
|
||||||
|
|
||||||
|
# EAP-GPSK for the integrated EAP server
|
||||||
|
#CONFIG_EAP_GPSK=y
|
||||||
|
# Include support for optional SHA256 cipher suite in EAP-GPSK
|
||||||
|
#CONFIG_EAP_GPSK_SHA256=y
|
||||||
|
|
||||||
|
# EAP-FAST for the integrated EAP server
|
||||||
|
#CONFIG_EAP_FAST=y
|
||||||
|
|
||||||
|
# EAP-TEAP for the integrated EAP server
|
||||||
|
# Note: The current EAP-TEAP implementation is experimental and should not be
|
||||||
|
# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
|
||||||
|
# of conflicting statements and missing details and the implementation has
|
||||||
|
# vendor specific workarounds for those and as such, may not interoperate with
|
||||||
|
# any other implementation. This should not be used for anything else than
|
||||||
|
# experimentation and interoperability testing until those issues has been
|
||||||
|
# resolved.
|
||||||
|
#CONFIG_EAP_TEAP=y
|
||||||
|
|
||||||
|
# Wi-Fi Protected Setup (WPS)
|
||||||
|
#CONFIG_WPS=y
|
||||||
|
# Enable UPnP support for external WPS Registrars
|
||||||
|
#CONFIG_WPS_UPNP=y
|
||||||
|
# Enable WPS support with NFC config method
|
||||||
|
#CONFIG_WPS_NFC=y
|
||||||
|
|
||||||
|
# EAP-IKEv2
|
||||||
|
#CONFIG_EAP_IKEV2=y
|
||||||
|
|
||||||
|
# Trusted Network Connect (EAP-TNC)
|
||||||
|
#CONFIG_EAP_TNC=y
|
||||||
|
|
||||||
|
# EAP-EKE for the integrated EAP server
|
||||||
|
#CONFIG_EAP_EKE=y
|
||||||
|
|
||||||
|
# PKCS#12 (PFX) support (used to read private key and certificate file from
|
||||||
|
# a file that usually has extension .p12 or .pfx)
|
||||||
|
CONFIG_PKCS12=y
|
||||||
|
|
||||||
|
# RADIUS authentication server. This provides access to the integrated EAP
|
||||||
|
# server from external hosts using RADIUS.
|
||||||
|
#CONFIG_RADIUS_SERVER=y
|
||||||
|
|
||||||
|
# Build IPv6 support for RADIUS operations
|
||||||
|
CONFIG_IPV6=y
|
||||||
|
|
||||||
|
# IEEE Std 802.11r-2008 (Fast BSS Transition)
|
||||||
|
#CONFIG_IEEE80211R=y
|
||||||
|
|
||||||
|
# Use the hostapd's IEEE 802.11 authentication (ACL), but without
|
||||||
|
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
|
||||||
|
#CONFIG_DRIVER_RADIUS_ACL=y
|
||||||
|
|
||||||
|
# IEEE 802.11n (High Throughput) support
|
||||||
|
#CONFIG_IEEE80211N=y
|
||||||
|
|
||||||
|
# Wireless Network Management (IEEE Std 802.11v-2011)
|
||||||
|
# Note: This is experimental and not complete implementation.
|
||||||
|
#CONFIG_WNM=y
|
||||||
|
|
||||||
|
# IEEE 802.11ac (Very High Throughput) support
|
||||||
|
#CONFIG_IEEE80211AC=y
|
||||||
|
|
||||||
|
# IEEE 802.11ax HE support
|
||||||
|
# Note: This is experimental and work in progress. The definitions are still
|
||||||
|
# subject to change and this should not be expected to interoperate with the
|
||||||
|
# final IEEE 802.11ax version.
|
||||||
|
#CONFIG_IEEE80211AX=y
|
||||||
|
|
||||||
|
# Remove debugging code that is printing out debug messages to stdout.
|
||||||
|
# This can be used to reduce the size of the hostapd considerably if debugging
|
||||||
|
# code is not needed.
|
||||||
|
#CONFIG_NO_STDOUT_DEBUG=y
|
||||||
|
|
||||||
|
# Add support for writing debug log to a file: -f /tmp/hostapd.log
|
||||||
|
# Disabled by default.
|
||||||
|
#CONFIG_DEBUG_FILE=y
|
||||||
|
|
||||||
|
# Send debug messages to syslog instead of stdout
|
||||||
|
#CONFIG_DEBUG_SYSLOG=y
|
||||||
|
|
||||||
|
# Add support for sending all debug messages (regardless of debug verbosity)
|
||||||
|
# to the Linux kernel tracing facility. This helps debug the entire stack by
|
||||||
|
# making it easy to record everything happening from the driver up into the
|
||||||
|
# same file, e.g., using trace-cmd.
|
||||||
|
#CONFIG_DEBUG_LINUX_TRACING=y
|
||||||
|
|
||||||
|
# Remove support for RADIUS accounting
|
||||||
|
#CONFIG_NO_ACCOUNTING=y
|
||||||
|
|
||||||
|
# Remove support for RADIUS
|
||||||
|
#CONFIG_NO_RADIUS=y
|
||||||
|
|
||||||
|
# Remove support for VLANs
|
||||||
|
#CONFIG_NO_VLAN=y
|
||||||
|
|
||||||
|
# Enable support for fully dynamic VLANs. This enables hostapd to
|
||||||
|
# automatically create bridge and VLAN interfaces if necessary.
|
||||||
|
#CONFIG_FULL_DYNAMIC_VLAN=y
|
||||||
|
|
||||||
|
# Use netlink-based kernel API for VLAN operations instead of ioctl()
|
||||||
|
# Note: This requires libnl 3.1 or newer.
|
||||||
|
#CONFIG_VLAN_NETLINK=y
|
||||||
|
|
||||||
|
# Remove support for dumping internal state through control interface commands
|
||||||
|
# This can be used to reduce binary size at the cost of disabling a debugging
|
||||||
|
# option.
|
||||||
|
#CONFIG_NO_DUMP_STATE=y
|
||||||
|
|
||||||
|
# Enable tracing code for developer debugging
|
||||||
|
# This tracks use of memory allocations and other registrations and reports
|
||||||
|
# incorrect use with a backtrace of call (or allocation) location.
|
||||||
|
#CONFIG_WPA_TRACE=y
|
||||||
|
# For BSD, comment out these.
|
||||||
|
#LIBS += -lexecinfo
|
||||||
|
#LIBS_p += -lexecinfo
|
||||||
|
#LIBS_c += -lexecinfo
|
||||||
|
|
||||||
|
# Use libbfd to get more details for developer debugging
|
||||||
|
# This enables use of libbfd to get more detailed symbols for the backtraces
|
||||||
|
# generated by CONFIG_WPA_TRACE=y.
|
||||||
|
#CONFIG_WPA_TRACE_BFD=y
|
||||||
|
# For BSD, comment out these.
|
||||||
|
#LIBS += -lbfd -liberty -lz
|
||||||
|
#LIBS_p += -lbfd -liberty -lz
|
||||||
|
#LIBS_c += -lbfd -liberty -lz
|
||||||
|
|
||||||
|
# hostapd depends on strong random number generation being available from the
|
||||||
|
# operating system. os_get_random() function is used to fetch random data when
|
||||||
|
# needed, e.g., for key generation. On Linux and BSD systems, this works by
|
||||||
|
# reading /dev/urandom. It should be noted that the OS entropy pool needs to be
|
||||||
|
# properly initialized before hostapd is started. This is important especially
|
||||||
|
# on embedded devices that do not have a hardware random number generator and
|
||||||
|
# may by default start up with minimal entropy available for random number
|
||||||
|
# generation.
|
||||||
|
#
|
||||||
|
# As a safety net, hostapd is by default trying to internally collect
|
||||||
|
# additional entropy for generating random data to mix in with the data
|
||||||
|
# fetched from the OS. This by itself is not considered to be very strong, but
|
||||||
|
# it may help in cases where the system pool is not initialized properly.
|
||||||
|
# However, it is very strongly recommended that the system pool is initialized
|
||||||
|
# with enough entropy either by using hardware assisted random number
|
||||||
|
# generator or by storing state over device reboots.
|
||||||
|
#
|
||||||
|
# hostapd can be configured to maintain its own entropy store over restarts to
|
||||||
|
# enhance random number generation. This is not perfect, but it is much more
|
||||||
|
# secure than using the same sequence of random numbers after every reboot.
|
||||||
|
# This can be enabled with -e<entropy file> command line option. The specified
|
||||||
|
# file needs to be readable and writable by hostapd.
|
||||||
|
#
|
||||||
|
# If the os_get_random() is known to provide strong random data (e.g., on
|
||||||
|
# Linux/BSD, the board in question is known to have reliable source of random
|
||||||
|
# data from /dev/urandom), the internal hostapd random pool can be disabled.
|
||||||
|
# This will save some in binary size and CPU use. However, this should only be
|
||||||
|
# considered for builds that are known to be used on devices that meet the
|
||||||
|
# requirements described above.
|
||||||
|
#CONFIG_NO_RANDOM_POOL=y
|
||||||
|
|
||||||
|
# Should we attempt to use the getrandom(2) call that provides more reliable
|
||||||
|
# yet secure randomness source than /dev/random on Linux 3.17 and newer.
|
||||||
|
# Requires glibc 2.25 to build, falls back to /dev/random if unavailable.
|
||||||
|
#CONFIG_GETRANDOM=y
|
||||||
|
|
||||||
|
# Should we use poll instead of select? Select is used by default.
|
||||||
|
#CONFIG_ELOOP_POLL=y
|
||||||
|
|
||||||
|
# Should we use epoll instead of select? Select is used by default.
|
||||||
|
#CONFIG_ELOOP_EPOLL=y
|
||||||
|
|
||||||
|
# Should we use kqueue instead of select? Select is used by default.
|
||||||
|
#CONFIG_ELOOP_KQUEUE=y
|
||||||
|
|
||||||
|
# Select TLS implementation
|
||||||
|
# openssl = OpenSSL (default)
|
||||||
|
# gnutls = GnuTLS
|
||||||
|
# internal = Internal TLSv1 implementation (experimental)
|
||||||
|
# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
|
||||||
|
# none = Empty template
|
||||||
|
#CONFIG_TLS=openssl
|
||||||
|
|
||||||
|
# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
|
||||||
|
# can be enabled to get a stronger construction of messages when block ciphers
|
||||||
|
# are used.
|
||||||
|
#CONFIG_TLSV11=y
|
||||||
|
|
||||||
|
# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
|
||||||
|
# can be enabled to enable use of stronger crypto algorithms.
|
||||||
|
#CONFIG_TLSV12=y
|
||||||
|
|
||||||
|
# Select which ciphers to use by default with OpenSSL if the user does not
|
||||||
|
# specify them.
|
||||||
|
#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
|
||||||
|
|
||||||
|
# If CONFIG_TLS=internal is used, additional library and include paths are
|
||||||
|
# needed for LibTomMath. Alternatively, an integrated, minimal version of
|
||||||
|
# LibTomMath can be used. See beginning of libtommath.c for details on benefits
|
||||||
|
# and drawbacks of this option.
|
||||||
|
#CONFIG_INTERNAL_LIBTOMMATH=y
|
||||||
|
#ifndef CONFIG_INTERNAL_LIBTOMMATH
|
||||||
|
#LTM_PATH=/usr/src/libtommath-0.39
|
||||||
|
#CFLAGS += -I$(LTM_PATH)
|
||||||
|
#LIBS += -L$(LTM_PATH)
|
||||||
|
#LIBS_p += -L$(LTM_PATH)
|
||||||
|
#endif
|
||||||
|
# At the cost of about 4 kB of additional binary size, the internal LibTomMath
|
||||||
|
# can be configured to include faster routines for exptmod, sqr, and div to
|
||||||
|
# speed up DH and RSA calculation considerably
|
||||||
|
#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
|
||||||
|
|
||||||
|
# Interworking (IEEE 802.11u)
|
||||||
|
# This can be used to enable functionality to improve interworking with
|
||||||
|
# external networks.
|
||||||
|
#CONFIG_INTERWORKING=y
|
||||||
|
|
||||||
|
# Hotspot 2.0
|
||||||
|
#CONFIG_HS20=y
|
||||||
|
|
||||||
|
# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
|
||||||
|
#CONFIG_SQLITE=y
|
||||||
|
|
||||||
|
# Enable Fast Session Transfer (FST)
|
||||||
|
#CONFIG_FST=y
|
||||||
|
|
||||||
|
# Enable CLI commands for FST testing
|
||||||
|
#CONFIG_FST_TEST=y
|
||||||
|
|
||||||
|
# Testing options
|
||||||
|
# This can be used to enable some testing options (see also the example
|
||||||
|
# configuration file) that are really useful only for testing clients that
|
||||||
|
# connect to this hostapd. These options allow, for example, to drop a
|
||||||
|
# certain percentage of probe requests or auth/(re)assoc frames.
|
||||||
|
#
|
||||||
|
#CONFIG_TESTING_OPTIONS=y
|
||||||
|
|
||||||
|
# Automatic Channel Selection
|
||||||
|
# This will allow hostapd to pick the channel automatically when channel is set
|
||||||
|
# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
|
||||||
|
# similar way.
|
||||||
|
#
|
||||||
|
# Automatic selection is currently only done through initialization, later on
|
||||||
|
# we hope to do background checks to keep us moving to more ideal channels as
|
||||||
|
# time goes by. ACS is currently only supported through the nl80211 driver and
|
||||||
|
# your driver must have survey dump capability that is filled by the driver
|
||||||
|
# during scanning.
|
||||||
|
#
|
||||||
|
# You can customize the ACS survey algorithm with the hostapd.conf variable
|
||||||
|
# acs_num_scans.
|
||||||
|
#
|
||||||
|
# Supported ACS drivers:
|
||||||
|
# * ath9k
|
||||||
|
# * ath5k
|
||||||
|
# * ath10k
|
||||||
|
#
|
||||||
|
# For more details refer to:
|
||||||
|
# http://wireless.kernel.org/en/users/Documentation/acs
|
||||||
|
#
|
||||||
|
#CONFIG_ACS=y
|
||||||
|
|
||||||
|
# Multiband Operation support
|
||||||
|
# These extentions facilitate efficient use of multiple frequency bands
|
||||||
|
# available to the AP and the devices that may associate with it.
|
||||||
|
#CONFIG_MBO=y
|
||||||
|
|
||||||
|
# Client Taxonomy
|
||||||
|
# Has the AP retain the Probe Request and (Re)Association Request frames from
|
||||||
|
# a client, from which a signature can be produced which can identify the model
|
||||||
|
# of client device like "Nexus 6P" or "iPhone 5s".
|
||||||
|
#CONFIG_TAXONOMY=y
|
||||||
|
|
||||||
|
# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
|
||||||
|
#CONFIG_FILS=y
|
||||||
|
# FILS shared key authentication with PFS
|
||||||
|
#CONFIG_FILS_SK_PFS=y
|
||||||
|
|
||||||
|
# Include internal line edit mode in hostapd_cli. This can be used to provide
|
||||||
|
# limited command line editing and history support.
|
||||||
|
#CONFIG_WPA_CLI_EDIT=y
|
||||||
|
|
||||||
|
# Opportunistic Wireless Encryption (OWE)
|
||||||
|
# Experimental implementation of draft-harkins-owe-07.txt
|
||||||
|
#CONFIG_OWE=y
|
||||||
|
|
||||||
|
# Airtime policy support
|
||||||
|
#CONFIG_AIRTIME_POLICY=y
|
||||||
|
|
||||||
|
# Override default value for the wpa_disable_eapol_key_retries configuration
|
||||||
|
# parameter. See that parameter in hostapd.conf for more details.
|
||||||
|
#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
|
155
hostapd-2.9/hostapd/eap_register.c
Normal file
155
hostapd-2.9/hostapd/eap_register.c
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* EAP method registration
|
||||||
|
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "eap_server/eap_methods.h"
|
||||||
|
#include "eap_register.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* eap_server_register_methods - Register statically linked EAP server methods
|
||||||
|
* Returns: 0 on success, -1 or -2 on failure
|
||||||
|
*
|
||||||
|
* This function is called at program initialization to register all EAP
|
||||||
|
* methods that were linked in statically.
|
||||||
|
*/
|
||||||
|
int eap_server_register_methods(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_IDENTITY
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_identity_register();
|
||||||
|
#endif /* EAP_SERVER_IDENTITY */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_MD5
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_md5_register();
|
||||||
|
#endif /* EAP_SERVER_MD5 */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TLS
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_tls_register();
|
||||||
|
#endif /* EAP_SERVER_TLS */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_UNAUTH_TLS
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_unauth_tls_register();
|
||||||
|
#endif /* EAP_SERVER_TLS */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TLS
|
||||||
|
#ifdef CONFIG_HS20
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_wfa_unauth_tls_register();
|
||||||
|
#endif /* CONFIG_HS20 */
|
||||||
|
#endif /* EAP_SERVER_TLS */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_MSCHAPV2
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_mschapv2_register();
|
||||||
|
#endif /* EAP_SERVER_MSCHAPV2 */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_PEAP
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_peap_register();
|
||||||
|
#endif /* EAP_SERVER_PEAP */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TLV
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_tlv_register();
|
||||||
|
#endif /* EAP_SERVER_TLV */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_GTC
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_gtc_register();
|
||||||
|
#endif /* EAP_SERVER_GTC */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TTLS
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_ttls_register();
|
||||||
|
#endif /* EAP_SERVER_TTLS */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_SIM
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_sim_register();
|
||||||
|
#endif /* EAP_SERVER_SIM */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_AKA
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_aka_register();
|
||||||
|
#endif /* EAP_SERVER_AKA */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_AKA_PRIME
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_aka_prime_register();
|
||||||
|
#endif /* EAP_SERVER_AKA_PRIME */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_PAX
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_pax_register();
|
||||||
|
#endif /* EAP_SERVER_PAX */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_PSK
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_psk_register();
|
||||||
|
#endif /* EAP_SERVER_PSK */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_SAKE
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_sake_register();
|
||||||
|
#endif /* EAP_SERVER_SAKE */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_GPSK
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_gpsk_register();
|
||||||
|
#endif /* EAP_SERVER_GPSK */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_VENDOR_TEST
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_vendor_test_register();
|
||||||
|
#endif /* EAP_SERVER_VENDOR_TEST */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_FAST
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_fast_register();
|
||||||
|
#endif /* EAP_SERVER_FAST */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TEAP
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_teap_register();
|
||||||
|
#endif /* EAP_SERVER_TEAP */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_WSC
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_wsc_register();
|
||||||
|
#endif /* EAP_SERVER_WSC */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_IKEV2
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_ikev2_register();
|
||||||
|
#endif /* EAP_SERVER_IKEV2 */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TNC
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_tnc_register();
|
||||||
|
#endif /* EAP_SERVER_TNC */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_PWD
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_pwd_register();
|
||||||
|
#endif /* EAP_SERVER_PWD */
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_EKE
|
||||||
|
if (ret == 0)
|
||||||
|
ret = eap_server_eke_register();
|
||||||
|
#endif /* EAP_SERVER_EKE */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
14
hostapd-2.9/hostapd/eap_register.h
Normal file
14
hostapd-2.9/hostapd/eap_register.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* EAP method registration
|
||||||
|
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EAP_REGISTER_H
|
||||||
|
#define EAP_REGISTER_H
|
||||||
|
|
||||||
|
int eap_server_register_methods(void);
|
||||||
|
|
||||||
|
#endif /* EAP_REGISTER_H */
|
77
hostapd-2.9/hostapd/eap_testing.txt
Normal file
77
hostapd-2.9/hostapd/eap_testing.txt
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
Interoperability testing of hostapd's IEEE 802.1X/EAPOL authentication
|
||||||
|
|
||||||
|
Test matrix
|
||||||
|
|
||||||
|
+) tested successfully
|
||||||
|
F) failed
|
||||||
|
-) peer did not support
|
||||||
|
?) not tested
|
||||||
|
|
||||||
|
XSupplicant --------------------------------.
|
||||||
|
Intel PROSet ---------------------------. |
|
||||||
|
Windows XP -------------------------. | |
|
||||||
|
Mac OS X 10.4 ------------------. | | |
|
||||||
|
Nokia S60 ------------------. | | | |
|
||||||
|
wpa_supplicant ---------. | | | | |
|
||||||
|
| | | | | |
|
||||||
|
|
||||||
|
EAP-MD5 + - ? ? -
|
||||||
|
EAP-GTC + - ? - -
|
||||||
|
EAP-MSCHAPv2 + - ? - -
|
||||||
|
EAP-TLS + + +1 + +
|
||||||
|
EAP-PEAPv0/MSCHAPv2 + + + + + +
|
||||||
|
EAP-PEAPv0/GTC + + + - +
|
||||||
|
EAP-PEAPv0/MD5 + - + - -
|
||||||
|
EAP-PEAPv0/TLS + F - + +
|
||||||
|
EAP-PEAPv0/SIM + + - - -
|
||||||
|
EAP-PEAPv0/AKA + + - - -
|
||||||
|
EAP-PEAPv0/PSK + - - - -
|
||||||
|
EAP-PEAPv0/PAX + - - - -
|
||||||
|
EAP-PEAPv0/SAKE + - - - -
|
||||||
|
EAP-PEAPv0/GPSK + - - - -
|
||||||
|
EAP-PEAPv1/MSCHAPv2 + + + - + +
|
||||||
|
EAP-PEAPv1/GTC + + + - +
|
||||||
|
EAP-PEAPv1/MD5 + - + - -
|
||||||
|
EAP-PEAPv1/TLS + F - - +
|
||||||
|
EAP-PEAPv1/SIM + + - - -
|
||||||
|
EAP-PEAPv1/AKA + + - - -
|
||||||
|
EAP-PEAPv1/PSK + - - - -
|
||||||
|
EAP-PEAPv1/PAX + - - - -
|
||||||
|
EAP-PEAPv1/SAKE + - - - -
|
||||||
|
EAP-PEAPv1/GPSK + - - - -
|
||||||
|
EAP-TTLS/CHAP + - + - + +
|
||||||
|
EAP-TTLS/MSCHAP + - + - + +
|
||||||
|
EAP-TTLS/MSCHAPv2 + + + - + +
|
||||||
|
EAP-TTLS/PAP + - + - + +
|
||||||
|
EAP-TTLS/EAP-MD5 + - - - - +
|
||||||
|
EAP-TTLS/EAP-GTC + + - - -
|
||||||
|
EAP-TTLS/EAP-MSCHAPv2 + + - - -
|
||||||
|
EAP-TTLS/EAP-TLS + F - - -
|
||||||
|
EAP-TTLS/EAP-SIM + + - - -
|
||||||
|
EAP-TTLS/EAP-AKA + + - - -
|
||||||
|
EAP-TTLS + TNC + - - - -
|
||||||
|
EAP-SIM + + - - +
|
||||||
|
EAP-AKA + + - - -
|
||||||
|
EAP-PAX + - - - -
|
||||||
|
EAP-SAKE + - - - -
|
||||||
|
EAP-GPSK + - - - -
|
||||||
|
EAP-FAST/MSCHAPv2(prov) + - F - F
|
||||||
|
EAP-FAST/GTC(auth) + - + - +
|
||||||
|
EAP-FAST/MSCHAPv2(aprov)+ - F - F
|
||||||
|
EAP-FAST/GTC(aprov) + - F - F
|
||||||
|
EAP-FAST/MD5(aprov) + - - - -
|
||||||
|
EAP-FAST/TLS(aprov) + - - - -
|
||||||
|
EAP-FAST/SIM(aprov) + - - - -
|
||||||
|
EAP-FAST/AKA(aprov) + - - - -
|
||||||
|
EAP-FAST/MSCHAPv2(auth) + - + - +
|
||||||
|
EAP-FAST/MD5(auth) + - + - -
|
||||||
|
EAP-FAST/TLS(auth) + - - - -
|
||||||
|
EAP-FAST/SIM(auth) + - - - -
|
||||||
|
EAP-FAST/AKA(auth) + - - - -
|
||||||
|
EAP-FAST + TNC + - - - -
|
||||||
|
EAP-IKEv2 + - - - -
|
||||||
|
EAP-TNC + - - - -
|
||||||
|
|
||||||
|
1) EAP-TLS itself worked, but peer certificate validation failed at
|
||||||
|
least when using the internal TLS server (peer included incorrect
|
||||||
|
certificates in the chain?)
|
18
hostapd-2.9/hostapd/hapd_module_tests.c
Normal file
18
hostapd-2.9/hostapd/hapd_module_tests.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* hostapd module tests
|
||||||
|
* Copyright (c) 2014, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/module_tests.h"
|
||||||
|
|
||||||
|
int hapd_module_tests(void)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_INFO, "hostapd module tests");
|
||||||
|
return 0;
|
||||||
|
}
|
1109
hostapd-2.9/hostapd/hlr_auc_gw.c
Normal file
1109
hostapd-2.9/hostapd/hlr_auc_gw.c
Normal file
File diff suppressed because it is too large
Load diff
15
hostapd-2.9/hostapd/hlr_auc_gw.milenage_db
Normal file
15
hostapd-2.9/hostapd/hlr_auc_gw.milenage_db
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Parameters for Milenage (Example algorithms for AKA).
|
||||||
|
# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0
|
||||||
|
# 4.3.20 Test Set 20. SQN is the last used SQN value.
|
||||||
|
# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM)
|
||||||
|
# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
|
||||||
|
# dummy values will need to be included in this file.
|
||||||
|
|
||||||
|
# IMSI Ki OPc AMF SQN [RES_len]
|
||||||
|
232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
|
||||||
|
# Example using truncated 32-bit RES instead of 64-bit default
|
||||||
|
232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4
|
||||||
|
|
||||||
|
# These values are from Test Set 19 which has the AMF separation bit set to 1
|
||||||
|
# and as such, is suitable for EAP-AKA' test.
|
||||||
|
555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1
|
104
hostapd-2.9/hostapd/hlr_auc_gw.txt
Normal file
104
hostapd-2.9/hostapd/hlr_auc_gw.txt
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
|
||||||
|
|
||||||
|
hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA'
|
||||||
|
database/authentication gateway interface to HLR/AuC. It could be
|
||||||
|
replaced with an implementation of SS7 gateway to GSM/UMTS
|
||||||
|
authentication center (HLR/AuC). hostapd will send SIM/AKA
|
||||||
|
authentication queries over a UNIX domain socket to and external
|
||||||
|
program, e.g., hlr_auc_gw.
|
||||||
|
|
||||||
|
hlr_auc_gw can be configured with GSM and UMTS authentication data with
|
||||||
|
text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see
|
||||||
|
hlr_auc_gw.milenage_db). Milenage parameters can be used to generate
|
||||||
|
dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the
|
||||||
|
GSM triplet data is used for a more static configuration (e.g., triplets
|
||||||
|
extracted from a SIM card).
|
||||||
|
|
||||||
|
Alternatively, hlr_auc_gw can be built with support for an SQLite
|
||||||
|
database for more dynamic operations. This is enabled by adding
|
||||||
|
"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make
|
||||||
|
clean; make hlr_auc_gw" in this directory).
|
||||||
|
|
||||||
|
hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in
|
||||||
|
hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw
|
||||||
|
is configured with command line parameters:
|
||||||
|
|
||||||
|
hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \
|
||||||
|
[-D<DB file>] [-i<IND len in bits>]
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h = show this usage help
|
||||||
|
-u = update SQN in Milenage file on exit
|
||||||
|
-s<socket path> = path for UNIX domain socket
|
||||||
|
(default: /tmp/hlr_auc_gw.sock)
|
||||||
|
-g<triplet file> = path for GSM authentication triplets
|
||||||
|
-m<milenage file> = path for Milenage keys
|
||||||
|
-D<DB file> = path to SQLite database
|
||||||
|
-i<IND len in bits> = IND length for SQN (default: 5)
|
||||||
|
|
||||||
|
|
||||||
|
The SQLite database can be initialized with sqlite, e.g., by running
|
||||||
|
following commands in "sqlite3 /path/to/hlr_auc_gw.db":
|
||||||
|
|
||||||
|
CREATE TABLE milenage(
|
||||||
|
imsi INTEGER PRIMARY KEY NOT NULL,
|
||||||
|
ki CHAR(32) NOT NULL,
|
||||||
|
opc CHAR(32) NOT NULL,
|
||||||
|
amf CHAR(4) NOT NULL,
|
||||||
|
sqn CHAR(12) NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
|
||||||
|
232010000000000,
|
||||||
|
'90dca4eda45b53cf0f12d7c9c3bc6a89',
|
||||||
|
'cb9cccc4b9258e6dca4760379fb82581',
|
||||||
|
'61df',
|
||||||
|
'000000000000'
|
||||||
|
);
|
||||||
|
INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
|
||||||
|
555444333222111,
|
||||||
|
'5122250214c33e723a5dd523fc145fc0',
|
||||||
|
'981d464c7c52eb6e5036234984ad0bcf',
|
||||||
|
'c3ab',
|
||||||
|
'16f3b3f70fc1'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
hostapd (EAP server) can also be configured to store the EAP-SIM/AKA
|
||||||
|
pseudonyms and reauth information into a SQLite database. This is
|
||||||
|
configured with the db parameter within the eap_sim_db configuration
|
||||||
|
option.
|
||||||
|
|
||||||
|
|
||||||
|
"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch
|
||||||
|
Milenage parameters based on IMSI from the database. The database can be
|
||||||
|
updated dynamically while hlr_auc_gw is running to add/remove/modify
|
||||||
|
entries.
|
||||||
|
|
||||||
|
|
||||||
|
Example configuration files for hostapd to operate as a RADIUS
|
||||||
|
authentication server for EAP-SIM/AKA/AKA':
|
||||||
|
|
||||||
|
hostapd.conf:
|
||||||
|
|
||||||
|
driver=none
|
||||||
|
radius_server_clients=hostapd.radius_clients
|
||||||
|
eap_server=1
|
||||||
|
eap_user_file=hostapd.eap_user
|
||||||
|
eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db
|
||||||
|
eap_sim_aka_result_ind=1
|
||||||
|
|
||||||
|
hostapd.radius_clients:
|
||||||
|
|
||||||
|
0.0.0.0/0 radius
|
||||||
|
|
||||||
|
hostapd.eap_user:
|
||||||
|
|
||||||
|
"0"* AKA
|
||||||
|
"1"* SIM
|
||||||
|
"2"* AKA
|
||||||
|
"3"* SIM
|
||||||
|
"4"* AKA
|
||||||
|
"5"* SIM
|
||||||
|
"6"* AKA'
|
||||||
|
"7"* AKA'
|
||||||
|
"8"* AKA'
|
59
hostapd-2.9/hostapd/hostapd.8
Normal file
59
hostapd-2.9/hostapd/hostapd.8
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
.TH HOSTAPD 8 "April 7, 2005" hostapd hostapd
|
||||||
|
.SH NAME
|
||||||
|
hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B hostapd
|
||||||
|
[\-hdBKtv] [\-P <PID file>] <configuration file(s)>
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This manual page documents briefly the
|
||||||
|
.B hostapd
|
||||||
|
daemon.
|
||||||
|
.PP
|
||||||
|
.B hostapd
|
||||||
|
is a user space daemon for access point and authentication servers.
|
||||||
|
It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
|
||||||
|
The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211).
|
||||||
|
|
||||||
|
.B hostapd
|
||||||
|
is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication.
|
||||||
|
.B hostapd
|
||||||
|
supports separate frontend programs and an example text-based frontend,
|
||||||
|
.BR hostapd_cli ,
|
||||||
|
is included with
|
||||||
|
.BR hostapd .
|
||||||
|
.SH OPTIONS
|
||||||
|
A summary of options is included below.
|
||||||
|
For a complete description, run
|
||||||
|
.BR hostapd
|
||||||
|
from the command line.
|
||||||
|
.TP
|
||||||
|
.B \-h
|
||||||
|
Show usage.
|
||||||
|
.TP
|
||||||
|
.B \-d
|
||||||
|
Show more debug messages.
|
||||||
|
.TP
|
||||||
|
.B \-dd
|
||||||
|
Show even more debug messages.
|
||||||
|
.TP
|
||||||
|
.B \-B
|
||||||
|
Run daemon in the background.
|
||||||
|
.TP
|
||||||
|
.B \-P <PID file>
|
||||||
|
Path to PID file.
|
||||||
|
.TP
|
||||||
|
.B \-K
|
||||||
|
Include key data in debug messages.
|
||||||
|
.TP
|
||||||
|
.B \-t
|
||||||
|
Include timestamps in some debug messages.
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
Show hostapd version.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR hostapd_cli (1).
|
||||||
|
.SH AUTHOR
|
||||||
|
hostapd was written by Jouni Malinen <j@w1.fi>.
|
||||||
|
.PP
|
||||||
|
This manual page was written by Faidon Liambotis <faidon@cube.gr>,
|
||||||
|
for the Debian project (but may be used by others).
|
6
hostapd-2.9/hostapd/hostapd.accept
Normal file
6
hostapd-2.9/hostapd/hostapd.accept
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# List of MAC addresses that are allowed to authenticate (IEEE 802.11)
|
||||||
|
# with the AP. Optional VLAN ID can be assigned for clients based on the
|
||||||
|
# MAC address if dynamic VLANs (hostapd.conf dynamic_vlan option) are used.
|
||||||
|
00:11:22:33:44:55
|
||||||
|
00:66:77:88:99:aa
|
||||||
|
00:00:22:33:44:55 1
|
19
hostapd-2.9/hostapd/hostapd.android.rc
Normal file
19
hostapd-2.9/hostapd/hostapd.android.rc
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#
|
||||||
|
# init.rc fragment for hostapd on Android
|
||||||
|
# Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
|
||||||
|
#
|
||||||
|
# This software may be distributed under the terms of the BSD license.
|
||||||
|
# See README for more details.
|
||||||
|
#
|
||||||
|
|
||||||
|
on post-fs-data
|
||||||
|
mkdir /data/misc/wifi/hostapd 0770 wifi wifi
|
||||||
|
|
||||||
|
service hostapd /vendor/bin/hostapd \
|
||||||
|
/data/misc/wifi/hostapd.conf
|
||||||
|
class main
|
||||||
|
user wifi
|
||||||
|
writepid /data/misc/wifi/hostapd.pid
|
||||||
|
group wifi
|
||||||
|
disabled
|
||||||
|
oneshot
|
2708
hostapd-2.9/hostapd/hostapd.conf
Normal file
2708
hostapd-2.9/hostapd/hostapd.conf
Normal file
File diff suppressed because it is too large
Load diff
5
hostapd-2.9/hostapd/hostapd.deny
Normal file
5
hostapd-2.9/hostapd/hostapd.deny
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# List of MAC addresses that are not allowed to authenticate (IEEE 802.11)
|
||||||
|
# with the AP.
|
||||||
|
00:20:30:40:50:60
|
||||||
|
00:ab:cd:ef:12:34
|
||||||
|
00:00:30:40:50:60
|
103
hostapd-2.9/hostapd/hostapd.eap_user
Normal file
103
hostapd-2.9/hostapd/hostapd.eap_user
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
# hostapd user database for integrated EAP server
|
||||||
|
|
||||||
|
# Each line must contain an identity, EAP method(s), and an optional password
|
||||||
|
# separated with whitespace (space or tab). The identity and password must be
|
||||||
|
# double quoted ("user"). Password can alternatively be stored as
|
||||||
|
# NtPasswordHash (16-byte MD4 hash of the unicode presentation of the password
|
||||||
|
# in unicode) if it is used for MSCHAP or MSCHAPv2 authentication. This means
|
||||||
|
# that the plaintext password does not need to be included in the user file.
|
||||||
|
# Password hash is stored as hash:<16-octets of hex data> without quotation
|
||||||
|
# marks.
|
||||||
|
|
||||||
|
# [2] flag in the end of the line can be used to mark users for tunneled phase
|
||||||
|
# 2 authentication (e.g., within EAP-PEAP). In these cases, an anonymous
|
||||||
|
# identity can be used in the unencrypted phase 1 and the real user identity
|
||||||
|
# is transmitted only within the encrypted tunnel in phase 2. If non-anonymous
|
||||||
|
# access is needed, two user entries is needed, one for phase 1 and another
|
||||||
|
# with the same username for phase 2.
|
||||||
|
#
|
||||||
|
# EAP-TLS, EAP-PEAP, EAP-TTLS, EAP-FAST, EAP-SIM, and EAP-AKA do not use
|
||||||
|
# password option.
|
||||||
|
# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, EAP-PSK, and EAP-SAKE require a
|
||||||
|
# password.
|
||||||
|
# EAP-PEAP, EAP-TTLS, and EAP-FAST require Phase 2 configuration.
|
||||||
|
#
|
||||||
|
# * can be used as a wildcard to match any user identity. The main purposes for
|
||||||
|
# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to
|
||||||
|
# avoid having to configure every certificate for EAP-TLS authentication. The
|
||||||
|
# first matching entry is selected, so * should be used as the last phase 1
|
||||||
|
# user entry.
|
||||||
|
#
|
||||||
|
# "prefix"* can be used to match the given prefix and anything after this. The
|
||||||
|
# main purpose for this is to be able to avoid EAP method negotiation when the
|
||||||
|
# method is using known prefix in identities (e.g., EAP-SIM and EAP-AKA). This
|
||||||
|
# is only allowed for phase 1 identities.
|
||||||
|
#
|
||||||
|
# Multiple methods can be configured to make the authenticator try them one by
|
||||||
|
# one until the peer accepts one. The method names are separated with a
|
||||||
|
# comma (,).
|
||||||
|
#
|
||||||
|
# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP
|
||||||
|
# version based on the Phase 1 identity. Without this flag, the EAP
|
||||||
|
# authenticator advertises the highest supported version and select the version
|
||||||
|
# based on the first PEAP packet from the supplicant.
|
||||||
|
#
|
||||||
|
# EAP-TTLS supports both EAP and non-EAP authentication inside the tunnel.
|
||||||
|
# Tunneled EAP methods are configured with standard EAP method name and [2]
|
||||||
|
# flag. Non-EAP methods can be enabled by following method names: TTLS-PAP,
|
||||||
|
# TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a
|
||||||
|
# plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password
|
||||||
|
# hash.
|
||||||
|
#
|
||||||
|
# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
|
||||||
|
# to the way radius_auth_req_attr is used for Access-Request packet in
|
||||||
|
# hostapd.conf. For EAP server, this is configured separately for each user
|
||||||
|
# entry with radius_accept_attr=<value> line(s) following the main user entry
|
||||||
|
# line.
|
||||||
|
|
||||||
|
# Phase 1 users
|
||||||
|
"user" MD5 "password"
|
||||||
|
"test user" MD5 "secret"
|
||||||
|
"example user" TLS
|
||||||
|
"DOMAIN\user" MSCHAPV2 "password"
|
||||||
|
"gtc user" GTC "password"
|
||||||
|
"pax user" PAX "unknown"
|
||||||
|
"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef
|
||||||
|
"psk user" PSK "unknown"
|
||||||
|
"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef
|
||||||
|
"sake.user@example.com" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||||
|
"ttls" TTLS
|
||||||
|
"not anonymous" PEAP
|
||||||
|
# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes
|
||||||
|
"0"* AKA,TTLS,TLS,PEAP,SIM
|
||||||
|
"1"* SIM,TTLS,TLS,PEAP,AKA
|
||||||
|
"2"* AKA,TTLS,TLS,PEAP,SIM
|
||||||
|
"3"* SIM,TTLS,TLS,PEAP,AKA
|
||||||
|
"4"* AKA,TTLS,TLS,PEAP,SIM
|
||||||
|
"5"* SIM,TTLS,TLS,PEAP,AKA
|
||||||
|
"6"* AKA'
|
||||||
|
"7"* AKA'
|
||||||
|
"8"* AKA'
|
||||||
|
|
||||||
|
# Wildcard for all other identities
|
||||||
|
* PEAP,TTLS,TLS,SIM,AKA
|
||||||
|
|
||||||
|
# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users
|
||||||
|
"t-md5" MD5 "password" [2]
|
||||||
|
"DOMAIN\t-mschapv2" MSCHAPV2 "password" [2]
|
||||||
|
"t-gtc" GTC "password" [2]
|
||||||
|
"not anonymous" MSCHAPV2 "password" [2]
|
||||||
|
"user" MD5,GTC,MSCHAPV2 "password" [2]
|
||||||
|
"test user" MSCHAPV2 hash:000102030405060708090a0b0c0d0e0f [2]
|
||||||
|
"ttls-user" TTLS-PAP,TTLS-CHAP,TTLS-MSCHAP,TTLS-MSCHAPV2 "password" [2]
|
||||||
|
|
||||||
|
# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes in phase 2
|
||||||
|
"0"* AKA [2]
|
||||||
|
"1"* SIM [2]
|
||||||
|
"2"* AKA [2]
|
||||||
|
"3"* SIM [2]
|
||||||
|
"4"* AKA [2]
|
||||||
|
"5"* SIM [2]
|
||||||
|
"6"* AKA' [2]
|
||||||
|
"7"* AKA' [2]
|
||||||
|
"8"* AKA' [2]
|
42
hostapd-2.9/hostapd/hostapd.eap_user_sqlite
Normal file
42
hostapd-2.9/hostapd/hostapd.eap_user_sqlite
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
CREATE TABLE users(
|
||||||
|
identity TEXT PRIMARY KEY,
|
||||||
|
methods TEXT,
|
||||||
|
password TEXT,
|
||||||
|
remediation TEXT,
|
||||||
|
phase2 INTEGER,
|
||||||
|
t_c_timestamp INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE wildcards(
|
||||||
|
identity TEXT PRIMARY KEY,
|
||||||
|
methods TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1);
|
||||||
|
INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1);
|
||||||
|
|
||||||
|
INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
|
||||||
|
INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
|
||||||
|
|
||||||
|
CREATE TABLE authlog(
|
||||||
|
timestamp TEXT,
|
||||||
|
session TEXT,
|
||||||
|
nas_ip TEXT,
|
||||||
|
username TEXT,
|
||||||
|
note TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE pending_tc(
|
||||||
|
mac_addr TEXT PRIMARY KEY,
|
||||||
|
identity TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE current_sessions(
|
||||||
|
mac_addr TEXT PRIMARY KEY,
|
||||||
|
identity TEXT,
|
||||||
|
start_time TEXT,
|
||||||
|
nas TEXT,
|
||||||
|
hs20_t_c_filtering BOOLEAN,
|
||||||
|
waiting_coa_ack BOOLEAN,
|
||||||
|
coa_ack_received BOOLEAN
|
||||||
|
);
|
4
hostapd-2.9/hostapd/hostapd.radius_clients
Normal file
4
hostapd-2.9/hostapd/hostapd.radius_clients
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# RADIUS client configuration for the RADIUS server
|
||||||
|
10.1.2.3 secret passphrase
|
||||||
|
192.168.1.0/24 another very secret passphrase
|
||||||
|
0.0.0.0/0 radius
|
9
hostapd-2.9/hostapd/hostapd.sim_db
Normal file
9
hostapd-2.9/hostapd/hostapd.sim_db
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Example GSM authentication triplet file for EAP-SIM authenticator
|
||||||
|
# IMSI:Kc:SRES:RAND
|
||||||
|
# IMSI: ASCII string (numbers)
|
||||||
|
# Kc: hex, 8 octets
|
||||||
|
# SRES: hex, 4 octets
|
||||||
|
# RAND: hex, 16 octets
|
||||||
|
234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
9
hostapd-2.9/hostapd/hostapd.vlan
Normal file
9
hostapd-2.9/hostapd/hostapd.vlan
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# VLAN ID to network interface mapping
|
||||||
|
1 vlan1
|
||||||
|
2 vlan2
|
||||||
|
3 vlan3
|
||||||
|
100 guest
|
||||||
|
# Optional wildcard entry matching all VLAN IDs. The first # in the interface
|
||||||
|
# name will be replaced with the VLAN ID. The network interfaces are created
|
||||||
|
# (and removed) dynamically based on the use.
|
||||||
|
* vlan#
|
15
hostapd-2.9/hostapd/hostapd.wpa_psk
Normal file
15
hostapd-2.9/hostapd/hostapd.wpa_psk
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# List of WPA PSKs. Each line, except for empty lines and lines starting
|
||||||
|
# with #, must contain a MAC address and PSK separated with a space.
|
||||||
|
# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that
|
||||||
|
# anyone can use. PSK can be configured as an ASCII passphrase of 8..63
|
||||||
|
# characters or as a 256-bit hex PSK (64 hex digits).
|
||||||
|
# An optional key identifier can be added by prefixing the line with
|
||||||
|
# keyid=<keyid_string>
|
||||||
|
# An optional VLAN ID can be specified by prefixing the line with
|
||||||
|
# vlanid=<VLAN ID>.
|
||||||
|
00:00:00:00:00:00 secret passphrase
|
||||||
|
00:11:22:33:44:55 another passphrase
|
||||||
|
00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||||
|
keyid=example_id 00:11:22:33:44:77 passphrase with keyid
|
||||||
|
vlanid=3 00:00:00:00:00:00 passphrase with vlanid
|
||||||
|
00:00:00:00:00:00 another passphrase for all STAs
|
89
hostapd-2.9/hostapd/hostapd_cli.1
Normal file
89
hostapd-2.9/hostapd/hostapd_cli.1
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
.TH HOSTAPD_CLI 1 "April 7, 2005" hostapd_cli "hostapd command-line interface"
|
||||||
|
.SH NAME
|
||||||
|
hostapd_cli \- hostapd command-line interface
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B hostapd_cli
|
||||||
|
[\-p<path>] [\-i<ifname>] [\-a<path>] [\-hvB] [command..]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This manual page documents briefly the
|
||||||
|
.B hostapd_cli
|
||||||
|
utility.
|
||||||
|
.PP
|
||||||
|
.B hostapd_cli
|
||||||
|
is a command-line interface for the
|
||||||
|
.B hostapd
|
||||||
|
daemon.
|
||||||
|
|
||||||
|
.B hostapd
|
||||||
|
is a user space daemon for access point and authentication servers.
|
||||||
|
It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
|
||||||
|
For more information about
|
||||||
|
.B hostapd
|
||||||
|
refer to the
|
||||||
|
.BR hostapd (8)
|
||||||
|
man page.
|
||||||
|
.SH OPTIONS
|
||||||
|
A summary of options is included below.
|
||||||
|
For a complete description, run
|
||||||
|
.BR hostapd_cli
|
||||||
|
from the command line.
|
||||||
|
.TP
|
||||||
|
.B \-p<path>
|
||||||
|
Path to find control sockets.
|
||||||
|
|
||||||
|
Default: /var/run/hostapd
|
||||||
|
.TP
|
||||||
|
.B \-i<ifname>
|
||||||
|
Interface to listen on.
|
||||||
|
|
||||||
|
Default: first interface found in socket path.
|
||||||
|
.TP
|
||||||
|
.B \-a<path>
|
||||||
|
Run in daemon mode executing the action file based on events from hostapd.
|
||||||
|
.TP
|
||||||
|
.B \-B
|
||||||
|
Run a daemon in the background.
|
||||||
|
.TP
|
||||||
|
.B \-h
|
||||||
|
Show usage.
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
Show hostapd_cli version.
|
||||||
|
.SH COMMANDS
|
||||||
|
A summary of commands is included below.
|
||||||
|
For a complete description, run
|
||||||
|
.BR hostapd_cli
|
||||||
|
from the command line.
|
||||||
|
.TP
|
||||||
|
.B mib
|
||||||
|
Get MIB variables (dot1x, dot11, radius).
|
||||||
|
.TP
|
||||||
|
.B sta <addr>
|
||||||
|
Get MIB variables for one station.
|
||||||
|
.TP
|
||||||
|
.B all_sta
|
||||||
|
Get MIB variables for all stations.
|
||||||
|
.TP
|
||||||
|
.B help
|
||||||
|
Get usage help.
|
||||||
|
.TP
|
||||||
|
.B interface [ifname]
|
||||||
|
Show interfaces/select interface.
|
||||||
|
.TP
|
||||||
|
.B level <debug level>
|
||||||
|
Change debug level.
|
||||||
|
.TP
|
||||||
|
.B license
|
||||||
|
Show full
|
||||||
|
.B hostapd_cli
|
||||||
|
license.
|
||||||
|
.TP
|
||||||
|
.B quit
|
||||||
|
Exit hostapd_cli.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR hostapd (8).
|
||||||
|
.SH AUTHOR
|
||||||
|
hostapd_cli was written by Jouni Malinen <j@w1.fi>.
|
||||||
|
.PP
|
||||||
|
This manual page was written by Faidon Liambotis <faidon@cube.gr>,
|
||||||
|
for the Debian project (but may be used by others).
|
2131
hostapd-2.9/hostapd/hostapd_cli.c
Normal file
2131
hostapd-2.9/hostapd/hostapd_cli.c
Normal file
File diff suppressed because it is too large
Load diff
9
hostapd-2.9/hostapd/logwatch/README
Normal file
9
hostapd-2.9/hostapd/logwatch/README
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Logwatch is a utility for analyzing system logs and provide a human
|
||||||
|
readable summary. This directory has a configuration file and a log
|
||||||
|
analyzer script for parsing hostapd system log entries for logwatch.
|
||||||
|
These files can be installed by copying them to following locations:
|
||||||
|
|
||||||
|
/etc/log.d/conf/services/hostapd.conf
|
||||||
|
/etc/log.d/scripts/services/hostapd
|
||||||
|
|
||||||
|
More information about logwatch is available from http://www.logwatch.org/
|
65
hostapd-2.9/hostapd/logwatch/hostapd
Executable file
65
hostapd-2.9/hostapd/logwatch/hostapd
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# Logwatch script for hostapd
|
||||||
|
#
|
||||||
|
# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org>
|
||||||
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
|
# Alternatively, this file may be distributed under the terms of the BSD License
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
my $debug = $ENV{'LOGWATCH_DEBUG'} || 0;
|
||||||
|
my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
|
||||||
|
my $debugcounter = 1;
|
||||||
|
|
||||||
|
my %hostapd;
|
||||||
|
my @unmatched;
|
||||||
|
|
||||||
|
if ($debug >= 5) {
|
||||||
|
print STDERR "\n\nDEBUG: Inside HOSTAPD Filter\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
while (defined(my $line = <STDIN>)) {
|
||||||
|
if ($debug >= 5) {
|
||||||
|
print STDERR "DEBUG($debugcounter): $line";
|
||||||
|
$debugcounter++;
|
||||||
|
}
|
||||||
|
chomp($line);
|
||||||
|
|
||||||
|
if (my ($iface,$mac,$layer,$details) = ($line =~ /(.*?): STA (.*?) (.*?): (.*?)$/i)) {
|
||||||
|
unless ($detail == 10) {
|
||||||
|
# collapse association events
|
||||||
|
$details =~ s/^(associated) .*$/$1/i;
|
||||||
|
}
|
||||||
|
$hostapd{$iface}->{$mac}->{$layer}->{$details}++;
|
||||||
|
} else {
|
||||||
|
push @unmatched, "$line\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys %hostapd) {
|
||||||
|
foreach my $iface (sort keys %hostapd) {
|
||||||
|
print "Interface $iface:\n";
|
||||||
|
foreach my $mac (sort keys %{$hostapd{$iface}}) {
|
||||||
|
print " Client MAC Address $mac:\n";
|
||||||
|
foreach my $layer (sort keys %{$hostapd{$iface}->{$mac}}) {
|
||||||
|
print " $layer:\n";
|
||||||
|
foreach my $details (sort keys %{$hostapd{$iface}->{$mac}->{$layer}}) {
|
||||||
|
print " $details";
|
||||||
|
my $count = $hostapd{$iface}->{$mac}->{$layer}->{$details};
|
||||||
|
if ($count > 1) {
|
||||||
|
print ": " . $count . " Times";
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($#unmatched >= 0) {
|
||||||
|
print "\n**Unmatched Entries**\n";
|
||||||
|
print @unmatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
10
hostapd-2.9/hostapd/logwatch/hostapd.conf
Normal file
10
hostapd-2.9/hostapd/logwatch/hostapd.conf
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Logwatch configuration for hostapd
|
||||||
|
#
|
||||||
|
# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org>
|
||||||
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
|
# Alternatively, this file may be distributed under the terms of the BSD License
|
||||||
|
|
||||||
|
Title = "hostapd"
|
||||||
|
LogFile = messages
|
||||||
|
*OnlyService = hostapd
|
||||||
|
*RemoveHeaders
|
936
hostapd-2.9/hostapd/main.c
Normal file
936
hostapd-2.9/hostapd/main.c
Normal file
|
@ -0,0 +1,936 @@
|
||||||
|
/*
|
||||||
|
* hostapd / main()
|
||||||
|
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "utils/uuid.h"
|
||||||
|
#include "crypto/random.h"
|
||||||
|
#include "crypto/tls.h"
|
||||||
|
#include "common/version.h"
|
||||||
|
#include "common/dpp.h"
|
||||||
|
#include "drivers/driver.h"
|
||||||
|
#include "eap_server/eap.h"
|
||||||
|
#include "eap_server/tncs.h"
|
||||||
|
#include "ap/hostapd.h"
|
||||||
|
#include "ap/ap_config.h"
|
||||||
|
#include "ap/ap_drv_ops.h"
|
||||||
|
#include "ap/dpp_hostapd.h"
|
||||||
|
#include "fst/fst.h"
|
||||||
|
#include "config_file.h"
|
||||||
|
#include "eap_register.h"
|
||||||
|
#include "ctrl_iface.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct hapd_global {
|
||||||
|
void **drv_priv;
|
||||||
|
size_t drv_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct hapd_global global;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_HOSTAPD_LOGGER
|
||||||
|
static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
|
||||||
|
int level, const char *txt, size_t len)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = ctx;
|
||||||
|
char *format, *module_str;
|
||||||
|
int maxlen;
|
||||||
|
int conf_syslog_level, conf_stdout_level;
|
||||||
|
unsigned int conf_syslog, conf_stdout;
|
||||||
|
|
||||||
|
maxlen = len + 100;
|
||||||
|
format = os_malloc(maxlen);
|
||||||
|
if (!format)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hapd && hapd->conf) {
|
||||||
|
conf_syslog_level = hapd->conf->logger_syslog_level;
|
||||||
|
conf_stdout_level = hapd->conf->logger_stdout_level;
|
||||||
|
conf_syslog = hapd->conf->logger_syslog;
|
||||||
|
conf_stdout = hapd->conf->logger_stdout;
|
||||||
|
} else {
|
||||||
|
conf_syslog_level = conf_stdout_level = 0;
|
||||||
|
conf_syslog = conf_stdout = (unsigned int) -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (module) {
|
||||||
|
case HOSTAPD_MODULE_IEEE80211:
|
||||||
|
module_str = "IEEE 802.11";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODULE_IEEE8021X:
|
||||||
|
module_str = "IEEE 802.1X";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODULE_RADIUS:
|
||||||
|
module_str = "RADIUS";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODULE_WPA:
|
||||||
|
module_str = "WPA";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODULE_DRIVER:
|
||||||
|
module_str = "DRIVER";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODULE_IAPP:
|
||||||
|
module_str = "IAPP";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODULE_MLME:
|
||||||
|
module_str = "MLME";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
module_str = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd && hapd->conf && addr)
|
||||||
|
os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
|
||||||
|
hapd->conf->iface, MAC2STR(addr),
|
||||||
|
module_str ? " " : "", module_str ? module_str : "",
|
||||||
|
txt);
|
||||||
|
else if (hapd && hapd->conf)
|
||||||
|
os_snprintf(format, maxlen, "%s:%s%s %s",
|
||||||
|
hapd->conf->iface, module_str ? " " : "",
|
||||||
|
module_str ? module_str : "", txt);
|
||||||
|
else if (addr)
|
||||||
|
os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
|
||||||
|
MAC2STR(addr), module_str ? " " : "",
|
||||||
|
module_str ? module_str : "", txt);
|
||||||
|
else
|
||||||
|
os_snprintf(format, maxlen, "%s%s%s",
|
||||||
|
module_str ? module_str : "",
|
||||||
|
module_str ? ": " : "", txt);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_SYSLOG
|
||||||
|
if (wpa_debug_syslog)
|
||||||
|
conf_stdout = 0;
|
||||||
|
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||||
|
if ((conf_stdout & module) && level >= conf_stdout_level) {
|
||||||
|
wpa_debug_print_timestamp();
|
||||||
|
wpa_printf(MSG_INFO, "%s", format);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
if ((conf_syslog & module) && level >= conf_syslog_level) {
|
||||||
|
int priority;
|
||||||
|
switch (level) {
|
||||||
|
case HOSTAPD_LEVEL_DEBUG_VERBOSE:
|
||||||
|
case HOSTAPD_LEVEL_DEBUG:
|
||||||
|
priority = LOG_DEBUG;
|
||||||
|
break;
|
||||||
|
case HOSTAPD_LEVEL_INFO:
|
||||||
|
priority = LOG_INFO;
|
||||||
|
break;
|
||||||
|
case HOSTAPD_LEVEL_NOTICE:
|
||||||
|
priority = LOG_NOTICE;
|
||||||
|
break;
|
||||||
|
case HOSTAPD_LEVEL_WARNING:
|
||||||
|
priority = LOG_WARNING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
priority = LOG_INFO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
syslog(priority, "%s", format);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
|
||||||
|
os_free(format);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_driver_init - Preparate driver interface
|
||||||
|
*/
|
||||||
|
static int hostapd_driver_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct wpa_init_params params;
|
||||||
|
size_t i;
|
||||||
|
struct hostapd_data *hapd = iface->bss[0];
|
||||||
|
struct hostapd_bss_config *conf = hapd->conf;
|
||||||
|
u8 *b = conf->bssid;
|
||||||
|
struct wpa_driver_capa capa;
|
||||||
|
|
||||||
|
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the driver interface */
|
||||||
|
if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
|
||||||
|
b = NULL;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
for (i = 0; wpa_drivers[i]; i++) {
|
||||||
|
if (wpa_drivers[i] != hapd->driver)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (global.drv_priv[i] == NULL &&
|
||||||
|
wpa_drivers[i]->global_init) {
|
||||||
|
global.drv_priv[i] =
|
||||||
|
wpa_drivers[i]->global_init(iface->interfaces);
|
||||||
|
if (global.drv_priv[i] == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize "
|
||||||
|
"driver '%s'",
|
||||||
|
wpa_drivers[i]->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.global_priv = global.drv_priv[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
params.bssid = b;
|
||||||
|
params.ifname = hapd->conf->iface;
|
||||||
|
params.driver_params = hapd->iconf->driver_params;
|
||||||
|
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
|
||||||
|
|
||||||
|
params.num_bridge = hapd->iface->num_bss;
|
||||||
|
params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
|
||||||
|
if (params.bridge == NULL)
|
||||||
|
return -1;
|
||||||
|
for (i = 0; i < hapd->iface->num_bss; i++) {
|
||||||
|
struct hostapd_data *bss = hapd->iface->bss[i];
|
||||||
|
if (bss->conf->bridge[0])
|
||||||
|
params.bridge[i] = bss->conf->bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.own_addr = hapd->own_addr;
|
||||||
|
|
||||||
|
hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms);
|
||||||
|
os_free(params.bridge);
|
||||||
|
if (hapd->drv_priv == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "%s driver initialization failed.",
|
||||||
|
hapd->driver->name);
|
||||||
|
hapd->driver = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->driver->get_capa &&
|
||||||
|
hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
|
||||||
|
struct wowlan_triggers *triggs;
|
||||||
|
|
||||||
|
iface->drv_flags = capa.flags;
|
||||||
|
iface->smps_modes = capa.smps_modes;
|
||||||
|
iface->probe_resp_offloads = capa.probe_resp_offloads;
|
||||||
|
/*
|
||||||
|
* Use default extended capa values from per-radio information
|
||||||
|
*/
|
||||||
|
iface->extended_capa = capa.extended_capa;
|
||||||
|
iface->extended_capa_mask = capa.extended_capa_mask;
|
||||||
|
iface->extended_capa_len = capa.extended_capa_len;
|
||||||
|
iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Override extended capa with per-interface type (AP), if
|
||||||
|
* available from the driver.
|
||||||
|
*/
|
||||||
|
hostapd_get_ext_capa(iface);
|
||||||
|
|
||||||
|
triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
|
||||||
|
if (triggs && hapd->driver->set_wowlan) {
|
||||||
|
if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
|
||||||
|
wpa_printf(MSG_ERROR, "set_wowlan failed");
|
||||||
|
}
|
||||||
|
os_free(triggs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_interface_init - Read configuration file and init BSS data
|
||||||
|
*
|
||||||
|
* This function is used to parse configuration file for a full interface (one
|
||||||
|
* or more BSSes sharing the same radio) and allocate memory for the BSS
|
||||||
|
* interfaces. No actual driver operations are started.
|
||||||
|
*/
|
||||||
|
static struct hostapd_iface *
|
||||||
|
hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
|
||||||
|
const char *config_fname, int debug)
|
||||||
|
{
|
||||||
|
struct hostapd_iface *iface;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
|
||||||
|
iface = hostapd_init(interfaces, config_fname);
|
||||||
|
if (!iface)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (if_name) {
|
||||||
|
os_strlcpy(iface->conf->bss[0]->iface, if_name,
|
||||||
|
sizeof(iface->conf->bss[0]->iface));
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->interfaces = interfaces;
|
||||||
|
|
||||||
|
for (k = 0; k < debug; k++) {
|
||||||
|
if (iface->bss[0]->conf->logger_stdout_level > 0)
|
||||||
|
iface->bss[0]->conf->logger_stdout_level--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->conf->bss[0]->iface[0] == '\0' &&
|
||||||
|
!hostapd_drv_none(iface->bss[0])) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Interface name not specified in %s, nor by '-i' parameter",
|
||||||
|
config_fname);
|
||||||
|
hostapd_interface_deinit_free(iface);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle_term - SIGINT and SIGTERM handler to terminate hostapd process
|
||||||
|
*/
|
||||||
|
static void handle_term(int sig, void *signal_ctx)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
|
||||||
|
eloop_terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
|
||||||
|
static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
|
||||||
|
{
|
||||||
|
if (hostapd_reload_config(iface) < 0) {
|
||||||
|
wpa_printf(MSG_WARNING, "Failed to read new configuration "
|
||||||
|
"file - continuing with old.");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle_reload - SIGHUP handler to reload configuration
|
||||||
|
*/
|
||||||
|
static void handle_reload(int sig, void *signal_ctx)
|
||||||
|
{
|
||||||
|
struct hapd_interfaces *interfaces = signal_ctx;
|
||||||
|
wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration",
|
||||||
|
sig);
|
||||||
|
hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_dump_state(int sig, void *signal_ctx)
|
||||||
|
{
|
||||||
|
/* Not used anymore - ignore signal */
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_global_init(struct hapd_interfaces *interfaces,
|
||||||
|
const char *entropy_file)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
os_memset(&global, 0, sizeof(global));
|
||||||
|
|
||||||
|
hostapd_logger_register_cb(hostapd_logger_cb);
|
||||||
|
|
||||||
|
if (eap_server_register_methods()) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eloop_init()) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
interfaces->eloop_initialized = 1;
|
||||||
|
|
||||||
|
random_init(entropy_file);
|
||||||
|
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
eloop_register_signal(SIGHUP, handle_reload, interfaces);
|
||||||
|
eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
eloop_register_signal_terminate(handle_term, interfaces);
|
||||||
|
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
openlog("hostapd", 0, LOG_DAEMON);
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
|
||||||
|
for (i = 0; wpa_drivers[i]; i++)
|
||||||
|
global.drv_count++;
|
||||||
|
if (global.drv_count == 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "No drivers enabled");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
|
||||||
|
if (global.drv_priv == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_global_deinit(const char *pid_file, int eloop_initialized)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
|
||||||
|
if (!global.drv_priv[i])
|
||||||
|
continue;
|
||||||
|
wpa_drivers[i]->global_deinit(global.drv_priv[i]);
|
||||||
|
}
|
||||||
|
os_free(global.drv_priv);
|
||||||
|
global.drv_priv = NULL;
|
||||||
|
|
||||||
|
#ifdef EAP_SERVER_TNC
|
||||||
|
tncs_global_deinit();
|
||||||
|
#endif /* EAP_SERVER_TNC */
|
||||||
|
|
||||||
|
random_deinit();
|
||||||
|
|
||||||
|
if (eloop_initialized)
|
||||||
|
eloop_destroy();
|
||||||
|
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
closelog();
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
|
||||||
|
eap_server_unregister_methods();
|
||||||
|
|
||||||
|
os_daemonize_terminate(pid_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
|
||||||
|
const char *pid_file)
|
||||||
|
{
|
||||||
|
#ifdef EAP_SERVER_TNC
|
||||||
|
int tnc = 0;
|
||||||
|
size_t i, k;
|
||||||
|
|
||||||
|
for (i = 0; !tnc && i < ifaces->count; i++) {
|
||||||
|
for (k = 0; k < ifaces->iface[i]->num_bss; k++) {
|
||||||
|
if (ifaces->iface[i]->bss[0]->conf->tnc) {
|
||||||
|
tnc++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tnc && tncs_global_init() < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* EAP_SERVER_TNC */
|
||||||
|
|
||||||
|
if (daemonize) {
|
||||||
|
if (os_daemonize(pid_file)) {
|
||||||
|
wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (eloop_sock_requeue()) {
|
||||||
|
wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eloop_run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void show_version(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"hostapd v" VERSION_STR " for Realtek rtl871xdrv\n"
|
||||||
|
"User space daemon for IEEE 802.11 AP management,\n"
|
||||||
|
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
|
||||||
|
"Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> "
|
||||||
|
"and contributors\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
show_version();
|
||||||
|
fprintf(stderr,
|
||||||
|
"\n"
|
||||||
|
"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
|
||||||
|
"\\\n"
|
||||||
|
" [-g <global ctrl_iface>] [-G <group>]\\\n"
|
||||||
|
" [-i <comma-separated list of interface names>]\\\n"
|
||||||
|
" <configuration file(s)>\n"
|
||||||
|
"\n"
|
||||||
|
"options:\n"
|
||||||
|
" -h show this usage\n"
|
||||||
|
" -d show more debug messages (-dd for even more)\n"
|
||||||
|
" -B run daemon in the background\n"
|
||||||
|
" -e entropy file\n"
|
||||||
|
" -g global control interface path\n"
|
||||||
|
" -G group for control interfaces\n"
|
||||||
|
" -P PID file\n"
|
||||||
|
" -K include key data in debug messages\n"
|
||||||
|
#ifdef CONFIG_DEBUG_FILE
|
||||||
|
" -f log output to debug file instead of stdout\n"
|
||||||
|
#endif /* CONFIG_DEBUG_FILE */
|
||||||
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||||
|
" -T record to Linux tracing in addition to logging\n"
|
||||||
|
" (records all messages regardless of debug verbosity)\n"
|
||||||
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||||
|
" -i list of interface names to use\n"
|
||||||
|
#ifdef CONFIG_DEBUG_SYSLOG
|
||||||
|
" -s log output to syslog instead of stdout\n"
|
||||||
|
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||||
|
" -S start all the interfaces synchronously\n"
|
||||||
|
" -t include timestamps in some debug messages\n"
|
||||||
|
" -v show hostapd version\n");
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char * hostapd_msg_ifname_cb(void *ctx)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = ctx;
|
||||||
|
if (hapd && hapd->conf)
|
||||||
|
return hapd->conf->iface;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
|
||||||
|
const char *path)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_CTRL_IFACE_UDP
|
||||||
|
char *pos;
|
||||||
|
#endif /* !CONFIG_CTRL_IFACE_UDP */
|
||||||
|
|
||||||
|
os_free(interfaces->global_iface_path);
|
||||||
|
interfaces->global_iface_path = os_strdup(path);
|
||||||
|
if (interfaces->global_iface_path == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#ifndef CONFIG_CTRL_IFACE_UDP
|
||||||
|
pos = os_strrchr(interfaces->global_iface_path, '/');
|
||||||
|
if (pos == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "No '/' in the global control interface "
|
||||||
|
"file");
|
||||||
|
os_free(interfaces->global_iface_path);
|
||||||
|
interfaces->global_iface_path = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pos = '\0';
|
||||||
|
interfaces->global_iface_name = pos + 1;
|
||||||
|
#endif /* !CONFIG_CTRL_IFACE_UDP */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
|
||||||
|
const char *group)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
struct group *grp;
|
||||||
|
grp = getgrnam(group);
|
||||||
|
if (grp == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "Unknown group '%s'", group);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
interfaces->ctrl_iface_group = grp->gr_gid;
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_get_interface_names(char ***if_names,
|
||||||
|
size_t *if_names_size,
|
||||||
|
char *arg)
|
||||||
|
{
|
||||||
|
char *if_name, *tmp, **nnames;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!arg)
|
||||||
|
return -1;
|
||||||
|
if_name = strtok_r(arg, ",", &tmp);
|
||||||
|
|
||||||
|
while (if_name) {
|
||||||
|
nnames = os_realloc_array(*if_names, 1 + *if_names_size,
|
||||||
|
sizeof(char *));
|
||||||
|
if (!nnames)
|
||||||
|
goto fail;
|
||||||
|
*if_names = nnames;
|
||||||
|
|
||||||
|
(*if_names)[*if_names_size] = os_strdup(if_name);
|
||||||
|
if (!(*if_names)[*if_names_size])
|
||||||
|
goto fail;
|
||||||
|
(*if_names_size)++;
|
||||||
|
if_name = strtok_r(NULL, ",", &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
for (i = 0; i < *if_names_size; i++)
|
||||||
|
os_free((*if_names)[i]);
|
||||||
|
os_free(*if_names);
|
||||||
|
*if_names = NULL;
|
||||||
|
*if_names_size = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_WPS
|
||||||
|
static int gen_uuid(const char *txt_addr)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
u8 uuid[UUID_LEN];
|
||||||
|
char buf[100];
|
||||||
|
|
||||||
|
if (hwaddr_aton(txt_addr, addr) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uuid_gen_mac_addr(addr, uuid);
|
||||||
|
if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
printf("%s\n", buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_WPS */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HOSTAPD_CLEANUP_INTERVAL
|
||||||
|
#define HOSTAPD_CLEANUP_INTERVAL 10
|
||||||
|
#endif /* HOSTAPD_CLEANUP_INTERVAL */
|
||||||
|
|
||||||
|
static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
|
||||||
|
{
|
||||||
|
hostapd_periodic_iface(iface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Periodic cleanup tasks */
|
||||||
|
static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
|
||||||
|
{
|
||||||
|
struct hapd_interfaces *interfaces = eloop_ctx;
|
||||||
|
|
||||||
|
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
|
||||||
|
hostapd_periodic, interfaces, NULL);
|
||||||
|
hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct hapd_interfaces interfaces;
|
||||||
|
int ret = 1;
|
||||||
|
size_t i, j;
|
||||||
|
int c, debug = 0, daemonize = 0;
|
||||||
|
char *pid_file = NULL;
|
||||||
|
const char *log_file = NULL;
|
||||||
|
const char *entropy_file = NULL;
|
||||||
|
char **bss_config = NULL, **tmp_bss;
|
||||||
|
size_t num_bss_configs = 0;
|
||||||
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||||
|
int enable_trace_dbg = 0;
|
||||||
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||||
|
int start_ifaces_in_sync = 0;
|
||||||
|
char **if_names = NULL;
|
||||||
|
size_t if_names_size = 0;
|
||||||
|
#ifdef CONFIG_DPP
|
||||||
|
struct dpp_global_config dpp_conf;
|
||||||
|
#endif /* CONFIG_DPP */
|
||||||
|
|
||||||
|
if (os_program_init())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
os_memset(&interfaces, 0, sizeof(interfaces));
|
||||||
|
interfaces.reload_config = hostapd_reload_config;
|
||||||
|
interfaces.config_read_cb = hostapd_config_read;
|
||||||
|
interfaces.for_each_interface = hostapd_for_each_interface;
|
||||||
|
interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
|
||||||
|
interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
|
||||||
|
interfaces.driver_init = hostapd_driver_init;
|
||||||
|
interfaces.global_iface_path = NULL;
|
||||||
|
interfaces.global_iface_name = NULL;
|
||||||
|
interfaces.global_ctrl_sock = -1;
|
||||||
|
dl_list_init(&interfaces.global_ctrl_dst);
|
||||||
|
#ifdef CONFIG_ETH_P_OUI
|
||||||
|
dl_list_init(&interfaces.eth_p_oui);
|
||||||
|
#endif /* CONFIG_ETH_P_OUI */
|
||||||
|
#ifdef CONFIG_DPP
|
||||||
|
os_memset(&dpp_conf, 0, sizeof(dpp_conf));
|
||||||
|
/* TODO: dpp_conf.msg_ctx? */
|
||||||
|
interfaces.dpp = dpp_global_init(&dpp_conf);
|
||||||
|
if (!interfaces.dpp)
|
||||||
|
return -1;
|
||||||
|
#endif /* CONFIG_DPP */
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
|
||||||
|
if (c < 0)
|
||||||
|
break;
|
||||||
|
switch (c) {
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
debug++;
|
||||||
|
if (wpa_debug_level > 0)
|
||||||
|
wpa_debug_level--;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
daemonize++;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
entropy_file = optarg;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
log_file = optarg;
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
wpa_debug_show_keys++;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
os_free(pid_file);
|
||||||
|
pid_file = os_rel2abs_path(optarg);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
wpa_debug_timestamp++;
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||||
|
case 'T':
|
||||||
|
enable_trace_dbg = 1;
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||||
|
case 'v':
|
||||||
|
show_version();
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
tmp_bss = os_realloc_array(bss_config,
|
||||||
|
num_bss_configs + 1,
|
||||||
|
sizeof(char *));
|
||||||
|
if (tmp_bss == NULL)
|
||||||
|
goto out;
|
||||||
|
bss_config = tmp_bss;
|
||||||
|
bss_config[num_bss_configs++] = optarg;
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_DEBUG_SYSLOG
|
||||||
|
case 's':
|
||||||
|
wpa_debug_syslog = 1;
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||||
|
case 'S':
|
||||||
|
start_ifaces_in_sync = 1;
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_WPS
|
||||||
|
case 'u':
|
||||||
|
return gen_uuid(optarg);
|
||||||
|
#endif /* CONFIG_WPS */
|
||||||
|
case 'i':
|
||||||
|
if (hostapd_get_interface_names(&if_names,
|
||||||
|
&if_names_size, optarg))
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind == argc && interfaces.global_iface_path == NULL &&
|
||||||
|
num_bss_configs == 0)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
|
||||||
|
|
||||||
|
if (log_file)
|
||||||
|
wpa_debug_open_file(log_file);
|
||||||
|
else
|
||||||
|
wpa_debug_setup_stdout();
|
||||||
|
#ifdef CONFIG_DEBUG_SYSLOG
|
||||||
|
if (wpa_debug_syslog)
|
||||||
|
wpa_debug_open_syslog();
|
||||||
|
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||||
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||||
|
if (enable_trace_dbg) {
|
||||||
|
int tret = wpa_debug_open_linux_tracing();
|
||||||
|
if (tret) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to enable trace logging");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||||
|
|
||||||
|
interfaces.count = argc - optind;
|
||||||
|
if (interfaces.count || num_bss_configs) {
|
||||||
|
interfaces.iface = os_calloc(interfaces.count + num_bss_configs,
|
||||||
|
sizeof(struct hostapd_iface *));
|
||||||
|
if (interfaces.iface == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "malloc failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostapd_global_init(&interfaces, entropy_file)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize global context");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
|
||||||
|
hostapd_periodic, &interfaces, NULL);
|
||||||
|
|
||||||
|
if (fst_global_init()) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Failed to initialize global FST context");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
|
||||||
|
if (!fst_global_add_ctrl(fst_ctrl_cli))
|
||||||
|
wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
|
||||||
|
#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
|
||||||
|
|
||||||
|
/* Allocate and parse configuration for full interface files */
|
||||||
|
for (i = 0; i < interfaces.count; i++) {
|
||||||
|
char *if_name = NULL;
|
||||||
|
|
||||||
|
if (i < if_names_size)
|
||||||
|
if_name = if_names[i];
|
||||||
|
|
||||||
|
interfaces.iface[i] = hostapd_interface_init(&interfaces,
|
||||||
|
if_name,
|
||||||
|
argv[optind + i],
|
||||||
|
debug);
|
||||||
|
if (!interfaces.iface[i]) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize interface");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (start_ifaces_in_sync)
|
||||||
|
interfaces.iface[i]->need_to_start_in_sync = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate and parse configuration for per-BSS files */
|
||||||
|
for (i = 0; i < num_bss_configs; i++) {
|
||||||
|
struct hostapd_iface *iface;
|
||||||
|
char *fname;
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);
|
||||||
|
fname = os_strchr(bss_config[i], ':');
|
||||||
|
if (fname == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Invalid BSS config identifier '%s'",
|
||||||
|
bss_config[i]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*fname++ = '\0';
|
||||||
|
iface = hostapd_interface_init_bss(&interfaces, bss_config[i],
|
||||||
|
fname, debug);
|
||||||
|
if (iface == NULL)
|
||||||
|
goto out;
|
||||||
|
for (j = 0; j < interfaces.count; j++) {
|
||||||
|
if (interfaces.iface[j] == iface)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j == interfaces.count) {
|
||||||
|
struct hostapd_iface **tmp;
|
||||||
|
tmp = os_realloc_array(interfaces.iface,
|
||||||
|
interfaces.count + 1,
|
||||||
|
sizeof(struct hostapd_iface *));
|
||||||
|
if (tmp == NULL) {
|
||||||
|
hostapd_interface_deinit_free(iface);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
interfaces.iface = tmp;
|
||||||
|
interfaces.iface[interfaces.count++] = iface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable configured interfaces. Depending on channel configuration,
|
||||||
|
* this may complete full initialization before returning or use a
|
||||||
|
* callback mechanism to complete setup in case of operations like HT
|
||||||
|
* co-ex scans, ACS, or DFS are needed to determine channel parameters.
|
||||||
|
* In such case, the interface will be enabled from eloop context within
|
||||||
|
* hostapd_global_run().
|
||||||
|
*/
|
||||||
|
interfaces.terminate_on_error = interfaces.count;
|
||||||
|
for (i = 0; i < interfaces.count; i++) {
|
||||||
|
if (hostapd_driver_init(interfaces.iface[i]) ||
|
||||||
|
hostapd_setup_interface(interfaces.iface[i]))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostapd_global_ctrl_iface_init(&interfaces);
|
||||||
|
|
||||||
|
if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to start eloop");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
hostapd_global_ctrl_iface_deinit(&interfaces);
|
||||||
|
/* Deinitialize all interfaces */
|
||||||
|
for (i = 0; i < interfaces.count; i++) {
|
||||||
|
if (!interfaces.iface[i])
|
||||||
|
continue;
|
||||||
|
interfaces.iface[i]->driver_ap_teardown =
|
||||||
|
!!(interfaces.iface[i]->drv_flags &
|
||||||
|
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
|
||||||
|
hostapd_interface_deinit_free(interfaces.iface[i]);
|
||||||
|
}
|
||||||
|
os_free(interfaces.iface);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DPP
|
||||||
|
dpp_global_deinit(interfaces.dpp);
|
||||||
|
#endif /* CONFIG_DPP */
|
||||||
|
|
||||||
|
if (interfaces.eloop_initialized)
|
||||||
|
eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
|
||||||
|
hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
|
||||||
|
os_free(pid_file);
|
||||||
|
|
||||||
|
wpa_debug_close_syslog();
|
||||||
|
if (log_file)
|
||||||
|
wpa_debug_close_file();
|
||||||
|
wpa_debug_close_linux_tracing();
|
||||||
|
|
||||||
|
os_free(bss_config);
|
||||||
|
|
||||||
|
for (i = 0; i < if_names_size; i++)
|
||||||
|
os_free(if_names[i]);
|
||||||
|
os_free(if_names);
|
||||||
|
|
||||||
|
fst_global_deinit();
|
||||||
|
|
||||||
|
os_program_deinit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
47
hostapd-2.9/hostapd/nt_password_hash.c
Normal file
47
hostapd-2.9/hostapd/nt_password_hash.c
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* hostapd - Plaintext password to NtPasswordHash
|
||||||
|
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "crypto/ms_funcs.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
unsigned char password_hash[16];
|
||||||
|
size_t i;
|
||||||
|
char *password, buf[64], *pos;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
password = argv[1];
|
||||||
|
else {
|
||||||
|
if (fgets(buf, sizeof(buf), stdin) == NULL) {
|
||||||
|
printf("Failed to read password\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
pos = buf;
|
||||||
|
while (*pos != '\0') {
|
||||||
|
if (*pos == '\r' || *pos == '\n') {
|
||||||
|
*pos = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
password = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nt_password_hash((u8 *) password, strlen(password), password_hash))
|
||||||
|
return -1;
|
||||||
|
for (i = 0; i < sizeof(password_hash); i++)
|
||||||
|
printf("%02x", password_hash[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
40
hostapd-2.9/hostapd/wired.conf
Normal file
40
hostapd-2.9/hostapd/wired.conf
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
##### hostapd configuration file ##############################################
|
||||||
|
# Empty lines and lines starting with # are ignored
|
||||||
|
|
||||||
|
# Example configuration file for wired authenticator. See hostapd.conf for
|
||||||
|
# more details.
|
||||||
|
|
||||||
|
interface=eth0
|
||||||
|
driver=wired
|
||||||
|
logger_stdout=-1
|
||||||
|
logger_stdout_level=1
|
||||||
|
debug=2
|
||||||
|
dump_file=/tmp/hostapd.dump
|
||||||
|
|
||||||
|
ieee8021x=1
|
||||||
|
eap_reauth_period=3600
|
||||||
|
|
||||||
|
use_pae_group_addr=1
|
||||||
|
|
||||||
|
|
||||||
|
##### RADIUS configuration ####################################################
|
||||||
|
# for IEEE 802.1X with external Authentication Server, IEEE 802.11
|
||||||
|
# authentication with external ACL for MAC addresses, and accounting
|
||||||
|
|
||||||
|
# The own IP address of the access point (used as NAS-IP-Address)
|
||||||
|
own_ip_addr=127.0.0.1
|
||||||
|
|
||||||
|
# Optional NAS-Identifier string for RADIUS messages. When used, this should be
|
||||||
|
# a unique to the NAS within the scope of the RADIUS server. For example, a
|
||||||
|
# fully qualified domain name can be used here.
|
||||||
|
nas_identifier=ap.example.com
|
||||||
|
|
||||||
|
# RADIUS authentication server
|
||||||
|
auth_server_addr=127.0.0.1
|
||||||
|
auth_server_port=1812
|
||||||
|
auth_server_shared_secret=radius
|
||||||
|
|
||||||
|
# RADIUS accounting server
|
||||||
|
acct_server_addr=127.0.0.1
|
||||||
|
acct_server_port=1813
|
||||||
|
acct_server_shared_secret=radius
|
342
hostapd-2.9/hostapd/wps-ap-nfc.py
Executable file
342
hostapd-2.9/hostapd/wps-ap-nfc.py
Executable file
|
@ -0,0 +1,342 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Example nfcpy to hostapd wrapper for WPS NFC operations
|
||||||
|
# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
|
||||||
|
#
|
||||||
|
# This software may be distributed under the terms of the BSD license.
|
||||||
|
# See README for more details.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
import nfc
|
||||||
|
import nfc.ndef
|
||||||
|
import nfc.llcp
|
||||||
|
import nfc.handover
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import wpaspy
|
||||||
|
|
||||||
|
wpas_ctrl = '/var/run/hostapd'
|
||||||
|
continue_loop = True
|
||||||
|
summary_file = None
|
||||||
|
success_file = None
|
||||||
|
|
||||||
|
def summary(txt):
|
||||||
|
print(txt)
|
||||||
|
if summary_file:
|
||||||
|
with open(summary_file, 'a') as f:
|
||||||
|
f.write(txt + "\n")
|
||||||
|
|
||||||
|
def success_report(txt):
|
||||||
|
summary(txt)
|
||||||
|
if success_file:
|
||||||
|
with open(success_file, 'a') as f:
|
||||||
|
f.write(txt + "\n")
|
||||||
|
|
||||||
|
def wpas_connect():
|
||||||
|
ifaces = []
|
||||||
|
if os.path.isdir(wpas_ctrl):
|
||||||
|
try:
|
||||||
|
ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
|
||||||
|
except OSError as error:
|
||||||
|
print("Could not find hostapd: ", error)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(ifaces) < 1:
|
||||||
|
print("No hostapd control interface found")
|
||||||
|
return None
|
||||||
|
|
||||||
|
for ctrl in ifaces:
|
||||||
|
try:
|
||||||
|
wpas = wpaspy.Ctrl(ctrl)
|
||||||
|
return wpas
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def wpas_tag_read(message):
|
||||||
|
wpas = wpas_connect()
|
||||||
|
if (wpas == None):
|
||||||
|
return False
|
||||||
|
if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def wpas_get_config_token():
|
||||||
|
wpas = wpas_connect()
|
||||||
|
if (wpas == None):
|
||||||
|
return None
|
||||||
|
ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
|
||||||
|
if "FAIL" in ret:
|
||||||
|
return None
|
||||||
|
return ret.rstrip().decode("hex")
|
||||||
|
|
||||||
|
|
||||||
|
def wpas_get_password_token():
|
||||||
|
wpas = wpas_connect()
|
||||||
|
if (wpas == None):
|
||||||
|
return None
|
||||||
|
ret = wpas.request("WPS_NFC_TOKEN NDEF")
|
||||||
|
if "FAIL" in ret:
|
||||||
|
return None
|
||||||
|
return ret.rstrip().decode("hex")
|
||||||
|
|
||||||
|
|
||||||
|
def wpas_get_handover_sel():
|
||||||
|
wpas = wpas_connect()
|
||||||
|
if (wpas == None):
|
||||||
|
return None
|
||||||
|
ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
|
||||||
|
if "FAIL" in ret:
|
||||||
|
return None
|
||||||
|
return ret.rstrip().decode("hex")
|
||||||
|
|
||||||
|
|
||||||
|
def wpas_report_handover(req, sel):
|
||||||
|
wpas = wpas_connect()
|
||||||
|
if (wpas == None):
|
||||||
|
return None
|
||||||
|
return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
|
||||||
|
str(req).encode("hex") + " " +
|
||||||
|
str(sel).encode("hex"))
|
||||||
|
|
||||||
|
|
||||||
|
class HandoverServer(nfc.handover.HandoverServer):
|
||||||
|
def __init__(self, llc):
|
||||||
|
super(HandoverServer, self).__init__(llc)
|
||||||
|
self.ho_server_processing = False
|
||||||
|
self.success = False
|
||||||
|
|
||||||
|
# override to avoid parser error in request/response.pretty() in nfcpy
|
||||||
|
# due to new WSC handover format
|
||||||
|
def _process_request(self, request):
|
||||||
|
summary("received handover request {}".format(request.type))
|
||||||
|
response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
|
||||||
|
if not request.type == 'urn:nfc:wkt:Hr':
|
||||||
|
summary("not a handover request")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
request = nfc.ndef.HandoverRequestMessage(request)
|
||||||
|
except nfc.ndef.DecodeError as e:
|
||||||
|
summary("error decoding 'Hr' message: {}".format(e))
|
||||||
|
else:
|
||||||
|
response = self.process_request(request)
|
||||||
|
summary("send handover response {}".format(response.type))
|
||||||
|
return response
|
||||||
|
|
||||||
|
def process_request(self, request):
|
||||||
|
summary("HandoverServer - request received")
|
||||||
|
try:
|
||||||
|
print("Parsed handover request: " + request.pretty())
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print(str(request).encode("hex"))
|
||||||
|
|
||||||
|
sel = nfc.ndef.HandoverSelectMessage(version="1.2")
|
||||||
|
|
||||||
|
for carrier in request.carriers:
|
||||||
|
print("Remote carrier type: " + carrier.type)
|
||||||
|
if carrier.type == "application/vnd.wfa.wsc":
|
||||||
|
summary("WPS carrier type match - add WPS carrier record")
|
||||||
|
data = wpas_get_handover_sel()
|
||||||
|
if data is None:
|
||||||
|
summary("Could not get handover select carrier record from hostapd")
|
||||||
|
continue
|
||||||
|
print("Handover select carrier record from hostapd:")
|
||||||
|
print(data.encode("hex"))
|
||||||
|
if "OK" in wpas_report_handover(carrier.record, data):
|
||||||
|
success_report("Handover reported successfully")
|
||||||
|
else:
|
||||||
|
summary("Handover report rejected")
|
||||||
|
|
||||||
|
message = nfc.ndef.Message(data);
|
||||||
|
sel.add_carrier(message[0], "active", message[1:])
|
||||||
|
|
||||||
|
print("Handover select:")
|
||||||
|
try:
|
||||||
|
print(sel.pretty())
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print(str(sel).encode("hex"))
|
||||||
|
|
||||||
|
summary("Sending handover select")
|
||||||
|
self.success = True
|
||||||
|
return sel
|
||||||
|
|
||||||
|
|
||||||
|
def wps_tag_read(tag):
|
||||||
|
success = False
|
||||||
|
if len(tag.ndef.message):
|
||||||
|
for record in tag.ndef.message:
|
||||||
|
print("record type " + record.type)
|
||||||
|
if record.type == "application/vnd.wfa.wsc":
|
||||||
|
summary("WPS tag - send to hostapd")
|
||||||
|
success = wpas_tag_read(tag.ndef.message)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
summary("Empty tag")
|
||||||
|
|
||||||
|
if success:
|
||||||
|
success_report("Tag read succeeded")
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
|
def rdwr_connected_write(tag):
|
||||||
|
summary("Tag found - writing - " + str(tag))
|
||||||
|
global write_data
|
||||||
|
tag.ndef.message = str(write_data)
|
||||||
|
success_report("Tag write succeeded")
|
||||||
|
print("Done - remove tag")
|
||||||
|
global only_one
|
||||||
|
if only_one:
|
||||||
|
global continue_loop
|
||||||
|
continue_loop = False
|
||||||
|
global write_wait_remove
|
||||||
|
while write_wait_remove and tag.is_present:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def wps_write_config_tag(clf, wait_remove=True):
|
||||||
|
summary("Write WPS config token")
|
||||||
|
global write_data, write_wait_remove
|
||||||
|
write_wait_remove = wait_remove
|
||||||
|
write_data = wpas_get_config_token()
|
||||||
|
if write_data == None:
|
||||||
|
summary("Could not get WPS config token from hostapd")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Touch an NFC tag")
|
||||||
|
clf.connect(rdwr={'on-connect': rdwr_connected_write})
|
||||||
|
|
||||||
|
|
||||||
|
def wps_write_password_tag(clf, wait_remove=True):
|
||||||
|
summary("Write WPS password token")
|
||||||
|
global write_data, write_wait_remove
|
||||||
|
write_wait_remove = wait_remove
|
||||||
|
write_data = wpas_get_password_token()
|
||||||
|
if write_data == None:
|
||||||
|
summary("Could not get WPS password token from hostapd")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Touch an NFC tag")
|
||||||
|
clf.connect(rdwr={'on-connect': rdwr_connected_write})
|
||||||
|
|
||||||
|
|
||||||
|
def rdwr_connected(tag):
|
||||||
|
global only_one, no_wait
|
||||||
|
summary("Tag connected: " + str(tag))
|
||||||
|
|
||||||
|
if tag.ndef:
|
||||||
|
print("NDEF tag: " + tag.type)
|
||||||
|
try:
|
||||||
|
print(tag.ndef.message.pretty())
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
success = wps_tag_read(tag)
|
||||||
|
if only_one and success:
|
||||||
|
global continue_loop
|
||||||
|
continue_loop = False
|
||||||
|
else:
|
||||||
|
summary("Not an NDEF tag - remove tag")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return not no_wait
|
||||||
|
|
||||||
|
|
||||||
|
def llcp_startup(clf, llc):
|
||||||
|
print("Start LLCP server")
|
||||||
|
global srv
|
||||||
|
srv = HandoverServer(llc)
|
||||||
|
return llc
|
||||||
|
|
||||||
|
def llcp_connected(llc):
|
||||||
|
print("P2P LLCP connected")
|
||||||
|
global wait_connection
|
||||||
|
wait_connection = False
|
||||||
|
global srv
|
||||||
|
srv.start()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
clf = nfc.ContactlessFrontend()
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
|
||||||
|
parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
|
||||||
|
action='store_const', dest='loglevel',
|
||||||
|
help='verbose debug output')
|
||||||
|
parser.add_argument('-q', const=logging.WARNING, action='store_const',
|
||||||
|
dest='loglevel', help='be quiet')
|
||||||
|
parser.add_argument('--only-one', '-1', action='store_true',
|
||||||
|
help='run only one operation and exit')
|
||||||
|
parser.add_argument('--no-wait', action='store_true',
|
||||||
|
help='do not wait for tag to be removed before exiting')
|
||||||
|
parser.add_argument('--summary',
|
||||||
|
help='summary file for writing status updates')
|
||||||
|
parser.add_argument('--success',
|
||||||
|
help='success file for writing success update')
|
||||||
|
parser.add_argument('command', choices=['write-config',
|
||||||
|
'write-password'],
|
||||||
|
nargs='?')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
global only_one
|
||||||
|
only_one = args.only_one
|
||||||
|
|
||||||
|
global no_wait
|
||||||
|
no_wait = args.no_wait
|
||||||
|
|
||||||
|
if args.summary:
|
||||||
|
global summary_file
|
||||||
|
summary_file = args.summary
|
||||||
|
|
||||||
|
if args.success:
|
||||||
|
global success_file
|
||||||
|
success_file = args.success
|
||||||
|
|
||||||
|
logging.basicConfig(level=args.loglevel)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not clf.open("usb"):
|
||||||
|
print("Could not open connection with an NFC device")
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
if args.command == "write-config":
|
||||||
|
wps_write_config_tag(clf, wait_remove=not args.no_wait)
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
if args.command == "write-password":
|
||||||
|
wps_write_password_tag(clf, wait_remove=not args.no_wait)
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
global continue_loop
|
||||||
|
while continue_loop:
|
||||||
|
print("Waiting for a tag or peer to be touched")
|
||||||
|
wait_connection = True
|
||||||
|
try:
|
||||||
|
if not clf.connect(rdwr={'on-connect': rdwr_connected},
|
||||||
|
llcp={'on-startup': llcp_startup,
|
||||||
|
'on-connect': llcp_connected}):
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print("clf.connect failed")
|
||||||
|
|
||||||
|
global srv
|
||||||
|
if only_one and srv and srv.success:
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise SystemExit
|
||||||
|
finally:
|
||||||
|
clf.close()
|
||||||
|
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
12
hostapd-2.9/src/Makefile
Normal file
12
hostapd-2.9/src/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
|
||||||
|
SUBDIRS += fst
|
||||||
|
|
||||||
|
all:
|
||||||
|
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
|
||||||
|
rm -f *~
|
||||||
|
|
||||||
|
install:
|
||||||
|
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
|
76
hostapd-2.9/src/ap/Makefile
Normal file
76
hostapd-2.9/src/ap/Makefile
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
all: libap.a
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
|
||||||
|
|
||||||
|
install:
|
||||||
|
@echo Nothing to be made.
|
||||||
|
|
||||||
|
include ../lib.rules
|
||||||
|
|
||||||
|
CFLAGS += -DHOSTAPD
|
||||||
|
CFLAGS += -DNEED_AP_MLME
|
||||||
|
CFLAGS += -DCONFIG_ETH_P_OUI
|
||||||
|
CFLAGS += -DCONFIG_HS20
|
||||||
|
CFLAGS += -DCONFIG_INTERWORKING
|
||||||
|
CFLAGS += -DCONFIG_IEEE80211R
|
||||||
|
CFLAGS += -DCONFIG_IEEE80211R_AP
|
||||||
|
CFLAGS += -DCONFIG_IEEE80211W
|
||||||
|
CFLAGS += -DCONFIG_WPS
|
||||||
|
CFLAGS += -DCONFIG_PROXYARP
|
||||||
|
CFLAGS += -DCONFIG_IPV6
|
||||||
|
CFLAGS += -DCONFIG_IAPP
|
||||||
|
CFLAGS += -DCONFIG_AIRTIME_POLICY
|
||||||
|
|
||||||
|
LIB_OBJS= \
|
||||||
|
accounting.o \
|
||||||
|
ap_config.o \
|
||||||
|
ap_drv_ops.o \
|
||||||
|
ap_list.o \
|
||||||
|
ap_mlme.o \
|
||||||
|
airtime_policy.o \
|
||||||
|
authsrv.o \
|
||||||
|
beacon.o \
|
||||||
|
bss_load.o \
|
||||||
|
ctrl_iface_ap.o \
|
||||||
|
dfs.o \
|
||||||
|
dhcp_snoop.o \
|
||||||
|
drv_callbacks.o \
|
||||||
|
eap_user_db.o \
|
||||||
|
eth_p_oui.o \
|
||||||
|
gas_serv.o \
|
||||||
|
hostapd.o \
|
||||||
|
hs20.o \
|
||||||
|
hw_features.o \
|
||||||
|
iapp.o \
|
||||||
|
ieee802_11_auth.o \
|
||||||
|
ieee802_11.o \
|
||||||
|
ieee802_11_ht.o \
|
||||||
|
ieee802_11_shared.o \
|
||||||
|
ieee802_11_vht.o \
|
||||||
|
ieee802_1x.o \
|
||||||
|
neighbor_db.o \
|
||||||
|
ndisc_snoop.o \
|
||||||
|
p2p_hostapd.o \
|
||||||
|
pmksa_cache_auth.o \
|
||||||
|
preauth_auth.o \
|
||||||
|
rrm.o \
|
||||||
|
sta_info.o \
|
||||||
|
tkip_countermeasures.o \
|
||||||
|
utils.o \
|
||||||
|
vlan.o \
|
||||||
|
vlan_ifconfig.o \
|
||||||
|
vlan_init.o \
|
||||||
|
wmm.o \
|
||||||
|
wnm_ap.o \
|
||||||
|
wpa_auth.o \
|
||||||
|
wpa_auth_ft.o \
|
||||||
|
wpa_auth_glue.o \
|
||||||
|
wpa_auth_ie.o \
|
||||||
|
wps_hostapd.o \
|
||||||
|
x_snoop.o
|
||||||
|
|
||||||
|
libap.a: $(LIB_OBJS)
|
||||||
|
$(AR) crT $@ $?
|
||||||
|
|
||||||
|
-include $(OBJS:%.o=%.d)
|
547
hostapd-2.9/src/ap/accounting.c
Normal file
547
hostapd-2.9/src/ap/accounting.c
Normal file
|
@ -0,0 +1,547 @@
|
||||||
|
/*
|
||||||
|
* hostapd / RADIUS Accounting
|
||||||
|
* Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "eapol_auth/eapol_auth_sm.h"
|
||||||
|
#include "eapol_auth/eapol_auth_sm_i.h"
|
||||||
|
#include "radius/radius.h"
|
||||||
|
#include "radius/radius_client.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ieee802_1x.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "accounting.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Default interval in seconds for polling TX/RX octets from the driver if
|
||||||
|
* STA is not using interim accounting. This detects wrap arounds for
|
||||||
|
* input/output octets and updates Acct-{Input,Output}-Gigawords. */
|
||||||
|
#define ACCT_DEFAULT_UPDATE_INTERVAL 300
|
||||||
|
|
||||||
|
static void accounting_sta_interim(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
|
||||||
|
|
||||||
|
static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
int status_type)
|
||||||
|
{
|
||||||
|
struct radius_msg *msg;
|
||||||
|
char buf[128];
|
||||||
|
u8 *val;
|
||||||
|
size_t len;
|
||||||
|
int i;
|
||||||
|
struct wpabuf *b;
|
||||||
|
struct os_time now;
|
||||||
|
|
||||||
|
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
|
||||||
|
radius_client_get_id(hapd->radius));
|
||||||
|
if (msg == NULL) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
|
||||||
|
status_type)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta) {
|
||||||
|
if (!hostapd_config_get_radius_attr(
|
||||||
|
hapd->conf->radius_acct_req_attr,
|
||||||
|
RADIUS_ATTR_ACCT_AUTHENTIC) &&
|
||||||
|
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
|
||||||
|
hapd->conf->ieee802_1x ?
|
||||||
|
RADIUS_ACCT_AUTHENTIC_RADIUS :
|
||||||
|
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use 802.1X identity if available */
|
||||||
|
val = ieee802_1x_get_identity(sta->eapol_sm, &len);
|
||||||
|
|
||||||
|
/* Use RADIUS ACL identity if 802.1X provides no identity */
|
||||||
|
if (!val && sta->identity) {
|
||||||
|
val = (u8 *) sta->identity;
|
||||||
|
len = os_strlen(sta->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
|
||||||
|
* identity */
|
||||||
|
if (!val) {
|
||||||
|
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
val = (u8 *) buf;
|
||||||
|
len = os_strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
|
||||||
|
len)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add User-Name");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
|
||||||
|
msg) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (sta) {
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
|
||||||
|
i);
|
||||||
|
if (val == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
|
||||||
|
val, len)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Class");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b = ieee802_1x_get_radius_cui(sta->eapol_sm);
|
||||||
|
if (b &&
|
||||||
|
!radius_msg_add_attr(msg,
|
||||||
|
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||||
|
wpabuf_head(b), wpabuf_len(b))) {
|
||||||
|
wpa_printf(MSG_ERROR, "Could not add CUI");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!b && sta->radius_cui &&
|
||||||
|
!radius_msg_add_attr(msg,
|
||||||
|
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||||
|
(u8 *) sta->radius_cui,
|
||||||
|
os_strlen(sta->radius_cui))) {
|
||||||
|
wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta->ipaddr &&
|
||||||
|
!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_FRAMED_IP_ADDRESS,
|
||||||
|
be_to_host32(sta->ipaddr))) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Could not add Framed-IP-Address");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os_get_time(&now);
|
||||||
|
if (now.sec > 1000000000 &&
|
||||||
|
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
|
||||||
|
now.sec)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add Acct-Delay-Time with zero value for the first transmission. This
|
||||||
|
* will be updated within radius_client.c when retransmitting the frame.
|
||||||
|
*/
|
||||||
|
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
radius_msg_free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int accounting_sta_update_stats(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
struct hostap_sta_driver_data *data)
|
||||||
|
{
|
||||||
|
if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!data->bytes_64bit) {
|
||||||
|
/* Extend 32-bit counters from the driver to 64-bit counters */
|
||||||
|
if (sta->last_rx_bytes_lo > data->rx_bytes)
|
||||||
|
sta->last_rx_bytes_hi++;
|
||||||
|
sta->last_rx_bytes_lo = data->rx_bytes;
|
||||||
|
|
||||||
|
if (sta->last_tx_bytes_lo > data->tx_bytes)
|
||||||
|
sta->last_tx_bytes_hi++;
|
||||||
|
sta->last_tx_bytes_lo = data->tx_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
|
||||||
|
data->rx_bytes, sta->last_rx_bytes_hi,
|
||||||
|
sta->last_rx_bytes_lo,
|
||||||
|
data->tx_bytes, sta->last_tx_bytes_hi,
|
||||||
|
sta->last_tx_bytes_lo,
|
||||||
|
data->bytes_64bit);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = eloop_ctx;
|
||||||
|
struct sta_info *sta = timeout_ctx;
|
||||||
|
int interval;
|
||||||
|
|
||||||
|
if (sta->acct_interim_interval) {
|
||||||
|
accounting_sta_interim(hapd, sta);
|
||||||
|
interval = sta->acct_interim_interval;
|
||||||
|
} else {
|
||||||
|
struct hostap_sta_driver_data data;
|
||||||
|
accounting_sta_update_stats(hapd, sta, &data);
|
||||||
|
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
eloop_register_timeout(interval, 0, accounting_interim_update,
|
||||||
|
hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accounting_sta_start - Start STA accounting
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* @sta: The station
|
||||||
|
*/
|
||||||
|
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
struct radius_msg *msg;
|
||||||
|
int interval;
|
||||||
|
|
||||||
|
if (sta->acct_session_started)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||||
|
HOSTAPD_LEVEL_INFO,
|
||||||
|
"starting accounting session %016llX",
|
||||||
|
(unsigned long long) sta->acct_session_id);
|
||||||
|
|
||||||
|
os_get_reltime(&sta->acct_session_start);
|
||||||
|
sta->last_rx_bytes_hi = 0;
|
||||||
|
sta->last_rx_bytes_lo = 0;
|
||||||
|
sta->last_tx_bytes_hi = 0;
|
||||||
|
sta->last_tx_bytes_lo = 0;
|
||||||
|
hostapd_drv_sta_clear_stats(hapd, sta->addr);
|
||||||
|
|
||||||
|
if (!hapd->conf->radius->acct_server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sta->acct_interim_interval)
|
||||||
|
interval = sta->acct_interim_interval;
|
||||||
|
else
|
||||||
|
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
|
||||||
|
eloop_register_timeout(interval, 0, accounting_interim_update,
|
||||||
|
hapd, sta);
|
||||||
|
|
||||||
|
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
|
||||||
|
if (msg &&
|
||||||
|
radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
|
||||||
|
radius_msg_free(msg);
|
||||||
|
|
||||||
|
sta->acct_session_started = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void accounting_sta_report(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int stop)
|
||||||
|
{
|
||||||
|
struct radius_msg *msg;
|
||||||
|
int cause = sta->acct_terminate_cause;
|
||||||
|
struct hostap_sta_driver_data data;
|
||||||
|
struct os_reltime now_r, diff;
|
||||||
|
u64 bytes;
|
||||||
|
|
||||||
|
if (!hapd->conf->radius->acct_server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
msg = accounting_msg(hapd, sta,
|
||||||
|
stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
|
||||||
|
RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
|
||||||
|
if (!msg) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_get_reltime(&now_r);
|
||||||
|
os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
|
||||||
|
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
|
||||||
|
diff.sec)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
|
||||||
|
if (!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_ACCT_INPUT_PACKETS,
|
||||||
|
data.rx_packets)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
|
||||||
|
data.tx_packets)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (data.bytes_64bit)
|
||||||
|
bytes = data.rx_bytes;
|
||||||
|
else
|
||||||
|
bytes = ((u64) sta->last_rx_bytes_hi << 32) |
|
||||||
|
sta->last_rx_bytes_lo;
|
||||||
|
if (!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_ACCT_INPUT_OCTETS,
|
||||||
|
(u32) bytes)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
|
||||||
|
(u32) (bytes >> 32))) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (data.bytes_64bit)
|
||||||
|
bytes = data.tx_bytes;
|
||||||
|
else
|
||||||
|
bytes = ((u64) sta->last_tx_bytes_hi << 32) |
|
||||||
|
sta->last_tx_bytes_lo;
|
||||||
|
if (!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
|
||||||
|
(u32) bytes)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!radius_msg_add_attr_int32(msg,
|
||||||
|
RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
|
||||||
|
(u32) (bytes >> 32))) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eloop_terminated())
|
||||||
|
cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
|
||||||
|
|
||||||
|
if (stop && cause &&
|
||||||
|
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
|
||||||
|
cause)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius_client_send(hapd->radius, msg,
|
||||||
|
stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
|
||||||
|
sta->addr) < 0)
|
||||||
|
goto fail;
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
radius_msg_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accounting_sta_interim - Send a interim STA accounting report
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* @sta: The station
|
||||||
|
*/
|
||||||
|
static void accounting_sta_interim(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
if (sta->acct_session_started)
|
||||||
|
accounting_sta_report(hapd, sta, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accounting_sta_stop - Stop STA accounting
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* @sta: The station
|
||||||
|
*/
|
||||||
|
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
if (sta->acct_session_started) {
|
||||||
|
accounting_sta_report(hapd, sta, 1);
|
||||||
|
eloop_cancel_timeout(accounting_interim_update, hapd, sta);
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||||
|
HOSTAPD_LEVEL_INFO,
|
||||||
|
"stopped accounting session %016llX",
|
||||||
|
(unsigned long long) sta->acct_session_id);
|
||||||
|
sta->acct_session_started = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
return radius_gen_session_id((u8 *) &sta->acct_session_id,
|
||||||
|
sizeof(sta->acct_session_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accounting_receive - Process the RADIUS frames from Accounting Server
|
||||||
|
* @msg: RADIUS response message
|
||||||
|
* @req: RADIUS request message
|
||||||
|
* @shared_secret: RADIUS shared secret
|
||||||
|
* @shared_secret_len: Length of shared_secret in octets
|
||||||
|
* @data: Context data (struct hostapd_data *)
|
||||||
|
* Returns: Processing status
|
||||||
|
*/
|
||||||
|
static RadiusRxResult
|
||||||
|
accounting_receive(struct radius_msg *msg, struct radius_msg *req,
|
||||||
|
const u8 *shared_secret, size_t shared_secret_len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
|
||||||
|
wpa_printf(MSG_INFO, "Unknown RADIUS message code");
|
||||||
|
return RADIUS_RX_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
|
||||||
|
wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
|
||||||
|
return RADIUS_RX_INVALID_AUTHENTICATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RADIUS_RX_PROCESSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void accounting_report_state(struct hostapd_data *hapd, int on)
|
||||||
|
{
|
||||||
|
struct radius_msg *msg;
|
||||||
|
|
||||||
|
if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Inform RADIUS server that accounting will start/stop so that the
|
||||||
|
* server can close old accounting sessions. */
|
||||||
|
msg = accounting_msg(hapd, NULL,
|
||||||
|
on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
|
||||||
|
RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hapd->acct_session_id) {
|
||||||
|
char buf[20];
|
||||||
|
|
||||||
|
os_snprintf(buf, sizeof(buf), "%016llX",
|
||||||
|
(unsigned long long) hapd->acct_session_id);
|
||||||
|
if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
|
||||||
|
(u8 *) buf, os_strlen(buf)))
|
||||||
|
wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
|
||||||
|
radius_msg_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void accounting_interim_error_cb(const u8 *addr, void *ctx)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = ctx;
|
||||||
|
struct sta_info *sta;
|
||||||
|
unsigned int i, wait_time;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (!sta)
|
||||||
|
return;
|
||||||
|
sta->acct_interim_errors++;
|
||||||
|
if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Interim RADIUS accounting update failed for " MACSTR
|
||||||
|
" - too many errors, abandon this interim accounting update",
|
||||||
|
MAC2STR(addr));
|
||||||
|
sta->acct_interim_errors = 0;
|
||||||
|
/* Next update will be tried after normal update interval */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use a shorter update interval as an improved retransmission mechanism
|
||||||
|
* for failed interim accounting updates. This allows the statistics to
|
||||||
|
* be updated for each retransmission.
|
||||||
|
*
|
||||||
|
* RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
|
||||||
|
* Schedule the first retry attempt immediately and every following one
|
||||||
|
* with exponential backoff.
|
||||||
|
*/
|
||||||
|
if (sta->acct_interim_errors == 1) {
|
||||||
|
wait_time = 0;
|
||||||
|
} else {
|
||||||
|
wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
|
||||||
|
for (i = 1; i < sta->acct_interim_errors; i++)
|
||||||
|
wait_time *= 2;
|
||||||
|
}
|
||||||
|
res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
|
||||||
|
hapd, sta);
|
||||||
|
if (res == 1)
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Interim RADIUS accounting update failed for " MACSTR
|
||||||
|
" (error count: %u) - schedule next update in %u seconds",
|
||||||
|
MAC2STR(addr), sta->acct_interim_errors, wait_time);
|
||||||
|
else if (res == 0)
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Interim RADIUS accounting update failed for " MACSTR
|
||||||
|
" (error count: %u)", MAC2STR(addr),
|
||||||
|
sta->acct_interim_errors);
|
||||||
|
else
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Interim RADIUS accounting update failed for " MACSTR
|
||||||
|
" (error count: %u) - no timer found", MAC2STR(addr),
|
||||||
|
sta->acct_interim_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accounting_init: Initialize accounting
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int accounting_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
|
||||||
|
sizeof(hapd->acct_session_id)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (radius_client_register(hapd->radius, RADIUS_ACCT,
|
||||||
|
accounting_receive, hapd))
|
||||||
|
return -1;
|
||||||
|
radius_client_set_interim_error_cb(hapd->radius,
|
||||||
|
accounting_interim_error_cb, hapd);
|
||||||
|
|
||||||
|
accounting_report_state(hapd, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accounting_deinit: Deinitialize accounting
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
*/
|
||||||
|
void accounting_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
accounting_report_state(hapd, 0);
|
||||||
|
}
|
45
hostapd-2.9/src/ap/accounting.h
Normal file
45
hostapd-2.9/src/ap/accounting.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* hostapd / RADIUS Accounting
|
||||||
|
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACCOUNTING_H
|
||||||
|
#define ACCOUNTING_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_NO_ACCOUNTING
|
||||||
|
static inline int accounting_sta_get_id(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void accounting_sta_start(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void accounting_sta_stop(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int accounting_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void accounting_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#else /* CONFIG_NO_ACCOUNTING */
|
||||||
|
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
int accounting_init(struct hostapd_data *hapd);
|
||||||
|
void accounting_deinit(struct hostapd_data *hapd);
|
||||||
|
#endif /* CONFIG_NO_ACCOUNTING */
|
||||||
|
|
||||||
|
#endif /* ACCOUNTING_H */
|
986
hostapd-2.9/src/ap/acs.c
Normal file
986
hostapd-2.9/src/ap/acs.c
Normal file
|
@ -0,0 +1,986 @@
|
||||||
|
/*
|
||||||
|
* ACS - Automatic Channel Selection module
|
||||||
|
* Copyright (c) 2011, Atheros Communications
|
||||||
|
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/list.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/hw_features_common.h"
|
||||||
|
#include "common/wpa_ctrl.h"
|
||||||
|
#include "drivers/driver.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "hw_features.h"
|
||||||
|
#include "acs.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Automatic Channel Selection
|
||||||
|
* ===========================
|
||||||
|
*
|
||||||
|
* More info at
|
||||||
|
* ------------
|
||||||
|
* http://wireless.kernel.org/en/users/Documentation/acs
|
||||||
|
*
|
||||||
|
* How to use
|
||||||
|
* ----------
|
||||||
|
* - make sure you have CONFIG_ACS=y in hostapd's .config
|
||||||
|
* - use channel=0 or channel=acs to enable ACS
|
||||||
|
*
|
||||||
|
* How does it work
|
||||||
|
* ----------------
|
||||||
|
* 1. passive scans are used to collect survey data
|
||||||
|
* (it is assumed that scan trigger collection of survey data in driver)
|
||||||
|
* 2. interference factor is calculated for each channel
|
||||||
|
* 3. ideal channel is picked depending on channel width by using adjacent
|
||||||
|
* channel interference factors
|
||||||
|
*
|
||||||
|
* Known limitations
|
||||||
|
* -----------------
|
||||||
|
* - Current implementation depends heavily on the amount of time willing to
|
||||||
|
* spend gathering survey data during hostapd startup. Short traffic bursts
|
||||||
|
* may be missed and a suboptimal channel may be picked.
|
||||||
|
* - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
|
||||||
|
*
|
||||||
|
* Todo / Ideas
|
||||||
|
* ------------
|
||||||
|
* - implement other interference computation methods
|
||||||
|
* - BSS/RSSI based
|
||||||
|
* - spectral scan based
|
||||||
|
* (should be possibly to hook this up with current ACS scans)
|
||||||
|
* - add wpa_supplicant support (for P2P)
|
||||||
|
* - collect a histogram of interference over time allowing more educated
|
||||||
|
* guess about an ideal channel (perhaps CSA could be used to migrate AP to a
|
||||||
|
* new "better" channel while running)
|
||||||
|
* - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
|
||||||
|
* when choosing the ideal channel
|
||||||
|
*
|
||||||
|
* Survey interference factor implementation details
|
||||||
|
* -------------------------------------------------
|
||||||
|
* Generic interference_factor in struct hostapd_channel_data is used.
|
||||||
|
*
|
||||||
|
* The survey interference factor is defined as the ratio of the
|
||||||
|
* observed busy time over the time we spent on the channel,
|
||||||
|
* this value is then amplified by the observed noise floor on
|
||||||
|
* the channel in comparison to the lowest noise floor observed
|
||||||
|
* on the entire band.
|
||||||
|
*
|
||||||
|
* This corresponds to:
|
||||||
|
* ---
|
||||||
|
* (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* The coefficient of 2 reflects the way power in "far-field"
|
||||||
|
* radiation decreases as the square of distance from the antenna [1].
|
||||||
|
* What this does is it decreases the observed busy time ratio if the
|
||||||
|
* noise observed was low but increases it if the noise was high,
|
||||||
|
* proportionally to the way "far field" radiation changes over
|
||||||
|
* distance.
|
||||||
|
*
|
||||||
|
* If channel busy time is not available the fallback is to use channel RX time.
|
||||||
|
*
|
||||||
|
* Since noise floor is in dBm it is necessary to convert it into Watts so that
|
||||||
|
* combined channel interference (e.g., HT40, which uses two channels) can be
|
||||||
|
* calculated easily.
|
||||||
|
* ---
|
||||||
|
* (busy time - tx time) / (active time - tx time) *
|
||||||
|
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* However to account for cases where busy/rx time is 0 (channel load is then
|
||||||
|
* 0%) channel noise floor signal power is combined into the equation so a
|
||||||
|
* channel with lower noise floor is preferred. The equation becomes:
|
||||||
|
* ---
|
||||||
|
* 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
|
||||||
|
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* All this "interference factor" is purely subjective and only time
|
||||||
|
* will tell how usable this is. By using the minimum noise floor we
|
||||||
|
* remove any possible issues due to card calibration. The computation
|
||||||
|
* of the interference factor then is dependent on what the card itself
|
||||||
|
* picks up as the minimum noise, not an actual real possible card
|
||||||
|
* noise value.
|
||||||
|
*
|
||||||
|
* Total interference computation details
|
||||||
|
* --------------------------------------
|
||||||
|
* The above channel interference factor is calculated with no respect to
|
||||||
|
* target operational bandwidth.
|
||||||
|
*
|
||||||
|
* To find an ideal channel the above data is combined by taking into account
|
||||||
|
* the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
|
||||||
|
* overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
|
||||||
|
* on 5 GHz.
|
||||||
|
*
|
||||||
|
* Each valid and possible channel spec (i.e., channel + width) is taken and its
|
||||||
|
* interference factor is computed by summing up interferences of each channel
|
||||||
|
* it overlaps. The one with least total interference is picked up.
|
||||||
|
*
|
||||||
|
* Note: This implies base channel interference factor must be non-negative
|
||||||
|
* allowing easy summing up.
|
||||||
|
*
|
||||||
|
* Example ACS analysis printout
|
||||||
|
* -----------------------------
|
||||||
|
*
|
||||||
|
* ACS: Trying survey-based ACS
|
||||||
|
* ACS: Survey analysis for channel 1 (2412 MHz)
|
||||||
|
* ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
|
||||||
|
* ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
|
||||||
|
* ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
|
||||||
|
* ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
|
||||||
|
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
|
||||||
|
* ACS: * interference factor average: 0.0557166
|
||||||
|
* ACS: Survey analysis for channel 2 (2417 MHz)
|
||||||
|
* ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
|
||||||
|
* ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
|
||||||
|
* ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
|
||||||
|
* ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
|
||||||
|
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
|
||||||
|
* ACS: * interference factor average: 0.050832
|
||||||
|
* ACS: Survey analysis for channel 3 (2422 MHz)
|
||||||
|
* ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||||
|
* ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
|
||||||
|
* ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||||
|
* ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||||
|
* ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||||
|
* ACS: * interference factor average: 0.0148838
|
||||||
|
* ACS: Survey analysis for channel 4 (2427 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||||
|
* ACS: * interference factor average: 0.0160801
|
||||||
|
* ACS: Survey analysis for channel 5 (2432 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
|
||||||
|
* ACS: * interference factor average: 0.232244
|
||||||
|
* ACS: Survey analysis for channel 6 (2437 MHz)
|
||||||
|
* ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
|
||||||
|
* ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
|
||||||
|
* ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
|
||||||
|
* ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
|
||||||
|
* ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
|
||||||
|
* ACS: * interference factor average: 0.232298
|
||||||
|
* ACS: Survey analysis for channel 7 (2442 MHz)
|
||||||
|
* ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
|
||||||
|
* ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
|
||||||
|
* ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||||
|
* ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||||
|
* ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
|
||||||
|
* ACS: * interference factor average: 0.195031
|
||||||
|
* ACS: Survey analysis for channel 8 (2447 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
|
||||||
|
* ACS: * interference factor average: 0.0865885
|
||||||
|
* ACS: Survey analysis for channel 9 (2452 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||||
|
* ACS: * interference factor average: 0.00993022
|
||||||
|
* ACS: Survey analysis for channel 10 (2457 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||||
|
* ACS: * interference factor average: 0.0136033
|
||||||
|
* ACS: Survey analysis for channel 11 (2462 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
|
||||||
|
* ACS: * interference factor average: 0.0271605
|
||||||
|
* ACS: Survey analysis for channel 12 (2467 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
|
||||||
|
* ACS: * interference factor average: 0.0148992
|
||||||
|
* ACS: Survey analysis for channel 13 (2472 MHz)
|
||||||
|
* ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
|
||||||
|
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
|
||||||
|
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||||
|
* ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||||
|
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||||
|
* ACS: * interference factor average: 0.0260179
|
||||||
|
* ACS: Survey analysis for selected bandwidth 20MHz
|
||||||
|
* ACS: * channel 1: total interference = 0.121432
|
||||||
|
* ACS: * channel 2: total interference = 0.137512
|
||||||
|
* ACS: * channel 3: total interference = 0.369757
|
||||||
|
* ACS: * channel 4: total interference = 0.546338
|
||||||
|
* ACS: * channel 5: total interference = 0.690538
|
||||||
|
* ACS: * channel 6: total interference = 0.762242
|
||||||
|
* ACS: * channel 7: total interference = 0.756092
|
||||||
|
* ACS: * channel 8: total interference = 0.537451
|
||||||
|
* ACS: * channel 9: total interference = 0.332313
|
||||||
|
* ACS: * channel 10: total interference = 0.152182
|
||||||
|
* ACS: * channel 11: total interference = 0.0916111
|
||||||
|
* ACS: * channel 12: total interference = 0.0816809
|
||||||
|
* ACS: * channel 13: total interference = 0.0680776
|
||||||
|
* ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
|
||||||
|
*
|
||||||
|
* [1] http://en.wikipedia.org/wiki/Near_and_far_field
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_request_scan(struct hostapd_iface *iface);
|
||||||
|
static int acs_survey_is_sufficient(struct freq_survey *survey);
|
||||||
|
|
||||||
|
|
||||||
|
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
struct freq_survey *survey, *tmp;
|
||||||
|
|
||||||
|
if (dl_list_empty(&chan->survey_list))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dl_list_for_each_safe(survey, tmp, &chan->survey_list,
|
||||||
|
struct freq_survey, list) {
|
||||||
|
dl_list_del(&survey->list);
|
||||||
|
os_free(survey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void acs_cleanup(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||||
|
chan = &iface->current_mode->channels[i];
|
||||||
|
|
||||||
|
if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
|
||||||
|
acs_clean_chan_surveys(chan);
|
||||||
|
|
||||||
|
dl_list_init(&chan->survey_list);
|
||||||
|
chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
|
||||||
|
chan->min_nf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->chans_surveyed = 0;
|
||||||
|
iface->acs_num_completed_scans = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void acs_fail(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Failed to start");
|
||||||
|
acs_cleanup(iface);
|
||||||
|
hostapd_disable_iface(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static long double
|
||||||
|
acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
|
||||||
|
{
|
||||||
|
long double factor, busy, total;
|
||||||
|
|
||||||
|
if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
|
||||||
|
busy = survey->channel_time_busy;
|
||||||
|
else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
|
||||||
|
busy = survey->channel_time_rx;
|
||||||
|
else {
|
||||||
|
/* This shouldn't really happen as survey data is checked in
|
||||||
|
* acs_sanity_check() */
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Survey data missing");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
total = survey->channel_time;
|
||||||
|
|
||||||
|
if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
|
||||||
|
busy -= survey->channel_time_tx;
|
||||||
|
total -= survey->channel_time_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: figure out the best multiplier for noise floor base */
|
||||||
|
factor = pow(10, survey->nf / 5.0L) +
|
||||||
|
(total ? (busy / total) : 0) *
|
||||||
|
pow(2, pow(10, (long double) survey->nf / 10.0L) -
|
||||||
|
pow(10, (long double) min_nf / 10.0L));
|
||||||
|
|
||||||
|
return factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
acs_survey_chan_interference_factor(struct hostapd_iface *iface,
|
||||||
|
struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
struct freq_survey *survey;
|
||||||
|
unsigned int i = 0;
|
||||||
|
long double int_factor = 0;
|
||||||
|
unsigned count = 0;
|
||||||
|
|
||||||
|
if (dl_list_empty(&chan->survey_list) ||
|
||||||
|
(chan->flag & HOSTAPD_CHAN_DISABLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
chan->interference_factor = 0;
|
||||||
|
|
||||||
|
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (!acs_survey_is_sufficient(survey)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
int_factor = acs_survey_interference_factor(survey,
|
||||||
|
iface->lowest_nf);
|
||||||
|
chan->interference_factor += int_factor;
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
|
||||||
|
i, chan->min_nf, int_factor,
|
||||||
|
survey->nf, (unsigned long) survey->channel_time,
|
||||||
|
(unsigned long) survey->channel_time_busy,
|
||||||
|
(unsigned long) survey->channel_time_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
chan->interference_factor /= count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
|
||||||
|
157, 184, 192 };
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||||
|
if (chan->chan == allowed[i])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
const int allowed[] = { 36, 52, 100, 116, 132, 149 };
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||||
|
if (chan->chan == allowed[i])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
const int allowed[] = { 36, 100 };
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||||
|
if (chan->chan == allowed[i])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_survey_is_sufficient(struct freq_survey *survey)
|
||||||
|
{
|
||||||
|
if (!(survey->filled & SURVEY_HAS_NF)) {
|
||||||
|
wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
|
||||||
|
wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
|
||||||
|
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"ACS: Survey is missing RX and busy time (at least one is required)");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
struct freq_survey *survey;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||||
|
{
|
||||||
|
if (acs_survey_is_sufficient(survey)) {
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == -1)
|
||||||
|
ret = 1; /* no survey list entries */
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"ACS: Channel %d has insufficient survey data",
|
||||||
|
chan->chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
int valid = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||||
|
chan = &iface->current_mode->channels[i];
|
||||||
|
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||||
|
acs_survey_list_is_sufficient(chan))
|
||||||
|
valid++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need at least survey data for one channel */
|
||||||
|
return !!valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_usable_chan(struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
return !dl_list_empty(&chan->survey_list) &&
|
||||||
|
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||||
|
acs_survey_list_is_sufficient(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int is_in_chanlist(struct hostapd_iface *iface,
|
||||||
|
struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
if (!iface->conf->acs_ch_list.num)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void acs_survey_all_chans_intereference_factor(
|
||||||
|
struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||||
|
chan = &iface->current_mode->channels[i];
|
||||||
|
|
||||||
|
if (!acs_usable_chan(chan))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!is_in_chanlist(iface, chan))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
|
||||||
|
chan->chan, chan->freq);
|
||||||
|
|
||||||
|
acs_survey_chan_interference_factor(iface, chan);
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
|
||||||
|
chan->interference_factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
|
||||||
|
int freq)
|
||||||
|
{
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||||
|
chan = &iface->current_mode->channels[i];
|
||||||
|
|
||||||
|
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (chan->freq == freq)
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int is_24ghz_mode(enum hostapd_hw_mode mode)
|
||||||
|
{
|
||||||
|
return mode == HOSTAPD_MODE_IEEE80211B ||
|
||||||
|
mode == HOSTAPD_MODE_IEEE80211G;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int is_common_24ghz_chan(int chan)
|
||||||
|
{
|
||||||
|
return chan == 1 || chan == 6 || chan == 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ACS_ADJ_WEIGHT
|
||||||
|
#define ACS_ADJ_WEIGHT 0.85
|
||||||
|
#endif /* ACS_ADJ_WEIGHT */
|
||||||
|
|
||||||
|
#ifndef ACS_NEXT_ADJ_WEIGHT
|
||||||
|
#define ACS_NEXT_ADJ_WEIGHT 0.55
|
||||||
|
#endif /* ACS_NEXT_ADJ_WEIGHT */
|
||||||
|
|
||||||
|
#ifndef ACS_24GHZ_PREFER_1_6_11
|
||||||
|
/*
|
||||||
|
* Select commonly used channels 1, 6, 11 by default even if a neighboring
|
||||||
|
* channel has a smaller interference factor as long as it is not better by more
|
||||||
|
* than this multiplier.
|
||||||
|
*/
|
||||||
|
#define ACS_24GHZ_PREFER_1_6_11 0.8
|
||||||
|
#endif /* ACS_24GHZ_PREFER_1_6_11 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point it's assumed chan->interface_factor has been computed.
|
||||||
|
* This function should be reusable regardless of interference computation
|
||||||
|
* option (survey, BSS, spectral, ...). chan->interference factor must be
|
||||||
|
* summable (i.e., must be always greater than zero).
|
||||||
|
*/
|
||||||
|
static struct hostapd_channel_data *
|
||||||
|
acs_find_ideal_chan(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
|
||||||
|
*rand_chan = NULL;
|
||||||
|
long double factor, ideal_factor = 0;
|
||||||
|
int i, j;
|
||||||
|
int n_chans = 1;
|
||||||
|
u32 bw;
|
||||||
|
unsigned int k;
|
||||||
|
|
||||||
|
/* TODO: HT40- support */
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211n &&
|
||||||
|
iface->conf->secondary_channel == -1) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211n &&
|
||||||
|
iface->conf->secondary_channel)
|
||||||
|
n_chans = 2;
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
|
||||||
|
switch (hostapd_get_oper_chwidth(iface->conf)) {
|
||||||
|
case CHANWIDTH_80MHZ:
|
||||||
|
n_chans = 4;
|
||||||
|
break;
|
||||||
|
case CHANWIDTH_160MHZ:
|
||||||
|
n_chans = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bw = num_chan_to_bw(n_chans);
|
||||||
|
|
||||||
|
/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: Survey analysis for selected bandwidth %d MHz", bw);
|
||||||
|
|
||||||
|
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||||
|
double total_weight;
|
||||||
|
struct acs_bias *bias, tmp_bias;
|
||||||
|
|
||||||
|
chan = &iface->current_mode->channels[i];
|
||||||
|
|
||||||
|
/* Since in the current ACS implementation the first channel is
|
||||||
|
* always a primary channel, skip channels not available as
|
||||||
|
* primary until more sophisticated channel selection is
|
||||||
|
* implemented. */
|
||||||
|
if (!chan_pri_allowed(chan))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!is_in_chanlist(iface, chan))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!chan_bw_allowed(chan, bw, 1, 1)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: Channel %d: BW %u is not supported",
|
||||||
|
chan->chan, bw);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HT40 on 5 GHz has a limited set of primary channels as per
|
||||||
|
* 11n Annex J */
|
||||||
|
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
|
||||||
|
iface->conf->ieee80211n &&
|
||||||
|
iface->conf->secondary_channel &&
|
||||||
|
!acs_usable_ht40_chan(chan)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
|
||||||
|
chan->chan);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
|
||||||
|
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
|
||||||
|
if (hostapd_get_oper_chwidth(iface->conf) ==
|
||||||
|
CHANWIDTH_80MHZ &&
|
||||||
|
!acs_usable_vht80_chan(chan)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: Channel %d: not allowed as primary channel for VHT80",
|
||||||
|
chan->chan);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostapd_get_oper_chwidth(iface->conf) ==
|
||||||
|
CHANWIDTH_160MHZ &&
|
||||||
|
!acs_usable_vht160_chan(chan)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: Channel %d: not allowed as primary channel for VHT160",
|
||||||
|
chan->chan);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factor = 0;
|
||||||
|
if (acs_usable_chan(chan))
|
||||||
|
factor = chan->interference_factor;
|
||||||
|
total_weight = 1;
|
||||||
|
|
||||||
|
for (j = 1; j < n_chans; j++) {
|
||||||
|
adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
|
||||||
|
if (!adj_chan)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
|
||||||
|
chan->chan, adj_chan->chan, bw);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acs_usable_chan(adj_chan)) {
|
||||||
|
factor += adj_chan->interference_factor;
|
||||||
|
total_weight += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j != n_chans) {
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
|
||||||
|
chan->chan);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
|
||||||
|
* channel interference factor. */
|
||||||
|
if (is_24ghz_mode(iface->current_mode->mode)) {
|
||||||
|
for (j = 0; j < n_chans; j++) {
|
||||||
|
adj_chan = acs_find_chan(iface, chan->freq +
|
||||||
|
(j * 20) - 5);
|
||||||
|
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||||
|
factor += ACS_ADJ_WEIGHT *
|
||||||
|
adj_chan->interference_factor;
|
||||||
|
total_weight += ACS_ADJ_WEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
adj_chan = acs_find_chan(iface, chan->freq +
|
||||||
|
(j * 20) - 10);
|
||||||
|
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||||
|
factor += ACS_NEXT_ADJ_WEIGHT *
|
||||||
|
adj_chan->interference_factor;
|
||||||
|
total_weight += ACS_NEXT_ADJ_WEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
adj_chan = acs_find_chan(iface, chan->freq +
|
||||||
|
(j * 20) + 5);
|
||||||
|
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||||
|
factor += ACS_ADJ_WEIGHT *
|
||||||
|
adj_chan->interference_factor;
|
||||||
|
total_weight += ACS_ADJ_WEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
adj_chan = acs_find_chan(iface, chan->freq +
|
||||||
|
(j * 20) + 10);
|
||||||
|
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||||
|
factor += ACS_NEXT_ADJ_WEIGHT *
|
||||||
|
adj_chan->interference_factor;
|
||||||
|
total_weight += ACS_NEXT_ADJ_WEIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factor /= total_weight;
|
||||||
|
|
||||||
|
bias = NULL;
|
||||||
|
if (iface->conf->acs_chan_bias) {
|
||||||
|
for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
|
||||||
|
bias = &iface->conf->acs_chan_bias[k];
|
||||||
|
if (bias->channel == chan->chan)
|
||||||
|
break;
|
||||||
|
bias = NULL;
|
||||||
|
}
|
||||||
|
} else if (is_24ghz_mode(iface->current_mode->mode) &&
|
||||||
|
is_common_24ghz_chan(chan->chan)) {
|
||||||
|
tmp_bias.channel = chan->chan;
|
||||||
|
tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
|
||||||
|
bias = &tmp_bias;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bias) {
|
||||||
|
factor *= bias->bias;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: * channel %d: total interference = %Lg (%f bias)",
|
||||||
|
chan->chan, factor, bias->bias);
|
||||||
|
} else {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"ACS: * channel %d: total interference = %Lg",
|
||||||
|
chan->chan, factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acs_usable_chan(chan) &&
|
||||||
|
(!ideal_chan || factor < ideal_factor)) {
|
||||||
|
ideal_factor = factor;
|
||||||
|
ideal_chan = chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This channel would at least be usable */
|
||||||
|
if (!rand_chan)
|
||||||
|
rand_chan = chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ideal_chan) {
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
|
||||||
|
ideal_chan->chan, ideal_chan->freq, ideal_factor);
|
||||||
|
return ideal_chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rand_chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void acs_adjust_center_freq(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
|
||||||
|
|
||||||
|
switch (hostapd_get_oper_chwidth(iface->conf)) {
|
||||||
|
case CHANWIDTH_USE_HT:
|
||||||
|
offset = 2 * iface->conf->secondary_channel;
|
||||||
|
break;
|
||||||
|
case CHANWIDTH_80MHZ:
|
||||||
|
offset = 6;
|
||||||
|
break;
|
||||||
|
case CHANWIDTH_160MHZ:
|
||||||
|
offset = 14;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* TODO: How can this be calculated? Adjust
|
||||||
|
* acs_find_ideal_chan() */
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"ACS: Only VHT20/40/80/160 is supported now");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
|
||||||
|
iface->conf->channel + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_study_survey_based(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
|
||||||
|
|
||||||
|
if (!iface->chans_surveyed) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!acs_surveys_are_sufficient(iface)) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
acs_survey_all_chans_intereference_factor(iface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_study_options(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
if (acs_study_survey_based(iface) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* TODO: If no surveys are available/sufficient this is a good
|
||||||
|
* place to fallback to BSS-based ACS */
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void acs_study(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct hostapd_channel_data *ideal_chan;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = acs_study_options(iface);
|
||||||
|
if (err < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: All study options have failed");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ideal_chan = acs_find_ideal_chan(iface);
|
||||||
|
if (!ideal_chan) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
|
||||||
|
err = -1;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->conf->channel = ideal_chan->chan;
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
|
||||||
|
acs_adjust_center_freq(iface);
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
fail:
|
||||||
|
/*
|
||||||
|
* hostapd_setup_interface_complete() will return -1 on failure,
|
||||||
|
* 0 on success and 0 is HOSTAPD_CHAN_VALID :)
|
||||||
|
*/
|
||||||
|
if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
|
||||||
|
acs_cleanup(iface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This can possibly happen if channel parameters (secondary
|
||||||
|
* channel, center frequencies) are misconfigured */
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
|
||||||
|
acs_fail(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void acs_scan_complete(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
iface->scan_cb = NULL;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
|
||||||
|
iface->conf->acs_num_scans);
|
||||||
|
|
||||||
|
err = hostapd_drv_get_survey(iface->bss[0], 0);
|
||||||
|
if (err) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
|
||||||
|
err = acs_request_scan(iface);
|
||||||
|
if (err) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
acs_study(iface);
|
||||||
|
return;
|
||||||
|
fail:
|
||||||
|
hostapd_acs_completed(iface, 1);
|
||||||
|
acs_fail(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int acs_request_scan(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct wpa_driver_scan_params params;
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
int i, *freq;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
params.freqs = os_calloc(iface->current_mode->num_channels + 1,
|
||||||
|
sizeof(params.freqs[0]));
|
||||||
|
if (params.freqs == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
freq = params.freqs;
|
||||||
|
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||||
|
chan = &iface->current_mode->channels[i];
|
||||||
|
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!is_in_chanlist(iface, chan))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*freq++ = chan->freq;
|
||||||
|
}
|
||||||
|
*freq = 0;
|
||||||
|
|
||||||
|
iface->scan_cb = acs_scan_complete;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
|
||||||
|
iface->acs_num_completed_scans + 1,
|
||||||
|
iface->conf->acs_num_scans);
|
||||||
|
|
||||||
|
if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
|
||||||
|
acs_cleanup(iface);
|
||||||
|
os_free(params.freqs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_free(params.freqs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
|
||||||
|
|
||||||
|
if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
|
||||||
|
wpa_printf(MSG_INFO, "ACS: Offloading to driver");
|
||||||
|
if (hostapd_drv_do_acs(iface->bss[0]))
|
||||||
|
return HOSTAPD_CHAN_INVALID;
|
||||||
|
return HOSTAPD_CHAN_ACS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iface->current_mode)
|
||||||
|
return HOSTAPD_CHAN_INVALID;
|
||||||
|
|
||||||
|
acs_cleanup(iface);
|
||||||
|
|
||||||
|
if (acs_request_scan(iface) < 0)
|
||||||
|
return HOSTAPD_CHAN_INVALID;
|
||||||
|
|
||||||
|
hostapd_set_state(iface, HAPD_IFACE_ACS);
|
||||||
|
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
|
||||||
|
|
||||||
|
return HOSTAPD_CHAN_ACS;
|
||||||
|
}
|
32
hostapd-2.9/src/ap/acs.h
Normal file
32
hostapd-2.9/src/ap/acs.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* ACS - Automatic Channel Selection module
|
||||||
|
* Copyright (c) 2011, Atheros Communications
|
||||||
|
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACS_H
|
||||||
|
#define ACS_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACS
|
||||||
|
|
||||||
|
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
|
||||||
|
void acs_cleanup(struct hostapd_iface *iface);
|
||||||
|
|
||||||
|
#else /* CONFIG_ACS */
|
||||||
|
|
||||||
|
static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
|
||||||
|
return HOSTAPD_CHAN_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void acs_cleanup(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_ACS */
|
||||||
|
|
||||||
|
#endif /* ACS_H */
|
269
hostapd-2.9/src/ap/airtime_policy.c
Normal file
269
hostapd-2.9/src/ap/airtime_policy.c
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* Airtime policy configuration
|
||||||
|
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "airtime_policy.h"
|
||||||
|
|
||||||
|
/* Idea:
|
||||||
|
* Two modes of airtime enforcement:
|
||||||
|
* 1. Static weights: specify weights per MAC address with a per-BSS default
|
||||||
|
* 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
|
||||||
|
* enforce relative total shares between BSSes.
|
||||||
|
*
|
||||||
|
* - Periodic per-station callback to update queue status.
|
||||||
|
*
|
||||||
|
* Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
|
||||||
|
* keep them updated in sta_info.
|
||||||
|
*
|
||||||
|
* - Separate periodic per-bss (or per-iface?) callback to update weights.
|
||||||
|
*
|
||||||
|
* Just need to loop through all interfaces, count sum the active stations (or
|
||||||
|
* should the per-STA callback just adjust that for the BSS?) and calculate new
|
||||||
|
* weights.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
|
||||||
|
unsigned int *sec,
|
||||||
|
unsigned int *usec)
|
||||||
|
{
|
||||||
|
unsigned int update_int = iface->conf->airtime_update_interval;
|
||||||
|
|
||||||
|
if (!update_int) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Airtime policy: Invalid airtime policy update interval %u",
|
||||||
|
update_int);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sec = update_int / 1000;
|
||||||
|
*usec = (update_int % 1000) * 1000;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void set_new_backlog_time(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
struct os_reltime *now)
|
||||||
|
{
|
||||||
|
sta->backlogged_until = *now;
|
||||||
|
sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
|
||||||
|
AIRTIME_BACKLOG_EXPIRY_FACTOR;
|
||||||
|
while (sta->backlogged_until.usec >= 1000000) {
|
||||||
|
sta->backlogged_until.sec++;
|
||||||
|
sta->backlogged_until.usec -= 1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void count_backlogged_sta(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct sta_info *sta;
|
||||||
|
struct hostap_sta_driver_data data = {};
|
||||||
|
unsigned int num_backlogged = 0;
|
||||||
|
struct os_reltime now;
|
||||||
|
|
||||||
|
os_get_reltime(&now);
|
||||||
|
|
||||||
|
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||||
|
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (data.backlog_bytes > 0)
|
||||||
|
set_new_backlog_time(hapd, sta, &now);
|
||||||
|
if (os_reltime_before(&now, &sta->backlogged_until))
|
||||||
|
num_backlogged++;
|
||||||
|
}
|
||||||
|
hapd->num_backlogged_sta = num_backlogged;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int sta_set_airtime_weight(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
unsigned int weight)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (weight != sta->airtime_weight &&
|
||||||
|
(ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
sta->airtime_weight = weight;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
|
||||||
|
{
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
for (sta = hapd->sta_list; sta; sta = sta->next)
|
||||||
|
sta_set_airtime_weight(hapd, sta, weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned int get_airtime_quantum(unsigned int max_wt)
|
||||||
|
{
|
||||||
|
unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
|
||||||
|
|
||||||
|
if (quantum < AIRTIME_QUANTUM_MIN)
|
||||||
|
quantum = AIRTIME_QUANTUM_MIN;
|
||||||
|
else if (quantum > AIRTIME_QUANTUM_MAX)
|
||||||
|
quantum = AIRTIME_QUANTUM_MAX;
|
||||||
|
|
||||||
|
return quantum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void update_airtime_weights(void *eloop_data, void *user_data)
|
||||||
|
{
|
||||||
|
struct hostapd_iface *iface = eloop_data;
|
||||||
|
struct hostapd_data *bss;
|
||||||
|
unsigned int sec, usec;
|
||||||
|
unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
|
||||||
|
wt_sum = 0;
|
||||||
|
unsigned int quantum;
|
||||||
|
Boolean all_div_min = TRUE;
|
||||||
|
Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
|
||||||
|
int wt, num_bss = 0, max_wt = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->num_bss; i++) {
|
||||||
|
bss = iface->bss[i];
|
||||||
|
if (!bss->started || !bss->conf->airtime_weight)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
count_backlogged_sta(bss);
|
||||||
|
if (!bss->num_backlogged_sta)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
|
||||||
|
num_sta_min = bss->num_backlogged_sta;
|
||||||
|
|
||||||
|
num_sta_prod *= bss->num_backlogged_sta;
|
||||||
|
num_sta_sum += bss->num_backlogged_sta;
|
||||||
|
wt_sum += bss->conf->airtime_weight;
|
||||||
|
num_bss++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_sta_min) {
|
||||||
|
for (i = 0; i < iface->num_bss; i++) {
|
||||||
|
bss = iface->bss[i];
|
||||||
|
if (!bss->started || !bss->conf->airtime_weight)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Check if we can divide all sta numbers by the
|
||||||
|
* smallest number to keep weights as small as possible.
|
||||||
|
* This is a lazy way to avoid having to factor
|
||||||
|
* integers. */
|
||||||
|
if (bss->num_backlogged_sta &&
|
||||||
|
bss->num_backlogged_sta % num_sta_min > 0)
|
||||||
|
all_div_min = FALSE;
|
||||||
|
|
||||||
|
/* If we're in LIMIT mode, we only apply the weight
|
||||||
|
* scaling when the BSS(es) marked as limited would a
|
||||||
|
* larger share than the relative BSS weights indicates
|
||||||
|
* it should. */
|
||||||
|
if (!apply_limit && bss->conf->airtime_limit) {
|
||||||
|
if (bss->num_backlogged_sta * wt_sum >
|
||||||
|
bss->conf->airtime_weight * num_sta_sum)
|
||||||
|
apply_limit = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (all_div_min)
|
||||||
|
num_sta_prod /= num_sta_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < iface->num_bss; i++) {
|
||||||
|
bss = iface->bss[i];
|
||||||
|
if (!bss->started || !bss->conf->airtime_weight)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* We only set the calculated weight if the BSS has active
|
||||||
|
* stations and there are other active interfaces as well -
|
||||||
|
* otherwise we just set a unit weight. This ensures that
|
||||||
|
* the weights are set reasonably when stations transition from
|
||||||
|
* inactive to active. */
|
||||||
|
if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
|
||||||
|
wt = bss->conf->airtime_weight * num_sta_prod /
|
||||||
|
bss->num_backlogged_sta;
|
||||||
|
else
|
||||||
|
wt = 1;
|
||||||
|
|
||||||
|
bss->airtime_weight = wt;
|
||||||
|
if (wt > max_wt)
|
||||||
|
max_wt = wt;
|
||||||
|
}
|
||||||
|
|
||||||
|
quantum = get_airtime_quantum(max_wt);
|
||||||
|
|
||||||
|
for (i = 0; i < iface->num_bss; i++) {
|
||||||
|
bss = iface->bss[i];
|
||||||
|
if (!bss->started || !bss->conf->airtime_weight)
|
||||||
|
continue;
|
||||||
|
set_sta_weights(bss, bss->airtime_weight * quantum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
eloop_register_timeout(sec, usec, update_airtime_weights, iface,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
|
||||||
|
{
|
||||||
|
struct airtime_sta_weight *wt;
|
||||||
|
|
||||||
|
wt = hapd->conf->airtime_weight_list;
|
||||||
|
while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
|
||||||
|
wt = wt->next;
|
||||||
|
|
||||||
|
return wt ? wt->weight : hapd->conf->airtime_weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
unsigned int weight;
|
||||||
|
|
||||||
|
if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
|
||||||
|
weight = get_weight_for_sta(hapd, sta->addr);
|
||||||
|
if (weight)
|
||||||
|
return sta_set_airtime_weight(hapd, sta, weight);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int airtime_policy_update_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
unsigned int sec, usec;
|
||||||
|
|
||||||
|
if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void airtime_policy_update_deinit(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
eloop_cancel_timeout(update_airtime_weights, iface, NULL);
|
||||||
|
}
|
48
hostapd-2.9/src/ap/airtime_policy.h
Normal file
48
hostapd-2.9/src/ap/airtime_policy.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Airtime policy configuration
|
||||||
|
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AIRTIME_POLICY_H
|
||||||
|
#define AIRTIME_POLICY_H
|
||||||
|
|
||||||
|
struct hostapd_iface;
|
||||||
|
|
||||||
|
#ifdef CONFIG_AIRTIME_POLICY
|
||||||
|
|
||||||
|
#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
|
||||||
|
#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
|
||||||
|
|
||||||
|
/* scale quantum so this becomes the effective quantum after applying the max
|
||||||
|
* weight, but never go below min or above max */
|
||||||
|
#define AIRTIME_QUANTUM_MIN 8 /* usec */
|
||||||
|
#define AIRTIME_QUANTUM_MAX 256 /* usec */
|
||||||
|
#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
|
||||||
|
|
||||||
|
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
int airtime_policy_update_init(struct hostapd_iface *iface);
|
||||||
|
void airtime_policy_update_deinit(struct hostapd_iface *iface);
|
||||||
|
|
||||||
|
#else /* CONFIG_AIRTIME_POLICY */
|
||||||
|
|
||||||
|
static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int airtime_policy_update_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_AIRTIME_POLICY */
|
||||||
|
|
||||||
|
#endif /* AIRTIME_POLICY_H */
|
1379
hostapd-2.9/src/ap/ap_config.c
Normal file
1379
hostapd-2.9/src/ap/ap_config.c
Normal file
File diff suppressed because it is too large
Load diff
1104
hostapd-2.9/src/ap/ap_config.h
Normal file
1104
hostapd-2.9/src/ap/ap_config.h
Normal file
File diff suppressed because it is too large
Load diff
970
hostapd-2.9/src/ap/ap_drv_ops.c
Normal file
970
hostapd-2.9/src/ap/ap_drv_ops.c
Normal file
|
@ -0,0 +1,970 @@
|
||||||
|
/*
|
||||||
|
* hostapd - Driver operations
|
||||||
|
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/hw_features_common.h"
|
||||||
|
#include "wps/wps.h"
|
||||||
|
#include "p2p/p2p.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "p2p_hostapd.h"
|
||||||
|
#include "hs20.h"
|
||||||
|
#include "wpa_auth.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
|
||||||
|
|
||||||
|
u32 hostapd_sta_flags_to_drv(u32 flags)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
if (flags & WLAN_STA_AUTHORIZED)
|
||||||
|
res |= WPA_STA_AUTHORIZED;
|
||||||
|
if (flags & WLAN_STA_WMM)
|
||||||
|
res |= WPA_STA_WMM;
|
||||||
|
if (flags & WLAN_STA_SHORT_PREAMBLE)
|
||||||
|
res |= WPA_STA_SHORT_PREAMBLE;
|
||||||
|
if (flags & WLAN_STA_MFP)
|
||||||
|
res |= WPA_STA_MFP;
|
||||||
|
if (flags & WLAN_STA_AUTH)
|
||||||
|
res |= WPA_STA_AUTHENTICATED;
|
||||||
|
if (flags & WLAN_STA_ASSOC)
|
||||||
|
res |= WPA_STA_ASSOCIATED;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
|
||||||
|
{
|
||||||
|
if (!src)
|
||||||
|
return 0;
|
||||||
|
if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
|
||||||
|
return -1;
|
||||||
|
wpabuf_put_buf(*dst, src);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
|
||||||
|
{
|
||||||
|
if (!data || !len)
|
||||||
|
return 0;
|
||||||
|
if (wpabuf_resize(dst, len) != 0)
|
||||||
|
return -1;
|
||||||
|
wpabuf_put_data(*dst, data, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
|
||||||
|
struct wpabuf **beacon_ret,
|
||||||
|
struct wpabuf **proberesp_ret,
|
||||||
|
struct wpabuf **assocresp_ret)
|
||||||
|
{
|
||||||
|
struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
|
||||||
|
u8 buf[200], *pos;
|
||||||
|
|
||||||
|
*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
pos = hostapd_eid_time_adv(hapd, pos);
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
pos = hostapd_eid_time_zone(hapd, pos);
|
||||||
|
if (add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
pos = hostapd_eid_ext_capab(hapd, pos);
|
||||||
|
if (add_buf_data(&assocresp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
pos = hostapd_eid_interworking(hapd, pos);
|
||||||
|
pos = hostapd_eid_adv_proto(hapd, pos);
|
||||||
|
pos = hostapd_eid_roaming_consortium(hapd, pos);
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
#ifdef CONFIG_FST
|
||||||
|
if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
|
||||||
|
add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
|
||||||
|
add_buf(&assocresp, hapd->iface->fst_ies) < 0)
|
||||||
|
goto fail;
|
||||||
|
#endif /* CONFIG_FST */
|
||||||
|
|
||||||
|
#ifdef CONFIG_FILS
|
||||||
|
pos = hostapd_eid_fils_indic(hapd, buf, 0);
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
|
if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
|
||||||
|
add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P
|
||||||
|
if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
|
||||||
|
add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
|
||||||
|
goto fail;
|
||||||
|
#endif /* CONFIG_P2P */
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P_MANAGER
|
||||||
|
if (hapd->conf->p2p & P2P_MANAGE) {
|
||||||
|
if (wpabuf_resize(&beacon, 100) == 0) {
|
||||||
|
u8 *start, *p;
|
||||||
|
start = wpabuf_put(beacon, 0);
|
||||||
|
p = hostapd_eid_p2p_manage(hapd, start);
|
||||||
|
wpabuf_put(beacon, p - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wpabuf_resize(&proberesp, 100) == 0) {
|
||||||
|
u8 *start, *p;
|
||||||
|
start = wpabuf_put(proberesp, 0);
|
||||||
|
p = hostapd_eid_p2p_manage(hapd, start);
|
||||||
|
wpabuf_put(proberesp, p - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_P2P_MANAGER */
|
||||||
|
|
||||||
|
#ifdef CONFIG_WPS
|
||||||
|
if (hapd->conf->wps_state) {
|
||||||
|
struct wpabuf *a = wps_build_assoc_resp_ie();
|
||||||
|
add_buf(&assocresp, a);
|
||||||
|
wpabuf_free(a);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_WPS */
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P_MANAGER
|
||||||
|
if (hapd->conf->p2p & P2P_MANAGE) {
|
||||||
|
if (wpabuf_resize(&assocresp, 100) == 0) {
|
||||||
|
u8 *start, *p;
|
||||||
|
start = wpabuf_put(assocresp, 0);
|
||||||
|
p = hostapd_eid_p2p_manage(hapd, start);
|
||||||
|
wpabuf_put(assocresp, p - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_P2P_MANAGER */
|
||||||
|
|
||||||
|
#ifdef CONFIG_WIFI_DISPLAY
|
||||||
|
if (hapd->p2p_group) {
|
||||||
|
struct wpabuf *a;
|
||||||
|
a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
|
||||||
|
add_buf(&assocresp, a);
|
||||||
|
wpabuf_free(a);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_WIFI_DISPLAY */
|
||||||
|
|
||||||
|
#ifdef CONFIG_HS20
|
||||||
|
pos = hostapd_eid_hs20_indication(hapd, buf);
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
pos = hostapd_eid_osen(hapd, buf);
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
#endif /* CONFIG_HS20 */
|
||||||
|
|
||||||
|
#ifdef CONFIG_MBO
|
||||||
|
if (hapd->conf->mbo_enabled ||
|
||||||
|
OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
|
||||||
|
pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&proberesp, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&assocresp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_MBO */
|
||||||
|
|
||||||
|
#ifdef CONFIG_OWE
|
||||||
|
pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
|
||||||
|
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||||
|
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||||
|
goto fail;
|
||||||
|
#endif /* CONFIG_OWE */
|
||||||
|
|
||||||
|
add_buf(&beacon, hapd->conf->vendor_elements);
|
||||||
|
add_buf(&proberesp, hapd->conf->vendor_elements);
|
||||||
|
add_buf(&assocresp, hapd->conf->assocresp_elements);
|
||||||
|
|
||||||
|
*beacon_ret = beacon;
|
||||||
|
*proberesp_ret = proberesp;
|
||||||
|
*assocresp_ret = assocresp;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
wpabuf_free(beacon);
|
||||||
|
wpabuf_free(proberesp);
|
||||||
|
wpabuf_free(assocresp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
|
||||||
|
struct wpabuf *beacon,
|
||||||
|
struct wpabuf *proberesp,
|
||||||
|
struct wpabuf *assocresp)
|
||||||
|
{
|
||||||
|
wpabuf_free(beacon);
|
||||||
|
wpabuf_free(proberesp);
|
||||||
|
wpabuf_free(assocresp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct wpabuf *beacon, *proberesp, *assocresp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
|
||||||
|
0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
|
||||||
|
assocresp);
|
||||||
|
|
||||||
|
hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_authorized(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int authorized)
|
||||||
|
{
|
||||||
|
if (authorized) {
|
||||||
|
return hostapd_sta_set_flags(hapd, sta->addr,
|
||||||
|
hostapd_sta_flags_to_drv(
|
||||||
|
sta->flags),
|
||||||
|
WPA_STA_AUTHORIZED, ~0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostapd_sta_set_flags(hapd, sta->addr,
|
||||||
|
hostapd_sta_flags_to_drv(sta->flags),
|
||||||
|
0, ~WPA_STA_AUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
int set_flags, total_flags, flags_and, flags_or;
|
||||||
|
total_flags = hostapd_sta_flags_to_drv(sta->flags);
|
||||||
|
set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
|
||||||
|
if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
|
||||||
|
sta->auth_alg == WLAN_AUTH_FT) &&
|
||||||
|
sta->flags & WLAN_STA_AUTHORIZED)
|
||||||
|
set_flags |= WPA_STA_AUTHORIZED;
|
||||||
|
flags_or = total_flags & set_flags;
|
||||||
|
flags_and = total_flags | ~set_flags;
|
||||||
|
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
|
||||||
|
flags_or, flags_and);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
|
||||||
|
int enabled)
|
||||||
|
{
|
||||||
|
struct wpa_bss_params params;
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
params.ifname = ifname;
|
||||||
|
params.enabled = enabled;
|
||||||
|
if (enabled) {
|
||||||
|
params.wpa = hapd->conf->wpa;
|
||||||
|
params.ieee802_1x = hapd->conf->ieee802_1x;
|
||||||
|
params.wpa_group = hapd->conf->wpa_group;
|
||||||
|
if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
|
||||||
|
(WPA_PROTO_WPA | WPA_PROTO_RSN))
|
||||||
|
params.wpa_pairwise = hapd->conf->wpa_pairwise |
|
||||||
|
hapd->conf->rsn_pairwise;
|
||||||
|
else if (hapd->conf->wpa & WPA_PROTO_RSN)
|
||||||
|
params.wpa_pairwise = hapd->conf->rsn_pairwise;
|
||||||
|
else if (hapd->conf->wpa & WPA_PROTO_WPA)
|
||||||
|
params.wpa_pairwise = hapd->conf->wpa_pairwise;
|
||||||
|
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
|
||||||
|
params.rsn_preauth = hapd->conf->rsn_preauth;
|
||||||
|
#ifdef CONFIG_IEEE80211W
|
||||||
|
params.ieee80211w = hapd->conf->ieee80211w;
|
||||||
|
#endif /* CONFIG_IEEE80211W */
|
||||||
|
}
|
||||||
|
return hostapd_set_ieee8021x(hapd, ¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
|
||||||
|
{
|
||||||
|
char force_ifname[IFNAMSIZ];
|
||||||
|
u8 if_addr[ETH_ALEN];
|
||||||
|
return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
|
||||||
|
NULL, NULL, force_ifname, if_addr, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
|
||||||
|
{
|
||||||
|
return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
|
||||||
|
const u8 *addr, int aid, int val)
|
||||||
|
{
|
||||||
|
const char *bridge = NULL;
|
||||||
|
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
|
||||||
|
return -1;
|
||||||
|
if (hapd->conf->wds_bridge[0])
|
||||||
|
bridge = hapd->conf->wds_bridge;
|
||||||
|
else if (hapd->conf->bridge[0])
|
||||||
|
bridge = hapd->conf->bridge;
|
||||||
|
return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
|
||||||
|
bridge, ifname_wds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u16 auth_alg)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u16 seq, u16 status, const u8 *ie, size_t len)
|
||||||
|
{
|
||||||
|
struct wpa_driver_sta_auth_params params;
|
||||||
|
#ifdef CONFIG_FILS
|
||||||
|
struct sta_info *sta;
|
||||||
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
|
if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
|
||||||
|
#ifdef CONFIG_FILS
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (!sta) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Station " MACSTR
|
||||||
|
" not found for sta_auth processing",
|
||||||
|
MAC2STR(addr));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
|
||||||
|
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
|
||||||
|
sta->auth_alg == WLAN_AUTH_FILS_PK) {
|
||||||
|
params.fils_auth = 1;
|
||||||
|
wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
|
||||||
|
params.fils_snonce,
|
||||||
|
params.fils_kek,
|
||||||
|
¶ms.fils_kek_len);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
|
params.own_addr = hapd->own_addr;
|
||||||
|
params.addr = addr;
|
||||||
|
params.seq = seq;
|
||||||
|
params.status = status;
|
||||||
|
params.ie = ie;
|
||||||
|
params.len = len;
|
||||||
|
|
||||||
|
return hapd->driver->sta_auth(hapd->drv_priv, ¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
int reassoc, u16 status, const u8 *ie, size_t len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
|
||||||
|
reassoc, status, ie, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_sta_add(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, u16 aid, u16 capability,
|
||||||
|
const u8 *supp_rates, size_t supp_rates_len,
|
||||||
|
u16 listen_interval,
|
||||||
|
const struct ieee80211_ht_capabilities *ht_capab,
|
||||||
|
const struct ieee80211_vht_capabilities *vht_capab,
|
||||||
|
const struct ieee80211_he_capabilities *he_capab,
|
||||||
|
size_t he_capab_len,
|
||||||
|
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||||
|
int set)
|
||||||
|
{
|
||||||
|
struct hostapd_sta_add_params params;
|
||||||
|
|
||||||
|
if (hapd->driver == NULL)
|
||||||
|
return 0;
|
||||||
|
if (hapd->driver->sta_add == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
params.addr = addr;
|
||||||
|
params.aid = aid;
|
||||||
|
params.capability = capability;
|
||||||
|
params.supp_rates = supp_rates;
|
||||||
|
params.supp_rates_len = supp_rates_len;
|
||||||
|
params.listen_interval = listen_interval;
|
||||||
|
params.ht_capabilities = ht_capab;
|
||||||
|
params.vht_capabilities = vht_capab;
|
||||||
|
params.he_capab = he_capab;
|
||||||
|
params.he_capab_len = he_capab_len;
|
||||||
|
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
|
||||||
|
params.vht_opmode = vht_opmode;
|
||||||
|
params.flags = hostapd_sta_flags_to_drv(flags);
|
||||||
|
params.qosinfo = qosinfo;
|
||||||
|
params.support_p2p_ps = supp_p2p_ps;
|
||||||
|
params.set = set;
|
||||||
|
return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u8 *tspec_ie, size_t tspec_ielen)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
|
||||||
|
tspec_ielen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_privacy(hapd->drv_priv, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
|
||||||
|
size_t elem_len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||||
|
const char *ifname, const u8 *addr, void *bss_ctx,
|
||||||
|
void **drv_priv, char *force_ifname, u8 *if_addr,
|
||||||
|
const char *bridge, int use_existing)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->if_add == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
|
||||||
|
bss_ctx, drv_priv, force_ifname, if_addr,
|
||||||
|
bridge, use_existing, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||||
|
const char *ifname)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||||
|
hapd->driver->if_remove == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
|
||||||
|
struct wpa_bss_params *params)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int idx, u8 *seq)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
|
||||||
|
seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_flush(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->flush == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->flush(hapd->drv_priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
|
||||||
|
int freq, int channel, int ht_enabled, int vht_enabled,
|
||||||
|
int he_enabled,
|
||||||
|
int sec_channel_offset, int oper_chwidth,
|
||||||
|
int center_segment0, int center_segment1)
|
||||||
|
{
|
||||||
|
struct hostapd_freq_params data;
|
||||||
|
struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
|
||||||
|
|
||||||
|
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
|
||||||
|
vht_enabled, he_enabled, sec_channel_offset,
|
||||||
|
oper_chwidth,
|
||||||
|
center_segment0, center_segment1,
|
||||||
|
cmode ? cmode->vht_capab : 0,
|
||||||
|
cmode ?
|
||||||
|
&cmode->he_capab[IEEE80211_MODE_AP] : NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (hapd->driver == NULL)
|
||||||
|
return 0;
|
||||||
|
if (hapd->driver->set_freq == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_freq(hapd->drv_priv, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hostapd_set_rts(struct hostapd_data *hapd, int rts)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_rts(hapd->drv_priv, rts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_frag(struct hostapd_data *hapd, int frag)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_frag(hapd->drv_priv, frag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
|
||||||
|
int total_flags, int flags_or, int flags_and)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
|
||||||
|
flags_or, flags_and);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
unsigned int weight)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
|
||||||
|
weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_country(struct hostapd_data *hapd, const char *country)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL ||
|
||||||
|
hapd->driver->set_country == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_country(hapd->drv_priv, country);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
|
||||||
|
int cw_min, int cw_max, int burst_time)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
|
||||||
|
cw_min, cw_max, burst_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct hostapd_hw_modes *
|
||||||
|
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
|
||||||
|
u16 *flags, u8 *dfs_domain)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL ||
|
||||||
|
hapd->driver->get_hw_feature_data == NULL)
|
||||||
|
return NULL;
|
||||||
|
return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
|
||||||
|
flags, dfs_domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_driver_commit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->commit == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->commit(hapd->drv_priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_none(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_driver_scan(struct hostapd_data *hapd,
|
||||||
|
struct wpa_driver_scan_params *params)
|
||||||
|
{
|
||||||
|
if (hapd->driver && hapd->driver->scan2)
|
||||||
|
return hapd->driver->scan2(hapd->drv_priv, params);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct wpa_scan_results * hostapd_driver_get_scan_results(
|
||||||
|
struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (hapd->driver && hapd->driver->get_scan_results2)
|
||||||
|
return hapd->driver->get_scan_results2(hapd->drv_priv);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||||
|
int duration)
|
||||||
|
{
|
||||||
|
if (hapd->driver && hapd->driver->set_noa)
|
||||||
|
return hapd->driver->set_noa(hapd->drv_priv, count, start,
|
||||||
|
duration);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
|
||||||
|
enum wpa_alg alg, const u8 *addr,
|
||||||
|
int key_idx, int set_tx,
|
||||||
|
const u8 *seq, size_t seq_len,
|
||||||
|
const u8 *key, size_t key_len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_key == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
|
||||||
|
key_idx, set_tx, seq, seq_len, key,
|
||||||
|
key_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||||
|
const void *msg, size_t len, int noack)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
|
||||||
|
const void *msg, size_t len, int noack,
|
||||||
|
const u16 *csa_offs, size_t csa_offs_len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
|
||||||
|
csa_offs, csa_offs_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int reason)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
|
||||||
|
reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int reason)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
|
||||||
|
reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
|
||||||
|
const u8 *peer, u8 *buf, u16 *buf_len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
|
||||||
|
buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||||
|
unsigned int wait, const u8 *dst, const u8 *data,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
const u8 *bssid;
|
||||||
|
const u8 wildcard_bssid[ETH_ALEN] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
bssid = hapd->own_addr;
|
||||||
|
if (!is_multicast_ether_addr(dst) &&
|
||||||
|
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public Action frames to a STA that is not a member of the BSS
|
||||||
|
* shall use wildcard BSSID value.
|
||||||
|
*/
|
||||||
|
sta = ap_get_sta(hapd, dst);
|
||||||
|
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
|
||||||
|
bssid = wildcard_bssid;
|
||||||
|
} else if (is_broadcast_ether_addr(dst) &&
|
||||||
|
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
|
||||||
|
/*
|
||||||
|
* The only current use case of Public Action frames with
|
||||||
|
* broadcast destination address is DPP PKEX. That case is
|
||||||
|
* directing all devices and not just the STAs within the BSS,
|
||||||
|
* so have to use the wildcard BSSID value.
|
||||||
|
*/
|
||||||
|
bssid = wildcard_bssid;
|
||||||
|
}
|
||||||
|
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
|
||||||
|
hapd->own_addr, bssid, data, len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
|
||||||
|
unsigned int freq,
|
||||||
|
unsigned int wait, const u8 *dst,
|
||||||
|
const u8 *data, size_t len)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->send_action == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
|
||||||
|
hapd->own_addr, hapd->own_addr, data,
|
||||||
|
len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
|
||||||
|
enum hostapd_hw_mode mode, int freq,
|
||||||
|
int channel, int ht_enabled, int vht_enabled,
|
||||||
|
int he_enabled,
|
||||||
|
int sec_channel_offset, int oper_chwidth,
|
||||||
|
int center_segment0, int center_segment1)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = iface->bss[0];
|
||||||
|
struct hostapd_freq_params data;
|
||||||
|
int res;
|
||||||
|
struct hostapd_hw_modes *cmode = iface->current_mode;
|
||||||
|
|
||||||
|
if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!iface->conf->ieee80211h) {
|
||||||
|
wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
|
||||||
|
"is not enabled");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
|
||||||
|
vht_enabled, he_enabled, sec_channel_offset,
|
||||||
|
oper_chwidth, center_segment0,
|
||||||
|
center_segment1,
|
||||||
|
cmode->vht_capab,
|
||||||
|
&cmode->he_capab[IEEE80211_MODE_AP])) {
|
||||||
|
wpa_printf(MSG_ERROR, "Can't set freq params");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
|
||||||
|
if (!res) {
|
||||||
|
iface->cac_started = 1;
|
||||||
|
os_get_reltime(&iface->dfs_cac_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
|
||||||
|
const u8 *qos_map_set, u8 qos_map_set_len)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
|
||||||
|
qos_map_set_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
|
||||||
|
struct hostapd_hw_modes *mode,
|
||||||
|
int acs_ch_list_all,
|
||||||
|
int **freq_list)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < mode->num_channels; i++) {
|
||||||
|
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||||
|
|
||||||
|
if ((acs_ch_list_all ||
|
||||||
|
freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
|
||||||
|
chan->chan)) &&
|
||||||
|
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||||
|
!(hapd->iface->conf->acs_exclude_dfs &&
|
||||||
|
(chan->flag & HOSTAPD_CHAN_RADAR)))
|
||||||
|
int_array_add_unique(freq_list, chan->freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_get_ext_capa(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = iface->bss[0];
|
||||||
|
|
||||||
|
if (!hapd->driver || !hapd->driver->get_ext_capab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
|
||||||
|
&iface->extended_capa,
|
||||||
|
&iface->extended_capa_mask,
|
||||||
|
&iface->extended_capa_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_do_acs(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct drv_acs_params params;
|
||||||
|
int ret, i, acs_ch_list_all = 0;
|
||||||
|
u8 *channels = NULL;
|
||||||
|
unsigned int num_channels = 0;
|
||||||
|
struct hostapd_hw_modes *mode;
|
||||||
|
int *freq_list = NULL;
|
||||||
|
|
||||||
|
if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
params.hw_mode = hapd->iface->conf->hw_mode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no chanlist config parameter is provided, include all enabled
|
||||||
|
* channels of the selected hw_mode.
|
||||||
|
*/
|
||||||
|
if (!hapd->iface->conf->acs_ch_list.num)
|
||||||
|
acs_ch_list_all = 1;
|
||||||
|
|
||||||
|
mode = hapd->iface->current_mode;
|
||||||
|
if (mode) {
|
||||||
|
channels = os_malloc(mode->num_channels);
|
||||||
|
if (channels == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < mode->num_channels; i++) {
|
||||||
|
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||||
|
if (!acs_ch_list_all &&
|
||||||
|
!freq_range_list_includes(
|
||||||
|
&hapd->iface->conf->acs_ch_list,
|
||||||
|
chan->chan))
|
||||||
|
continue;
|
||||||
|
if (hapd->iface->conf->acs_exclude_dfs &&
|
||||||
|
(chan->flag & HOSTAPD_CHAN_RADAR))
|
||||||
|
continue;
|
||||||
|
if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
|
||||||
|
channels[num_channels++] = chan->chan;
|
||||||
|
int_array_add_unique(&freq_list, chan->freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||||
|
mode = &hapd->iface->hw_features[i];
|
||||||
|
hostapd_get_hw_mode_any_channels(hapd, mode,
|
||||||
|
acs_ch_list_all,
|
||||||
|
&freq_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.ch_list = channels;
|
||||||
|
params.ch_list_len = num_channels;
|
||||||
|
params.freq_list = freq_list;
|
||||||
|
|
||||||
|
params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
|
||||||
|
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
|
||||||
|
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
|
||||||
|
params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
|
||||||
|
params.ch_width = 20;
|
||||||
|
if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
|
||||||
|
params.ch_width = 40;
|
||||||
|
|
||||||
|
/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
|
||||||
|
*/
|
||||||
|
if ((hapd->iface->conf->ieee80211ax ||
|
||||||
|
hapd->iface->conf->ieee80211ac) &&
|
||||||
|
params.ht40_enabled) {
|
||||||
|
u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
|
||||||
|
|
||||||
|
if (oper_chwidth == CHANWIDTH_80MHZ)
|
||||||
|
params.ch_width = 80;
|
||||||
|
else if (oper_chwidth == CHANWIDTH_160MHZ ||
|
||||||
|
oper_chwidth == CHANWIDTH_80P80MHZ)
|
||||||
|
params.ch_width = 160;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms);
|
||||||
|
os_free(channels);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
|
||||||
|
u16 reason_code, const u8 *ie, size_t ielen)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
|
||||||
|
ie, ielen);
|
||||||
|
}
|
384
hostapd-2.9/src/ap/ap_drv_ops.h
Normal file
384
hostapd-2.9/src/ap/ap_drv_ops.h
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
/*
|
||||||
|
* hostapd - Driver operations
|
||||||
|
* Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AP_DRV_OPS
|
||||||
|
#define AP_DRV_OPS
|
||||||
|
|
||||||
|
enum wpa_driver_if_type;
|
||||||
|
struct wpa_bss_params;
|
||||||
|
struct wpa_driver_scan_params;
|
||||||
|
struct ieee80211_ht_capabilities;
|
||||||
|
struct ieee80211_vht_capabilities;
|
||||||
|
struct hostapd_freq_params;
|
||||||
|
|
||||||
|
u32 hostapd_sta_flags_to_drv(u32 flags);
|
||||||
|
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
|
||||||
|
struct wpabuf **beacon,
|
||||||
|
struct wpabuf **proberesp,
|
||||||
|
struct wpabuf **assocresp);
|
||||||
|
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
|
||||||
|
struct wpabuf *proberesp,
|
||||||
|
struct wpabuf *assocresp);
|
||||||
|
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
|
||||||
|
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
|
||||||
|
int hostapd_set_authorized(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int authorized);
|
||||||
|
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
|
||||||
|
int enabled);
|
||||||
|
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
|
||||||
|
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
|
||||||
|
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
|
||||||
|
const u8 *addr, int aid, int val);
|
||||||
|
int hostapd_sta_add(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, u16 aid, u16 capability,
|
||||||
|
const u8 *supp_rates, size_t supp_rates_len,
|
||||||
|
u16 listen_interval,
|
||||||
|
const struct ieee80211_ht_capabilities *ht_capab,
|
||||||
|
const struct ieee80211_vht_capabilities *vht_capab,
|
||||||
|
const struct ieee80211_he_capabilities *he_capab,
|
||||||
|
size_t he_capab_len,
|
||||||
|
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||||
|
int set);
|
||||||
|
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
|
||||||
|
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
|
||||||
|
size_t elem_len);
|
||||||
|
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
|
||||||
|
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
|
||||||
|
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||||
|
const char *ifname, const u8 *addr, void *bss_ctx,
|
||||||
|
void **drv_priv, char *force_ifname, u8 *if_addr,
|
||||||
|
const char *bridge, int use_existing);
|
||||||
|
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||||
|
const char *ifname);
|
||||||
|
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
|
||||||
|
struct wpa_bss_params *params);
|
||||||
|
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int idx, u8 *seq);
|
||||||
|
int hostapd_flush(struct hostapd_data *hapd);
|
||||||
|
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
|
||||||
|
int freq, int channel, int ht_enabled, int vht_enabled,
|
||||||
|
int he_enabled, int sec_channel_offset, int oper_chwidth,
|
||||||
|
int center_segment0, int center_segment1);
|
||||||
|
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
|
||||||
|
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
|
||||||
|
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
|
||||||
|
int total_flags, int flags_or, int flags_and);
|
||||||
|
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
unsigned int weight);
|
||||||
|
int hostapd_set_country(struct hostapd_data *hapd, const char *country);
|
||||||
|
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
|
||||||
|
int cw_min, int cw_max, int burst_time);
|
||||||
|
struct hostapd_hw_modes *
|
||||||
|
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
|
||||||
|
u16 *flags, u8 *dfs_domain);
|
||||||
|
int hostapd_driver_commit(struct hostapd_data *hapd);
|
||||||
|
int hostapd_drv_none(struct hostapd_data *hapd);
|
||||||
|
int hostapd_driver_scan(struct hostapd_data *hapd,
|
||||||
|
struct wpa_driver_scan_params *params);
|
||||||
|
struct wpa_scan_results * hostapd_driver_get_scan_results(
|
||||||
|
struct hostapd_data *hapd);
|
||||||
|
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||||
|
int duration);
|
||||||
|
int hostapd_drv_set_key(const char *ifname,
|
||||||
|
struct hostapd_data *hapd,
|
||||||
|
enum wpa_alg alg, const u8 *addr,
|
||||||
|
int key_idx, int set_tx,
|
||||||
|
const u8 *seq, size_t seq_len,
|
||||||
|
const u8 *key, size_t key_len);
|
||||||
|
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||||
|
const void *msg, size_t len, int noack);
|
||||||
|
int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
|
||||||
|
const void *msg, size_t len, int noack,
|
||||||
|
const u16 *csa_offs, size_t csa_offs_len);
|
||||||
|
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int reason);
|
||||||
|
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int reason);
|
||||||
|
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||||
|
unsigned int wait, const u8 *dst, const u8 *data,
|
||||||
|
size_t len);
|
||||||
|
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
|
||||||
|
unsigned int freq,
|
||||||
|
unsigned int wait, const u8 *dst,
|
||||||
|
const u8 *data, size_t len);
|
||||||
|
static inline void
|
||||||
|
hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
|
||||||
|
!hapd->drv_priv)
|
||||||
|
return;
|
||||||
|
hapd->driver->send_action_cancel_wait(hapd->drv_priv);
|
||||||
|
}
|
||||||
|
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u16 auth_alg);
|
||||||
|
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u16 seq, u16 status, const u8 *ie, size_t len);
|
||||||
|
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
int reassoc, u16 status, const u8 *ie, size_t len);
|
||||||
|
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u8 *tspec_ie, size_t tspec_ielen);
|
||||||
|
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
|
||||||
|
enum hostapd_hw_mode mode, int freq,
|
||||||
|
int channel, int ht_enabled, int vht_enabled,
|
||||||
|
int he_enabled,
|
||||||
|
int sec_channel_offset, int oper_chwidth,
|
||||||
|
int center_segment0, int center_segment1);
|
||||||
|
int hostapd_drv_do_acs(struct hostapd_data *hapd);
|
||||||
|
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
|
||||||
|
u16 reason_code, const u8 *ie, size_t ielen);
|
||||||
|
|
||||||
|
|
||||||
|
#include "drivers/driver.h"
|
||||||
|
|
||||||
|
int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
|
||||||
|
enum wnm_oper oper, const u8 *peer,
|
||||||
|
u8 *buf, u16 *buf_len);
|
||||||
|
|
||||||
|
int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
|
||||||
|
u8 qos_map_set_len);
|
||||||
|
|
||||||
|
void hostapd_get_ext_capa(struct hostapd_iface *iface);
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
|
||||||
|
int enabled)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL ||
|
||||||
|
hapd->driver->hapd_set_countermeasures == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_sta_vlan(const char *ifname,
|
||||||
|
struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int vlan_id)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
|
||||||
|
vlan_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_remove(hapd->drv_priv, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, const u8 *data,
|
||||||
|
size_t data_len, int encrypt,
|
||||||
|
u32 flags)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
|
||||||
|
data_len, encrypt,
|
||||||
|
hapd->own_addr, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_read_sta_data(
|
||||||
|
struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
|
||||||
|
const u8 *addr)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
|
||||||
|
struct hostapd_acl_params *params)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_acl(hapd->drv_priv, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
|
||||||
|
struct wpa_driver_ap_params *params)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_ap(hapd->drv_priv, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
|
||||||
|
const u8 *mac, int accepted,
|
||||||
|
u32 session_timeout)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
|
||||||
|
session_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
|
||||||
|
const u8 *mac)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL ||
|
||||||
|
hapd->driver->set_radius_acl_expire == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
|
||||||
|
int auth_algs)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
|
||||||
|
const u8 *own_addr, const u8 *addr,
|
||||||
|
int qos)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
|
||||||
|
return;
|
||||||
|
hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
|
||||||
|
unsigned int freq)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL)
|
||||||
|
return -1;
|
||||||
|
if (!hapd->driver->get_survey)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->get_survey(hapd->drv_priv, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->get_country == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->get_country(hapd->drv_priv, alpha2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||||
|
hapd->driver->get_radio_name == NULL)
|
||||||
|
return NULL;
|
||||||
|
return hapd->driver->get_radio_name(hapd->drv_priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
|
||||||
|
struct csa_settings *settings)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
|
||||||
|
hapd->drv_priv == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return hapd->driver->switch_channel(hapd->drv_priv, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
|
||||||
|
size_t buflen)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->status(hapd->drv_priv, buf, buflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
|
||||||
|
int version, const u8 *ipaddr,
|
||||||
|
int prefixlen, const u8 *addr)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||||
|
hapd->driver->br_add_ip_neigh == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
|
||||||
|
prefixlen, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
|
||||||
|
u8 version, const u8 *ipaddr)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||||
|
hapd->driver->br_delete_ip_neigh == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
|
||||||
|
ipaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
|
||||||
|
enum drv_br_port_attr attr,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||||
|
hapd->driver->br_port_set_attr == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
|
||||||
|
enum drv_br_net_param param,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||||
|
hapd->driver->br_set_net_param == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
|
||||||
|
int vendor_id, int subcmd,
|
||||||
|
const u8 *data, size_t data_len,
|
||||||
|
struct wpabuf *buf)
|
||||||
|
{
|
||||||
|
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
|
||||||
|
data_len, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
|
||||||
|
return 0;
|
||||||
|
return hapd->driver->stop_ap(hapd->drv_priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
|
||||||
|
struct wpa_channel_info *ci)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->driver->channel_info)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->channel_info(hapd->drv_priv, ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
|
||||||
|
struct external_auth *params)
|
||||||
|
{
|
||||||
|
if (!hapd->driver || !hapd->drv_priv ||
|
||||||
|
!hapd->driver->send_external_auth_status)
|
||||||
|
return -1;
|
||||||
|
return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* AP_DRV_OPS */
|
312
hostapd-2.9/src/ap/ap_list.c
Normal file
312
hostapd-2.9/src/ap/ap_list.c
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
/*
|
||||||
|
* hostapd / AP table
|
||||||
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
|
||||||
|
* Copyright (c) 2006, Devicescape Software, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/ieee802_11_common.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "beacon.h"
|
||||||
|
#include "ap_list.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* AP list is a double linked list with head->prev pointing to the end of the
|
||||||
|
* list and tail->next = NULL. Entries are moved to the head of the list
|
||||||
|
* whenever a beacon has been received from the AP in question. The tail entry
|
||||||
|
* in this link will thus be the least recently used entry. */
|
||||||
|
|
||||||
|
|
||||||
|
static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (iface->current_mode == NULL ||
|
||||||
|
iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
|
||||||
|
iface->conf->channel != ap->channel)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
|
||||||
|
int rate = (ap->supported_rates[i] & 0x7f) * 5;
|
||||||
|
if (rate == 60 || rate == 90 || rate > 110)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
|
||||||
|
{
|
||||||
|
struct ap_info *s;
|
||||||
|
|
||||||
|
s = iface->ap_hash[STA_HASH(ap)];
|
||||||
|
while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
|
||||||
|
s = s->hnext;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
|
||||||
|
{
|
||||||
|
if (iface->ap_list) {
|
||||||
|
ap->prev = iface->ap_list->prev;
|
||||||
|
iface->ap_list->prev = ap;
|
||||||
|
} else
|
||||||
|
ap->prev = ap;
|
||||||
|
ap->next = iface->ap_list;
|
||||||
|
iface->ap_list = ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
|
||||||
|
{
|
||||||
|
if (iface->ap_list == ap)
|
||||||
|
iface->ap_list = ap->next;
|
||||||
|
else
|
||||||
|
ap->prev->next = ap->next;
|
||||||
|
|
||||||
|
if (ap->next)
|
||||||
|
ap->next->prev = ap->prev;
|
||||||
|
else if (iface->ap_list)
|
||||||
|
iface->ap_list->prev = ap->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
|
||||||
|
{
|
||||||
|
ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
|
||||||
|
iface->ap_hash[STA_HASH(ap->addr)] = ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
|
||||||
|
{
|
||||||
|
struct ap_info *s;
|
||||||
|
|
||||||
|
s = iface->ap_hash[STA_HASH(ap->addr)];
|
||||||
|
if (s == NULL) return;
|
||||||
|
if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
|
||||||
|
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (s->hnext != NULL &&
|
||||||
|
os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
|
||||||
|
s = s->hnext;
|
||||||
|
if (s->hnext != NULL)
|
||||||
|
s->hnext = s->hnext->hnext;
|
||||||
|
else
|
||||||
|
wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
|
||||||
|
" from hash table", MAC2STR(ap->addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
|
||||||
|
{
|
||||||
|
ap_ap_hash_del(iface, ap);
|
||||||
|
ap_ap_list_del(iface, ap);
|
||||||
|
|
||||||
|
iface->num_ap--;
|
||||||
|
os_free(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_free_aps(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct ap_info *ap, *prev;
|
||||||
|
|
||||||
|
ap = iface->ap_list;
|
||||||
|
|
||||||
|
while (ap) {
|
||||||
|
prev = ap;
|
||||||
|
ap = ap->next;
|
||||||
|
ap_free_ap(iface, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->ap_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
|
||||||
|
{
|
||||||
|
struct ap_info *ap;
|
||||||
|
|
||||||
|
ap = os_zalloc(sizeof(struct ap_info));
|
||||||
|
if (ap == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* initialize AP info data */
|
||||||
|
os_memcpy(ap->addr, addr, ETH_ALEN);
|
||||||
|
ap_ap_list_add(iface, ap);
|
||||||
|
iface->num_ap++;
|
||||||
|
ap_ap_hash_add(iface, ap);
|
||||||
|
|
||||||
|
if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
|
||||||
|
MACSTR " from AP table", MAC2STR(ap->prev->addr));
|
||||||
|
ap_free_ap(iface, ap->prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ap_list_process_beacon(struct hostapd_iface *iface,
|
||||||
|
const struct ieee80211_mgmt *mgmt,
|
||||||
|
struct ieee802_11_elems *elems,
|
||||||
|
struct hostapd_frame_info *fi)
|
||||||
|
{
|
||||||
|
struct ap_info *ap;
|
||||||
|
int new_ap = 0;
|
||||||
|
int set_beacon = 0;
|
||||||
|
|
||||||
|
if (iface->conf->ap_table_max_size < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ap = ap_get_ap(iface, mgmt->bssid);
|
||||||
|
if (!ap) {
|
||||||
|
ap = ap_ap_add(iface, mgmt->bssid);
|
||||||
|
if (!ap) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"Failed to allocate AP information entry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_ap = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
|
||||||
|
elems->supp_rates, elems->supp_rates_len,
|
||||||
|
elems->ext_supp_rates, elems->ext_supp_rates_len);
|
||||||
|
|
||||||
|
if (elems->erp_info)
|
||||||
|
ap->erp = elems->erp_info[0];
|
||||||
|
else
|
||||||
|
ap->erp = -1;
|
||||||
|
|
||||||
|
if (elems->ds_params)
|
||||||
|
ap->channel = elems->ds_params[0];
|
||||||
|
else if (elems->ht_operation)
|
||||||
|
ap->channel = elems->ht_operation[0];
|
||||||
|
else if (fi)
|
||||||
|
ap->channel = fi->channel;
|
||||||
|
|
||||||
|
if (elems->ht_capabilities)
|
||||||
|
ap->ht_support = 1;
|
||||||
|
else
|
||||||
|
ap->ht_support = 0;
|
||||||
|
|
||||||
|
os_get_reltime(&ap->last_beacon);
|
||||||
|
|
||||||
|
if (!new_ap && ap != iface->ap_list) {
|
||||||
|
/* move AP entry into the beginning of the list so that the
|
||||||
|
* oldest entry is always in the end of the list */
|
||||||
|
ap_ap_list_del(iface, ap);
|
||||||
|
ap_ap_list_add(iface, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iface->olbc &&
|
||||||
|
ap_list_beacon_olbc(iface, ap)) {
|
||||||
|
iface->olbc = 1;
|
||||||
|
wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
|
||||||
|
" (channel %d) - enable protection",
|
||||||
|
MAC2STR(ap->addr), ap->channel);
|
||||||
|
set_beacon++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211N
|
||||||
|
if (!iface->olbc_ht && !ap->ht_support &&
|
||||||
|
(ap->channel == 0 ||
|
||||||
|
ap->channel == iface->conf->channel ||
|
||||||
|
ap->channel == iface->conf->channel +
|
||||||
|
iface->conf->secondary_channel * 4)) {
|
||||||
|
iface->olbc_ht = 1;
|
||||||
|
hostapd_ht_operation_update(iface);
|
||||||
|
wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
|
||||||
|
" (channel %d) - enable protection",
|
||||||
|
MAC2STR(ap->addr), ap->channel);
|
||||||
|
set_beacon++;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211N */
|
||||||
|
|
||||||
|
if (set_beacon)
|
||||||
|
ieee802_11_update_beacons(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ap_list_timer(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct os_reltime now;
|
||||||
|
struct ap_info *ap;
|
||||||
|
int set_beacon = 0;
|
||||||
|
|
||||||
|
if (!iface->ap_list)
|
||||||
|
return;
|
||||||
|
|
||||||
|
os_get_reltime(&now);
|
||||||
|
|
||||||
|
while (iface->ap_list) {
|
||||||
|
ap = iface->ap_list->prev;
|
||||||
|
if (!os_reltime_expired(&now, &ap->last_beacon,
|
||||||
|
iface->conf->ap_table_expiration_time))
|
||||||
|
break;
|
||||||
|
|
||||||
|
ap_free_ap(iface, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->olbc || iface->olbc_ht) {
|
||||||
|
int olbc = 0;
|
||||||
|
int olbc_ht = 0;
|
||||||
|
|
||||||
|
ap = iface->ap_list;
|
||||||
|
while (ap && (olbc == 0 || olbc_ht == 0)) {
|
||||||
|
if (ap_list_beacon_olbc(iface, ap))
|
||||||
|
olbc = 1;
|
||||||
|
if (!ap->ht_support)
|
||||||
|
olbc_ht = 1;
|
||||||
|
ap = ap->next;
|
||||||
|
}
|
||||||
|
if (!olbc && iface->olbc) {
|
||||||
|
wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
|
||||||
|
iface->olbc = 0;
|
||||||
|
set_beacon++;
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_IEEE80211N
|
||||||
|
if (!olbc_ht && iface->olbc_ht) {
|
||||||
|
wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
|
||||||
|
iface->olbc_ht = 0;
|
||||||
|
hostapd_ht_operation_update(iface);
|
||||||
|
set_beacon++;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211N */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_beacon)
|
||||||
|
ieee802_11_update_beacons(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ap_list_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ap_list_deinit(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
hostapd_free_aps(iface);
|
||||||
|
}
|
58
hostapd-2.9/src/ap/ap_list.h
Normal file
58
hostapd-2.9/src/ap/ap_list.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* hostapd / AP table
|
||||||
|
* Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
|
||||||
|
* Copyright (c) 2006, Devicescape Software, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AP_LIST_H
|
||||||
|
#define AP_LIST_H
|
||||||
|
|
||||||
|
struct ap_info {
|
||||||
|
/* Note: next/prev pointers are updated whenever a new beacon is
|
||||||
|
* received because these are used to find the least recently used
|
||||||
|
* entries. */
|
||||||
|
struct ap_info *next; /* next entry in AP list */
|
||||||
|
struct ap_info *prev; /* previous entry in AP list */
|
||||||
|
struct ap_info *hnext; /* next entry in hash table list */
|
||||||
|
u8 addr[6];
|
||||||
|
u8 supported_rates[WLAN_SUPP_RATES_MAX];
|
||||||
|
int erp; /* ERP Info or -1 if ERP info element not present */
|
||||||
|
|
||||||
|
int channel;
|
||||||
|
|
||||||
|
int ht_support;
|
||||||
|
|
||||||
|
struct os_reltime last_beacon;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ieee802_11_elems;
|
||||||
|
struct hostapd_frame_info;
|
||||||
|
|
||||||
|
void ap_list_process_beacon(struct hostapd_iface *iface,
|
||||||
|
const struct ieee80211_mgmt *mgmt,
|
||||||
|
struct ieee802_11_elems *elems,
|
||||||
|
struct hostapd_frame_info *fi);
|
||||||
|
#ifdef NEED_AP_MLME
|
||||||
|
int ap_list_init(struct hostapd_iface *iface);
|
||||||
|
void ap_list_deinit(struct hostapd_iface *iface);
|
||||||
|
void ap_list_timer(struct hostapd_iface *iface);
|
||||||
|
#else /* NEED_AP_MLME */
|
||||||
|
static inline int ap_list_init(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ap_list_deinit(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ap_list_timer(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* NEED_AP_MLME */
|
||||||
|
|
||||||
|
#endif /* AP_LIST_H */
|
191
hostapd-2.9/src/ap/ap_mlme.c
Normal file
191
hostapd-2.9/src/ap/ap_mlme.c
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11 MLME
|
||||||
|
* Copyright 2003-2006, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright 2003-2004, Instant802 Networks, Inc.
|
||||||
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "wpa_auth.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ap_mlme.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_HOSTAPD_LOGGER
|
||||||
|
static const char * mlme_auth_alg_str(int alg)
|
||||||
|
{
|
||||||
|
switch (alg) {
|
||||||
|
case WLAN_AUTH_OPEN:
|
||||||
|
return "OPEN_SYSTEM";
|
||||||
|
case WLAN_AUTH_SHARED_KEY:
|
||||||
|
return "SHARED_KEY";
|
||||||
|
case WLAN_AUTH_FT:
|
||||||
|
return "FT";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlme_authenticate_indication - Report the establishment of an authentication
|
||||||
|
* relationship with a specific peer MAC entity
|
||||||
|
* @hapd: BSS data
|
||||||
|
* @sta: peer STA data
|
||||||
|
*
|
||||||
|
* MLME calls this function as a result of the establishment of an
|
||||||
|
* authentication relationship with a specific peer MAC entity that
|
||||||
|
* resulted from an authentication procedure that was initiated by
|
||||||
|
* that specific peer MAC entity.
|
||||||
|
*
|
||||||
|
* PeerSTAAddress = sta->addr
|
||||||
|
* AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
|
||||||
|
*/
|
||||||
|
void mlme_authenticate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
|
||||||
|
MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
|
||||||
|
if (sta->auth_alg != WLAN_AUTH_FT &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_SK &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_PK &&
|
||||||
|
!(sta->flags & WLAN_STA_MFP))
|
||||||
|
mlme_deletekeys_request(hapd, sta);
|
||||||
|
ap_sta_clear_disconnect_timeouts(hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlme_deauthenticate_indication - Report the invalidation of an
|
||||||
|
* authentication relationship with a specific peer MAC entity
|
||||||
|
* @hapd: BSS data
|
||||||
|
* @sta: Peer STA data
|
||||||
|
* @reason_code: ReasonCode from Deauthentication frame
|
||||||
|
*
|
||||||
|
* MLME calls this function as a result of the invalidation of an
|
||||||
|
* authentication relationship with a specific peer MAC entity.
|
||||||
|
*
|
||||||
|
* PeerSTAAddress = sta->addr
|
||||||
|
*/
|
||||||
|
void mlme_deauthenticate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, u16 reason_code)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
|
||||||
|
MAC2STR(sta->addr), reason_code);
|
||||||
|
if (!hapd->iface->driver_ap_teardown)
|
||||||
|
mlme_deletekeys_request(hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlme_associate_indication - Report the establishment of an association with
|
||||||
|
* a specific peer MAC entity
|
||||||
|
* @hapd: BSS data
|
||||||
|
* @sta: peer STA data
|
||||||
|
*
|
||||||
|
* MLME calls this function as a result of the establishment of an
|
||||||
|
* association with a specific peer MAC entity that resulted from an
|
||||||
|
* association procedure that was initiated by that specific peer MAC entity.
|
||||||
|
*
|
||||||
|
* PeerSTAAddress = sta->addr
|
||||||
|
*/
|
||||||
|
void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-ASSOCIATE.indication(" MACSTR ")",
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
if (sta->auth_alg != WLAN_AUTH_FT &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_SK &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_PK)
|
||||||
|
mlme_deletekeys_request(hapd, sta);
|
||||||
|
ap_sta_clear_disconnect_timeouts(hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlme_reassociate_indication - Report the establishment of an reassociation
|
||||||
|
* with a specific peer MAC entity
|
||||||
|
* @hapd: BSS data
|
||||||
|
* @sta: peer STA data
|
||||||
|
*
|
||||||
|
* MLME calls this function as a result of the establishment of an
|
||||||
|
* reassociation with a specific peer MAC entity that resulted from a
|
||||||
|
* reassociation procedure that was initiated by that specific peer MAC entity.
|
||||||
|
*
|
||||||
|
* PeerSTAAddress = sta->addr
|
||||||
|
*/
|
||||||
|
void mlme_reassociate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-REASSOCIATE.indication(" MACSTR ")",
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
if (sta->auth_alg != WLAN_AUTH_FT &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_SK &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
|
||||||
|
sta->auth_alg != WLAN_AUTH_FILS_PK)
|
||||||
|
mlme_deletekeys_request(hapd, sta);
|
||||||
|
ap_sta_clear_disconnect_timeouts(hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlme_disassociate_indication - Report disassociation with a specific peer
|
||||||
|
* MAC entity
|
||||||
|
* @hapd: BSS data
|
||||||
|
* @sta: Peer STA data
|
||||||
|
* @reason_code: ReasonCode from Disassociation frame
|
||||||
|
*
|
||||||
|
* MLME calls this function as a result of the invalidation of an association
|
||||||
|
* relationship with a specific peer MAC entity.
|
||||||
|
*
|
||||||
|
* PeerSTAAddress = sta->addr
|
||||||
|
*/
|
||||||
|
void mlme_disassociate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, u16 reason_code)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
|
||||||
|
MAC2STR(sta->addr), reason_code);
|
||||||
|
mlme_deletekeys_request(hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-MichaelMICFailure.indication(" MACSTR ")",
|
||||||
|
MAC2STR(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"MLME-DELETEKEYS.request(" MACSTR ")",
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
|
||||||
|
if (sta->wpa_sm)
|
||||||
|
wpa_remove_ptk(sta->wpa_sm);
|
||||||
|
}
|
34
hostapd-2.9/src/ap/ap_mlme.h
Normal file
34
hostapd-2.9/src/ap/ap_mlme.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11 MLME
|
||||||
|
* Copyright 2003, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright 2003-2004, Instant802 Networks, Inc.
|
||||||
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MLME_H
|
||||||
|
#define MLME_H
|
||||||
|
|
||||||
|
void mlme_authenticate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
|
||||||
|
void mlme_deauthenticate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, u16 reason_code);
|
||||||
|
|
||||||
|
void mlme_associate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
|
||||||
|
void mlme_reassociate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
|
||||||
|
void mlme_disassociate_indication(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, u16 reason_code);
|
||||||
|
|
||||||
|
void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr);
|
||||||
|
|
||||||
|
void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
|
||||||
|
#endif /* MLME_H */
|
306
hostapd-2.9/src/ap/authsrv.c
Normal file
306
hostapd-2.9/src/ap/authsrv.c
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
/*
|
||||||
|
* Authentication server setup
|
||||||
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "crypto/tls.h"
|
||||||
|
#include "eap_server/eap.h"
|
||||||
|
#include "eap_server/eap_sim_db.h"
|
||||||
|
#include "eapol_auth/eapol_auth_sm.h"
|
||||||
|
#include "radius/radius_server.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "authsrv.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
|
||||||
|
#define EAP_SIM_DB
|
||||||
|
#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EAP_SIM_DB
|
||||||
|
static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, void *ctx)
|
||||||
|
{
|
||||||
|
if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = ctx;
|
||||||
|
if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
|
||||||
|
#ifdef RADIUS_SERVER
|
||||||
|
radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
|
||||||
|
#endif /* RADIUS_SERVER */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EAP_SIM_DB */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef RADIUS_SERVER
|
||||||
|
|
||||||
|
static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
|
||||||
|
size_t identity_len, int phase2,
|
||||||
|
struct eap_user *user)
|
||||||
|
{
|
||||||
|
const struct hostapd_eap_user *eap_user;
|
||||||
|
int i;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
|
||||||
|
if (eap_user == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (user == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
os_memset(user, 0, sizeof(*user));
|
||||||
|
for (i = 0; i < EAP_MAX_METHODS; i++) {
|
||||||
|
user->methods[i].vendor = eap_user->methods[i].vendor;
|
||||||
|
user->methods[i].method = eap_user->methods[i].method;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eap_user->password) {
|
||||||
|
user->password = os_memdup(eap_user->password,
|
||||||
|
eap_user->password_len);
|
||||||
|
if (user->password == NULL)
|
||||||
|
goto out;
|
||||||
|
user->password_len = eap_user->password_len;
|
||||||
|
user->password_hash = eap_user->password_hash;
|
||||||
|
if (eap_user->salt && eap_user->salt_len) {
|
||||||
|
user->salt = os_memdup(eap_user->salt,
|
||||||
|
eap_user->salt_len);
|
||||||
|
if (!user->salt)
|
||||||
|
goto out;
|
||||||
|
user->salt_len = eap_user->salt_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user->force_version = eap_user->force_version;
|
||||||
|
user->macacl = eap_user->macacl;
|
||||||
|
user->ttls_auth = eap_user->ttls_auth;
|
||||||
|
user->remediation = eap_user->remediation;
|
||||||
|
user->accept_attr = eap_user->accept_attr;
|
||||||
|
user->t_c_timestamp = eap_user->t_c_timestamp;
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (rv)
|
||||||
|
wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct radius_server_conf srv;
|
||||||
|
struct hostapd_bss_config *conf = hapd->conf;
|
||||||
|
os_memset(&srv, 0, sizeof(srv));
|
||||||
|
srv.client_file = conf->radius_server_clients;
|
||||||
|
srv.auth_port = conf->radius_server_auth_port;
|
||||||
|
srv.acct_port = conf->radius_server_acct_port;
|
||||||
|
srv.conf_ctx = hapd;
|
||||||
|
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
|
||||||
|
srv.ssl_ctx = hapd->ssl_ctx;
|
||||||
|
srv.msg_ctx = hapd->msg_ctx;
|
||||||
|
srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
|
||||||
|
srv.eap_fast_a_id = conf->eap_fast_a_id;
|
||||||
|
srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
|
||||||
|
srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
|
||||||
|
srv.eap_fast_prov = conf->eap_fast_prov;
|
||||||
|
srv.pac_key_lifetime = conf->pac_key_lifetime;
|
||||||
|
srv.pac_key_refresh_time = conf->pac_key_refresh_time;
|
||||||
|
srv.eap_teap_auth = conf->eap_teap_auth;
|
||||||
|
srv.eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
|
||||||
|
srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
|
||||||
|
srv.eap_sim_id = conf->eap_sim_id;
|
||||||
|
srv.tnc = conf->tnc;
|
||||||
|
srv.wps = hapd->wps;
|
||||||
|
srv.ipv6 = conf->radius_server_ipv6;
|
||||||
|
srv.get_eap_user = hostapd_radius_get_eap_user;
|
||||||
|
srv.eap_req_id_text = conf->eap_req_id_text;
|
||||||
|
srv.eap_req_id_text_len = conf->eap_req_id_text_len;
|
||||||
|
srv.pwd_group = conf->pwd_group;
|
||||||
|
srv.server_id = conf->server_id ? conf->server_id : "hostapd";
|
||||||
|
srv.sqlite_file = conf->eap_user_sqlite;
|
||||||
|
#ifdef CONFIG_RADIUS_TEST
|
||||||
|
srv.dump_msk_file = conf->dump_msk_file;
|
||||||
|
#endif /* CONFIG_RADIUS_TEST */
|
||||||
|
#ifdef CONFIG_HS20
|
||||||
|
srv.subscr_remediation_url = conf->subscr_remediation_url;
|
||||||
|
srv.subscr_remediation_method = conf->subscr_remediation_method;
|
||||||
|
srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
|
||||||
|
srv.t_c_server_url = conf->t_c_server_url;
|
||||||
|
#endif /* CONFIG_HS20 */
|
||||||
|
srv.erp = conf->eap_server_erp;
|
||||||
|
srv.erp_domain = conf->erp_domain;
|
||||||
|
srv.tls_session_lifetime = conf->tls_session_lifetime;
|
||||||
|
srv.tls_flags = conf->tls_flags;
|
||||||
|
|
||||||
|
hapd->radius_srv = radius_server_init(&srv);
|
||||||
|
if (hapd->radius_srv == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RADIUS_SERVER */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EAP_TLS_FUNCS
|
||||||
|
static void authsrv_tls_event(void *ctx, enum tls_event ev,
|
||||||
|
union tls_event_data *data)
|
||||||
|
{
|
||||||
|
switch (ev) {
|
||||||
|
case TLS_CERT_CHAIN_SUCCESS:
|
||||||
|
wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
|
||||||
|
break;
|
||||||
|
case TLS_CERT_CHAIN_FAILURE:
|
||||||
|
wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
|
||||||
|
data->cert_fail.reason,
|
||||||
|
data->cert_fail.depth,
|
||||||
|
data->cert_fail.subject,
|
||||||
|
data->cert_fail.reason_txt);
|
||||||
|
break;
|
||||||
|
case TLS_PEER_CERTIFICATE:
|
||||||
|
wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
|
||||||
|
data->peer_cert.depth,
|
||||||
|
data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
|
||||||
|
data->peer_cert.subject);
|
||||||
|
break;
|
||||||
|
case TLS_ALERT:
|
||||||
|
if (data->alert.is_local)
|
||||||
|
wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
|
||||||
|
data->alert.description);
|
||||||
|
else
|
||||||
|
wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
|
||||||
|
data->alert.description);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EAP_TLS_FUNCS */
|
||||||
|
|
||||||
|
|
||||||
|
int authsrv_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
#ifdef EAP_TLS_FUNCS
|
||||||
|
if (hapd->conf->eap_server &&
|
||||||
|
(hapd->conf->ca_cert || hapd->conf->server_cert ||
|
||||||
|
hapd->conf->private_key || hapd->conf->dh_file ||
|
||||||
|
hapd->conf->server_cert2 || hapd->conf->private_key2)) {
|
||||||
|
struct tls_config conf;
|
||||||
|
struct tls_connection_params params;
|
||||||
|
|
||||||
|
os_memset(&conf, 0, sizeof(conf));
|
||||||
|
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
|
||||||
|
if (hapd->conf->crl_reload_interval > 0 &&
|
||||||
|
hapd->conf->check_crl <= 0) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"Cannot enable CRL reload functionality - it depends on check_crl being set");
|
||||||
|
} else if (hapd->conf->crl_reload_interval > 0) {
|
||||||
|
conf.crl_reload_interval =
|
||||||
|
hapd->conf->crl_reload_interval;
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"Enabled CRL reload functionality");
|
||||||
|
}
|
||||||
|
conf.tls_flags = hapd->conf->tls_flags;
|
||||||
|
conf.event_cb = authsrv_tls_event;
|
||||||
|
conf.cb_ctx = hapd;
|
||||||
|
hapd->ssl_ctx = tls_init(&conf);
|
||||||
|
if (hapd->ssl_ctx == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize TLS");
|
||||||
|
authsrv_deinit(hapd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
params.ca_cert = hapd->conf->ca_cert;
|
||||||
|
params.client_cert = hapd->conf->server_cert;
|
||||||
|
params.client_cert2 = hapd->conf->server_cert2;
|
||||||
|
params.private_key = hapd->conf->private_key;
|
||||||
|
params.private_key2 = hapd->conf->private_key2;
|
||||||
|
params.private_key_passwd = hapd->conf->private_key_passwd;
|
||||||
|
params.private_key_passwd2 = hapd->conf->private_key_passwd2;
|
||||||
|
params.dh_file = hapd->conf->dh_file;
|
||||||
|
params.openssl_ciphers = hapd->conf->openssl_ciphers;
|
||||||
|
params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
|
||||||
|
params.ocsp_stapling_response =
|
||||||
|
hapd->conf->ocsp_stapling_response;
|
||||||
|
params.ocsp_stapling_response_multi =
|
||||||
|
hapd->conf->ocsp_stapling_response_multi;
|
||||||
|
params.check_cert_subject = hapd->conf->check_cert_subject;
|
||||||
|
|
||||||
|
if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
|
||||||
|
authsrv_deinit(hapd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tls_global_set_verify(hapd->ssl_ctx,
|
||||||
|
hapd->conf->check_crl,
|
||||||
|
hapd->conf->check_crl_strict)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to enable check_crl");
|
||||||
|
authsrv_deinit(hapd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EAP_TLS_FUNCS */
|
||||||
|
|
||||||
|
#ifdef EAP_SIM_DB
|
||||||
|
if (hapd->conf->eap_sim_db) {
|
||||||
|
hapd->eap_sim_db_priv =
|
||||||
|
eap_sim_db_init(hapd->conf->eap_sim_db,
|
||||||
|
hapd->conf->eap_sim_db_timeout,
|
||||||
|
hostapd_sim_db_cb, hapd);
|
||||||
|
if (hapd->eap_sim_db_priv == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
|
||||||
|
"database interface");
|
||||||
|
authsrv_deinit(hapd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EAP_SIM_DB */
|
||||||
|
|
||||||
|
#ifdef RADIUS_SERVER
|
||||||
|
if (hapd->conf->radius_server_clients &&
|
||||||
|
hostapd_setup_radius_srv(hapd))
|
||||||
|
return -1;
|
||||||
|
#endif /* RADIUS_SERVER */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void authsrv_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
#ifdef RADIUS_SERVER
|
||||||
|
radius_server_deinit(hapd->radius_srv);
|
||||||
|
hapd->radius_srv = NULL;
|
||||||
|
#endif /* RADIUS_SERVER */
|
||||||
|
|
||||||
|
#ifdef EAP_TLS_FUNCS
|
||||||
|
if (hapd->ssl_ctx) {
|
||||||
|
tls_deinit(hapd->ssl_ctx);
|
||||||
|
hapd->ssl_ctx = NULL;
|
||||||
|
}
|
||||||
|
#endif /* EAP_TLS_FUNCS */
|
||||||
|
|
||||||
|
#ifdef EAP_SIM_DB
|
||||||
|
if (hapd->eap_sim_db_priv) {
|
||||||
|
eap_sim_db_deinit(hapd->eap_sim_db_priv);
|
||||||
|
hapd->eap_sim_db_priv = NULL;
|
||||||
|
}
|
||||||
|
#endif /* EAP_SIM_DB */
|
||||||
|
}
|
15
hostapd-2.9/src/ap/authsrv.h
Normal file
15
hostapd-2.9/src/ap/authsrv.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Authentication server setup
|
||||||
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AUTHSRV_H
|
||||||
|
#define AUTHSRV_H
|
||||||
|
|
||||||
|
int authsrv_init(struct hostapd_data *hapd);
|
||||||
|
void authsrv_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
#endif /* AUTHSRV_H */
|
1485
hostapd-2.9/src/ap/beacon.c
Normal file
1485
hostapd-2.9/src/ap/beacon.c
Normal file
File diff suppressed because it is too large
Load diff
33
hostapd-2.9/src/ap/beacon.h
Normal file
33
hostapd-2.9/src/ap/beacon.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
|
||||||
|
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
|
||||||
|
* Copyright (c) 2005-2006, Devicescape Software, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BEACON_H
|
||||||
|
#define BEACON_H
|
||||||
|
|
||||||
|
struct ieee80211_mgmt;
|
||||||
|
|
||||||
|
void handle_probe_req(struct hostapd_data *hapd,
|
||||||
|
const struct ieee80211_mgmt *mgmt, size_t len,
|
||||||
|
int ssi_signal);
|
||||||
|
int ieee802_11_set_beacon(struct hostapd_data *hapd);
|
||||||
|
int ieee802_11_set_beacons(struct hostapd_iface *iface);
|
||||||
|
int ieee802_11_update_beacons(struct hostapd_iface *iface);
|
||||||
|
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
|
||||||
|
struct wpa_driver_ap_params *params);
|
||||||
|
void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
|
||||||
|
void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
|
||||||
|
void sta_track_del(struct hostapd_sta_info *info);
|
||||||
|
void sta_track_expire(struct hostapd_iface *iface, int force);
|
||||||
|
struct hostapd_data *
|
||||||
|
sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
|
||||||
|
const char *ifname);
|
||||||
|
void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
|
||||||
|
struct wpabuf **probe_ie_taxonomy);
|
||||||
|
|
||||||
|
#endif /* BEACON_H */
|
99
hostapd-2.9/src/ap/bss_load.c
Normal file
99
hostapd-2.9/src/ap/bss_load.c
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* BSS Load Element / Channel Utilization
|
||||||
|
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "bss_load.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "beacon.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int get_bss_load_update_timeout(struct hostapd_data *hapd,
|
||||||
|
unsigned int *sec, unsigned int *usec)
|
||||||
|
{
|
||||||
|
unsigned int update_period = hapd->conf->bss_load_update_period;
|
||||||
|
unsigned int beacon_int = hapd->iconf->beacon_int;
|
||||||
|
unsigned int update_timeout;
|
||||||
|
|
||||||
|
if (!update_period || !beacon_int) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
|
||||||
|
update_period, beacon_int);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_timeout = update_period * beacon_int;
|
||||||
|
|
||||||
|
*sec = ((update_timeout / 1000) * 1024) / 1000;
|
||||||
|
*usec = (update_timeout % 1000) * 1024;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void update_channel_utilization(void *eloop_data, void *user_data)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = eloop_data;
|
||||||
|
unsigned int sec, usec;
|
||||||
|
int err;
|
||||||
|
struct hostapd_iface *iface = hapd->iface;
|
||||||
|
|
||||||
|
if (!(hapd->beacon_set_done && hapd->started))
|
||||||
|
return;
|
||||||
|
|
||||||
|
err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
|
||||||
|
if (err) {
|
||||||
|
wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ieee802_11_set_beacon(hapd);
|
||||||
|
|
||||||
|
if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hapd->conf->chan_util_avg_period) {
|
||||||
|
iface->chan_util_samples_sum += iface->channel_utilization;
|
||||||
|
iface->chan_util_num_sample_periods +=
|
||||||
|
hapd->conf->bss_load_update_period;
|
||||||
|
if (iface->chan_util_num_sample_periods >=
|
||||||
|
hapd->conf->chan_util_avg_period) {
|
||||||
|
iface->chan_util_average =
|
||||||
|
iface->chan_util_samples_sum /
|
||||||
|
(iface->chan_util_num_sample_periods /
|
||||||
|
hapd->conf->bss_load_update_period);
|
||||||
|
iface->chan_util_samples_sum = 0;
|
||||||
|
iface->chan_util_num_sample_periods = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int bss_load_update_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
unsigned int sec, usec;
|
||||||
|
|
||||||
|
if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
|
||||||
|
NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void bss_load_update_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
|
||||||
|
}
|
17
hostapd-2.9/src/ap/bss_load.h
Normal file
17
hostapd-2.9/src/ap/bss_load.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* BSS load update
|
||||||
|
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BSS_LOAD_UPDATE_H
|
||||||
|
#define BSS_LOAD_UPDATE_H
|
||||||
|
|
||||||
|
|
||||||
|
int bss_load_update_init(struct hostapd_data *hapd);
|
||||||
|
void bss_load_update_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* BSS_LOAD_UPDATE_H */
|
996
hostapd-2.9/src/ap/ctrl_iface_ap.c
Normal file
996
hostapd-2.9/src/ap/ctrl_iface_ap.c
Normal file
|
@ -0,0 +1,996 @@
|
||||||
|
/*
|
||||||
|
* Control interface for shared AP commands
|
||||||
|
* Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/sae.h"
|
||||||
|
#include "eapol_auth/eapol_auth_sm.h"
|
||||||
|
#include "fst/fst_ctrl_iface.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ieee802_1x.h"
|
||||||
|
#include "wpa_auth.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "wps_hostapd.h"
|
||||||
|
#include "p2p_hostapd.h"
|
||||||
|
#include "ctrl_iface_ap.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "mbo_ap.h"
|
||||||
|
#include "taxonomy.h"
|
||||||
|
|
||||||
|
|
||||||
|
static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
|
||||||
|
size_t curr_len, const u8 *mcs_set)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
size_t len = curr_len;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"ht_mcs_bitmask=");
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
/* 77 first bits (+ 3 reserved bits) */
|
||||||
|
len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return curr_len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
struct hostap_sta_driver_data data;
|
||||||
|
int ret;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
|
||||||
|
"rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
|
||||||
|
"signal=%d\n",
|
||||||
|
data.rx_packets, data.tx_packets,
|
||||||
|
data.rx_bytes, data.tx_bytes, data.inactive_msec,
|
||||||
|
data.signal);
|
||||||
|
if (os_snprintf_error(buflen, ret))
|
||||||
|
return 0;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
|
||||||
|
data.current_rx_rate);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
if (data.flags & STA_DRV_DATA_RX_MCS) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " mcs %u",
|
||||||
|
data.rx_mcs);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
|
||||||
|
data.rx_vhtmcs);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
|
||||||
|
data.rx_vht_nss);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " shortGI");
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
|
||||||
|
data.current_tx_rate);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
if (data.flags & STA_DRV_DATA_TX_MCS) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " mcs %u",
|
||||||
|
data.tx_mcs);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
|
||||||
|
data.tx_vhtmcs);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
|
||||||
|
data.tx_vht_nss);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, " shortGI");
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"rx_vht_mcs_map=%04x\n"
|
||||||
|
"tx_vht_mcs_map=%04x\n",
|
||||||
|
le_to_host16(sta->vht_capabilities->
|
||||||
|
vht_supported_mcs_set.rx_map),
|
||||||
|
le_to_host16(sta->vht_capabilities->
|
||||||
|
vht_supported_mcs_set.tx_map));
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
|
||||||
|
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
|
||||||
|
sta->ht_capabilities->
|
||||||
|
supported_mcs_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"last_ack_signal=%d\n", data.last_ack_rssi);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_get_sta_conn_time(struct sta_info *sta,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
struct os_reltime age;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sta->connected_time.sec)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
os_reltime_age(&sta->connected_time, &age);
|
||||||
|
|
||||||
|
ret = os_snprintf(buf, buflen, "connected_time=%u\n",
|
||||||
|
(unsigned int) age.sec);
|
||||||
|
if (os_snprintf_error(buflen, ret))
|
||||||
|
return 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char * timeout_next_str(int val)
|
||||||
|
{
|
||||||
|
switch (val) {
|
||||||
|
case STA_NULLFUNC:
|
||||||
|
return "NULLFUNC POLL";
|
||||||
|
case STA_DISASSOC:
|
||||||
|
return "DISASSOC";
|
||||||
|
case STA_DEAUTH:
|
||||||
|
return "DEAUTH";
|
||||||
|
case STA_REMOVE:
|
||||||
|
return "REMOVE";
|
||||||
|
case STA_DISASSOC_FROM_CLI:
|
||||||
|
return "DISASSOC_FROM_CLI";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
int len, res, ret, i;
|
||||||
|
const char *keyid;
|
||||||
|
|
||||||
|
if (!sta)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
|
||||||
|
if (ret < 0)
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
|
||||||
|
"listen_interval=%d\nsupported_rates=",
|
||||||
|
sta->aid, sta->capability, sta->listen_interval);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
for (i = 0; i < sta->supported_rates_len; i++) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "%02x%s",
|
||||||
|
sta->supported_rates[i],
|
||||||
|
i + 1 < sta->supported_rates_len ? " " : "");
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
|
||||||
|
timeout_next_str(sta->timeout_next));
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
|
||||||
|
if (res >= 0)
|
||||||
|
len += res;
|
||||||
|
res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
|
||||||
|
if (res >= 0)
|
||||||
|
len += res;
|
||||||
|
res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
|
||||||
|
if (res >= 0)
|
||||||
|
len += res;
|
||||||
|
res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
|
||||||
|
buflen - len);
|
||||||
|
if (res >= 0)
|
||||||
|
len += res;
|
||||||
|
res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
|
||||||
|
if (res >= 0)
|
||||||
|
len += res;
|
||||||
|
|
||||||
|
len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
|
||||||
|
len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAE
|
||||||
|
if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
|
||||||
|
res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
|
||||||
|
sta->sae->group);
|
||||||
|
if (!os_snprintf_error(buflen - len, res))
|
||||||
|
len += res;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_SAE */
|
||||||
|
|
||||||
|
if (sta->vlan_id > 0) {
|
||||||
|
res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
|
||||||
|
sta->vlan_id);
|
||||||
|
if (!os_snprintf_error(buflen - len, res))
|
||||||
|
len += res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mbo_ap_get_info(sta, buf + len, buflen - len);
|
||||||
|
if (res >= 0)
|
||||||
|
len += res;
|
||||||
|
|
||||||
|
if (sta->supp_op_classes &&
|
||||||
|
buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
|
||||||
|
len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
|
||||||
|
len += wpa_snprintf_hex(buf + len, buflen - len,
|
||||||
|
sta->supp_op_classes + 1,
|
||||||
|
sta->supp_op_classes[0]);
|
||||||
|
len += os_snprintf(buf + len, buflen - len, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta->power_capab) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"min_txpower=%d\n"
|
||||||
|
"max_txpower=%d\n",
|
||||||
|
sta->min_tx_power, sta->max_tx_power);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211AC
|
||||||
|
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
|
||||||
|
res = os_snprintf(buf + len, buflen - len,
|
||||||
|
"vht_caps_info=0x%08x\n",
|
||||||
|
le_to_host32(sta->vht_capabilities->
|
||||||
|
vht_capabilities_info));
|
||||||
|
if (!os_snprintf_error(buflen - len, res))
|
||||||
|
len += res;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211AC */
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211N
|
||||||
|
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
|
||||||
|
res = os_snprintf(buf + len, buflen - len,
|
||||||
|
"ht_caps_info=0x%04x\n",
|
||||||
|
le_to_host16(sta->ht_capabilities->
|
||||||
|
ht_capabilities_info));
|
||||||
|
if (!os_snprintf_error(buflen - len, res))
|
||||||
|
len += res;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211N */
|
||||||
|
|
||||||
|
if (sta->ext_capability &&
|
||||||
|
buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
|
||||||
|
len += os_snprintf(buf + len, buflen - len, "ext_capab=");
|
||||||
|
len += wpa_snprintf_hex(buf + len, buflen - len,
|
||||||
|
sta->ext_capability + 1,
|
||||||
|
sta->ext_capability[0]);
|
||||||
|
len += os_snprintf(buf + len, buflen - len, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"wds_sta_ifname=%s\n", sta->ifname_wds);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyid = ap_sta_wpa_get_keyid(hapd, sta);
|
||||||
|
if (keyid) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
|
||||||
|
if (!os_snprintf_error(buflen - len, ret))
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
int ret;
|
||||||
|
const char *pos;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
if (hwaddr_aton(txtaddr, addr)) {
|
||||||
|
ret = os_snprintf(buf, buflen, "FAIL\n");
|
||||||
|
if (os_snprintf_error(buflen, ret))
|
||||||
|
return 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (sta == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = os_strchr(txtaddr, ' ');
|
||||||
|
if (pos) {
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
#ifdef HOSTAPD_DUMP_STATE
|
||||||
|
if (os_strcmp(pos, "eapol") == 0) {
|
||||||
|
if (sta->eapol_sm == NULL)
|
||||||
|
return -1;
|
||||||
|
return eapol_auth_dump_state(sta->eapol_sm, buf,
|
||||||
|
buflen);
|
||||||
|
}
|
||||||
|
#endif /* HOSTAPD_DUMP_STATE */
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
|
||||||
|
ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
struct sta_info *sta;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (hwaddr_aton(txtaddr, addr) ||
|
||||||
|
(sta = ap_get_sta(hapd, addr)) == NULL) {
|
||||||
|
ret = os_snprintf(buf, buflen, "FAIL\n");
|
||||||
|
if (os_snprintf_error(buflen, ret))
|
||||||
|
return 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sta->next)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P_MANAGER
|
||||||
|
static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
|
||||||
|
u8 minor_reason_code, const u8 *addr)
|
||||||
|
{
|
||||||
|
struct ieee80211_mgmt *mgmt;
|
||||||
|
int ret;
|
||||||
|
u8 *pos;
|
||||||
|
|
||||||
|
if (!hapd->drv_priv || !hapd->driver->send_frame)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mgmt = os_zalloc(sizeof(*mgmt) + 100);
|
||||||
|
if (mgmt == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
|
||||||
|
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
|
||||||
|
" with minor reason code %u (stype=%u (%s))",
|
||||||
|
MAC2STR(addr), minor_reason_code, stype,
|
||||||
|
fc2str(le_to_host16(mgmt->frame_control)));
|
||||||
|
|
||||||
|
os_memcpy(mgmt->da, addr, ETH_ALEN);
|
||||||
|
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||||
|
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||||
|
if (stype == WLAN_FC_STYPE_DEAUTH) {
|
||||||
|
mgmt->u.deauth.reason_code =
|
||||||
|
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||||
|
pos = mgmt->u.deauth.variable;
|
||||||
|
} else {
|
||||||
|
mgmt->u.disassoc.reason_code =
|
||||||
|
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||||
|
pos = mgmt->u.disassoc.variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||||
|
*pos++ = 4 + 3 + 1;
|
||||||
|
WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
|
||||||
|
pos += 4;
|
||||||
|
|
||||||
|
*pos++ = P2P_ATTR_MINOR_REASON_CODE;
|
||||||
|
WPA_PUT_LE16(pos, 1);
|
||||||
|
pos += 2;
|
||||||
|
*pos++ = minor_reason_code;
|
||||||
|
|
||||||
|
ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
|
||||||
|
pos - (u8 *) mgmt, 1);
|
||||||
|
os_free(mgmt);
|
||||||
|
|
||||||
|
return ret < 0 ? -1 : 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_P2P_MANAGER */
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
struct sta_info *sta;
|
||||||
|
const char *pos;
|
||||||
|
u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
||||||
|
|
||||||
|
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
|
||||||
|
txtaddr);
|
||||||
|
|
||||||
|
if (hwaddr_aton(txtaddr, addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = os_strstr(txtaddr, " reason=");
|
||||||
|
if (pos)
|
||||||
|
reason = atoi(pos + 8);
|
||||||
|
|
||||||
|
pos = os_strstr(txtaddr, " test=");
|
||||||
|
if (pos) {
|
||||||
|
struct ieee80211_mgmt mgmt;
|
||||||
|
int encrypt;
|
||||||
|
if (!hapd->drv_priv || !hapd->driver->send_frame)
|
||||||
|
return -1;
|
||||||
|
pos += 6;
|
||||||
|
encrypt = atoi(pos);
|
||||||
|
os_memset(&mgmt, 0, sizeof(mgmt));
|
||||||
|
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||||
|
WLAN_FC_STYPE_DEAUTH);
|
||||||
|
os_memcpy(mgmt.da, addr, ETH_ALEN);
|
||||||
|
os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
|
||||||
|
os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
|
||||||
|
mgmt.u.deauth.reason_code = host_to_le16(reason);
|
||||||
|
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
|
||||||
|
IEEE80211_HDRLEN +
|
||||||
|
sizeof(mgmt.u.deauth),
|
||||||
|
encrypt) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P_MANAGER
|
||||||
|
pos = os_strstr(txtaddr, " p2p=");
|
||||||
|
if (pos) {
|
||||||
|
return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
|
||||||
|
atoi(pos + 5), addr);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_P2P_MANAGER */
|
||||||
|
|
||||||
|
if (os_strstr(txtaddr, " tx=0"))
|
||||||
|
hostapd_drv_sta_remove(hapd, addr);
|
||||||
|
else
|
||||||
|
hostapd_drv_sta_deauth(hapd, addr, reason);
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (sta)
|
||||||
|
ap_sta_deauthenticate(hapd, sta, reason);
|
||||||
|
else if (addr[0] == 0xff)
|
||||||
|
hostapd_free_stas(hapd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
struct sta_info *sta;
|
||||||
|
const char *pos;
|
||||||
|
u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
||||||
|
|
||||||
|
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
|
||||||
|
txtaddr);
|
||||||
|
|
||||||
|
if (hwaddr_aton(txtaddr, addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = os_strstr(txtaddr, " reason=");
|
||||||
|
if (pos)
|
||||||
|
reason = atoi(pos + 8);
|
||||||
|
|
||||||
|
pos = os_strstr(txtaddr, " test=");
|
||||||
|
if (pos) {
|
||||||
|
struct ieee80211_mgmt mgmt;
|
||||||
|
int encrypt;
|
||||||
|
if (!hapd->drv_priv || !hapd->driver->send_frame)
|
||||||
|
return -1;
|
||||||
|
pos += 6;
|
||||||
|
encrypt = atoi(pos);
|
||||||
|
os_memset(&mgmt, 0, sizeof(mgmt));
|
||||||
|
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||||
|
WLAN_FC_STYPE_DISASSOC);
|
||||||
|
os_memcpy(mgmt.da, addr, ETH_ALEN);
|
||||||
|
os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
|
||||||
|
os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
|
||||||
|
mgmt.u.disassoc.reason_code = host_to_le16(reason);
|
||||||
|
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
|
||||||
|
IEEE80211_HDRLEN +
|
||||||
|
sizeof(mgmt.u.deauth),
|
||||||
|
encrypt) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P_MANAGER
|
||||||
|
pos = os_strstr(txtaddr, " p2p=");
|
||||||
|
if (pos) {
|
||||||
|
return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
|
||||||
|
atoi(pos + 5), addr);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_P2P_MANAGER */
|
||||||
|
|
||||||
|
if (os_strstr(txtaddr, " tx=0"))
|
||||||
|
hostapd_drv_sta_remove(hapd, addr);
|
||||||
|
else
|
||||||
|
hostapd_drv_sta_disassoc(hapd, addr, reason);
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (sta)
|
||||||
|
ap_sta_disassociate(hapd, sta, reason);
|
||||||
|
else if (addr[0] == 0xff)
|
||||||
|
hostapd_free_stas(hapd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_TAXONOMY
|
||||||
|
int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
|
||||||
|
|
||||||
|
if (hwaddr_aton(txtaddr, addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (!sta)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_TAXONOMY */
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
|
||||||
|
|
||||||
|
if (hwaddr_aton(txtaddr, addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (!sta)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
|
||||||
|
sta->flags & WLAN_STA_WMM);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
|
||||||
|
size_t buflen)
|
||||||
|
{
|
||||||
|
struct hostapd_iface *iface = hapd->iface;
|
||||||
|
struct hostapd_hw_modes *mode = iface->current_mode;
|
||||||
|
int len = 0, ret, j;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"state=%s\n"
|
||||||
|
"phy=%s\n"
|
||||||
|
"freq=%d\n"
|
||||||
|
"num_sta_non_erp=%d\n"
|
||||||
|
"num_sta_no_short_slot_time=%d\n"
|
||||||
|
"num_sta_no_short_preamble=%d\n"
|
||||||
|
"olbc=%d\n"
|
||||||
|
"num_sta_ht_no_gf=%d\n"
|
||||||
|
"num_sta_no_ht=%d\n"
|
||||||
|
"num_sta_ht_20_mhz=%d\n"
|
||||||
|
"num_sta_ht40_intolerant=%d\n"
|
||||||
|
"olbc_ht=%d\n"
|
||||||
|
"ht_op_mode=0x%x\n",
|
||||||
|
hostapd_state_text(iface->state),
|
||||||
|
iface->phy,
|
||||||
|
iface->freq,
|
||||||
|
iface->num_sta_non_erp,
|
||||||
|
iface->num_sta_no_short_slot_time,
|
||||||
|
iface->num_sta_no_short_preamble,
|
||||||
|
iface->olbc,
|
||||||
|
iface->num_sta_ht_no_gf,
|
||||||
|
iface->num_sta_no_ht,
|
||||||
|
iface->num_sta_ht_20mhz,
|
||||||
|
iface->num_sta_ht40_intolerant,
|
||||||
|
iface->olbc_ht,
|
||||||
|
iface->ht_op_mode);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
if (!iface->cac_started || !iface->dfs_cac_ms) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"cac_time_seconds=%d\n"
|
||||||
|
"cac_time_left_seconds=N/A\n",
|
||||||
|
iface->dfs_cac_ms / 1000);
|
||||||
|
} else {
|
||||||
|
/* CAC started and CAC time set - calculate remaining time */
|
||||||
|
struct os_reltime now;
|
||||||
|
unsigned int left_time;
|
||||||
|
|
||||||
|
os_reltime_age(&iface->dfs_cac_start, &now);
|
||||||
|
left_time = iface->dfs_cac_ms / 1000 - now.sec;
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"cac_time_seconds=%u\n"
|
||||||
|
"cac_time_left_seconds=%u\n",
|
||||||
|
iface->dfs_cac_ms / 1000,
|
||||||
|
left_time);
|
||||||
|
}
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"channel=%u\n"
|
||||||
|
"secondary_channel=%d\n"
|
||||||
|
"ieee80211n=%d\n"
|
||||||
|
"ieee80211ac=%d\n"
|
||||||
|
"ieee80211ax=%d\n"
|
||||||
|
"beacon_int=%u\n"
|
||||||
|
"dtim_period=%d\n",
|
||||||
|
iface->conf->channel,
|
||||||
|
iface->conf->ieee80211n && !hapd->conf->disable_11n ?
|
||||||
|
iface->conf->secondary_channel : 0,
|
||||||
|
iface->conf->ieee80211n && !hapd->conf->disable_11n,
|
||||||
|
iface->conf->ieee80211ac &&
|
||||||
|
!hapd->conf->disable_11ac,
|
||||||
|
iface->conf->ieee80211ax,
|
||||||
|
iface->conf->beacon_int,
|
||||||
|
hapd->conf->dtim_period);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"vht_oper_chwidth=%d\n"
|
||||||
|
"vht_oper_centr_freq_seg0_idx=%d\n"
|
||||||
|
"vht_oper_centr_freq_seg1_idx=%d\n"
|
||||||
|
"vht_caps_info=%08x\n",
|
||||||
|
iface->conf->vht_oper_chwidth,
|
||||||
|
iface->conf->vht_oper_centr_freq_seg0_idx,
|
||||||
|
iface->conf->vht_oper_centr_freq_seg1_idx,
|
||||||
|
iface->conf->vht_capab);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
|
||||||
|
u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
|
||||||
|
u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
|
||||||
|
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"rx_vht_mcs_map=%04x\n"
|
||||||
|
"tx_vht_mcs_map=%04x\n",
|
||||||
|
rxmap, txmap);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"ht_caps_info=%04x\n",
|
||||||
|
hapd->iconf->ht_capab);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
|
||||||
|
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
|
||||||
|
mode->mcs_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->current_rates && iface->num_rates) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
for (j = 0; j < iface->num_rates; j++) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "%s%02x",
|
||||||
|
j > 0 ? " " : "",
|
||||||
|
iface->current_rates[j].rate / 5);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; mode && j < mode->num_channels; j++) {
|
||||||
|
if (mode->channels[j].freq == iface->freq) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"max_txpower=%u\n",
|
||||||
|
mode->channels[j].max_tx_power);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < iface->num_bss; i++) {
|
||||||
|
struct hostapd_data *bss = iface->bss[i];
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"bss[%d]=%s\n"
|
||||||
|
"bssid[%d]=" MACSTR "\n"
|
||||||
|
"ssid[%d]=%s\n"
|
||||||
|
"num_sta[%d]=%d\n",
|
||||||
|
(int) i, bss->conf->iface,
|
||||||
|
(int) i, MAC2STR(bss->own_addr),
|
||||||
|
(int) i,
|
||||||
|
wpa_ssid_txt(bss->conf->ssid.ssid,
|
||||||
|
bss->conf->ssid.ssid_len),
|
||||||
|
(int) i, bss->num_sta);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->chan_util_avg_period) {
|
||||||
|
ret = os_snprintf(buf + len, buflen - len,
|
||||||
|
"chan_util_avg=%u\n",
|
||||||
|
iface->chan_util_average);
|
||||||
|
if (os_snprintf_error(buflen - len, ret))
|
||||||
|
return len;
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_parse_csa_settings(const char *pos,
|
||||||
|
struct csa_settings *settings)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
os_memset(settings, 0, sizeof(*settings));
|
||||||
|
settings->cs_count = strtol(pos, &end, 10);
|
||||||
|
if (pos == end) {
|
||||||
|
wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings->freq_params.freq = atoi(end);
|
||||||
|
if (settings->freq_params.freq == 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_CSA_SETTING(str) \
|
||||||
|
do { \
|
||||||
|
const char *pos2 = os_strstr(pos, " " #str "="); \
|
||||||
|
if (pos2) { \
|
||||||
|
pos2 += sizeof(" " #str "=") - 1; \
|
||||||
|
settings->freq_params.str = atoi(pos2); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
SET_CSA_SETTING(center_freq1);
|
||||||
|
SET_CSA_SETTING(center_freq2);
|
||||||
|
SET_CSA_SETTING(bandwidth);
|
||||||
|
SET_CSA_SETTING(sec_channel_offset);
|
||||||
|
settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
|
||||||
|
settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
|
||||||
|
settings->block_tx = !!os_strstr(pos, " blocktx");
|
||||||
|
#undef SET_CSA_SETTING
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return hostapd_drv_stop_ap(hapd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
wpa_auth_pmksa_flush(hapd->wpa_auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
|
||||||
|
{
|
||||||
|
u8 spa[ETH_ALEN];
|
||||||
|
u8 pmkid[PMKID_LEN];
|
||||||
|
u8 pmk[PMK_LEN_MAX];
|
||||||
|
size_t pmk_len;
|
||||||
|
char *pos, *pos2;
|
||||||
|
int akmp = 0, expiration = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entry format:
|
||||||
|
* <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (hwaddr_aton(cmd, spa))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = os_strchr(cmd, ' ');
|
||||||
|
if (!pos)
|
||||||
|
return -1;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = os_strchr(pos, ' ');
|
||||||
|
if (!pos)
|
||||||
|
return -1;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
pos2 = os_strchr(pos, ' ');
|
||||||
|
if (!pos2)
|
||||||
|
return -1;
|
||||||
|
pmk_len = (pos2 - pos) / 2;
|
||||||
|
if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
|
||||||
|
hexstr2bin(pos, pmk, pmk_len) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = pos2 + 1;
|
||||||
|
|
||||||
|
if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
|
||||||
|
pmkid, expiration, akmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||||
|
#ifdef CONFIG_MESH
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
|
||||||
|
{
|
||||||
|
u8 spa[ETH_ALEN];
|
||||||
|
u8 pmkid[PMKID_LEN];
|
||||||
|
u8 pmk[PMK_LEN_MAX];
|
||||||
|
char *pos;
|
||||||
|
int expiration;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entry format:
|
||||||
|
* <BSSID> <PMKID> <PMK> <expiration in seconds>
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (hwaddr_aton(cmd, spa))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pos = os_strchr(cmd, ' ');
|
||||||
|
if (!pos)
|
||||||
|
return NULL;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pos = os_strchr(pos, ' ');
|
||||||
|
if (!pos)
|
||||||
|
return NULL;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pos = os_strchr(pos, ' ');
|
||||||
|
if (!pos)
|
||||||
|
return NULL;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (sscanf(pos, "%d", &expiration) != 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_MESH */
|
||||||
|
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
|
40
hostapd-2.9/src/ap/ctrl_iface_ap.h
Normal file
40
hostapd-2.9/src/ap/ctrl_iface_ap.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Control interface for shared AP commands
|
||||||
|
* Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CTRL_IFACE_AP_H
|
||||||
|
#define CTRL_IFACE_AP_H
|
||||||
|
|
||||||
|
int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr);
|
||||||
|
int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr);
|
||||||
|
int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
|
||||||
|
const char *txtaddr);
|
||||||
|
int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
|
||||||
|
size_t buflen);
|
||||||
|
int hostapd_parse_csa_settings(const char *pos,
|
||||||
|
struct csa_settings *settings);
|
||||||
|
int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
|
||||||
|
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
|
||||||
|
size_t len);
|
||||||
|
void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
|
||||||
|
int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
|
||||||
|
int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, char *buf, size_t len);
|
||||||
|
void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
|
||||||
|
|
||||||
|
#endif /* CTRL_IFACE_AP_H */
|
1140
hostapd-2.9/src/ap/dfs.c
Normal file
1140
hostapd-2.9/src/ap/dfs.c
Normal file
File diff suppressed because it is too large
Load diff
33
hostapd-2.9/src/ap/dfs.h
Normal file
33
hostapd-2.9/src/ap/dfs.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* DFS - Dynamic Frequency Selection
|
||||||
|
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
#ifndef DFS_H
|
||||||
|
#define DFS_H
|
||||||
|
|
||||||
|
int hostapd_handle_dfs(struct hostapd_iface *iface);
|
||||||
|
|
||||||
|
int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
|
||||||
|
int ht_enabled, int chan_offset, int chan_width,
|
||||||
|
int cf1, int cf2);
|
||||||
|
int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
|
||||||
|
int ht_enabled, int chan_offset, int chan_width,
|
||||||
|
int cf1, int cf2);
|
||||||
|
int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
|
||||||
|
int ht_enabled,
|
||||||
|
int chan_offset, int chan_width,
|
||||||
|
int cf1, int cf2);
|
||||||
|
int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
|
||||||
|
int ht_enabled,
|
||||||
|
int chan_offset, int chan_width, int cf1, int cf2);
|
||||||
|
int hostapd_is_dfs_required(struct hostapd_iface *iface);
|
||||||
|
int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
|
||||||
|
int ht_enabled, int chan_offset, int chan_width,
|
||||||
|
int cf1, int cf2);
|
||||||
|
int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
|
||||||
|
|
||||||
|
#endif /* DFS_H */
|
158
hostapd-2.9/src/ap/dhcp_snoop.c
Normal file
158
hostapd-2.9/src/ap/dhcp_snoop.c
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* DHCP snooping for Proxy ARP
|
||||||
|
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "common/dhcp.h"
|
||||||
|
#include "l2_packet/l2_packet.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "x_snoop.h"
|
||||||
|
#include "dhcp_snoop.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const char * ipaddr_str(u32 addr)
|
||||||
|
{
|
||||||
|
static char buf[17];
|
||||||
|
|
||||||
|
os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
|
||||||
|
(addr >> 24) & 0xff, (addr >> 16) & 0xff,
|
||||||
|
(addr >> 8) & 0xff, addr & 0xff);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = ctx;
|
||||||
|
const struct bootp_pkt *b;
|
||||||
|
struct sta_info *sta;
|
||||||
|
int exten_len;
|
||||||
|
const u8 *end, *pos;
|
||||||
|
int res, msgtype = 0, prefixlen = 32;
|
||||||
|
u32 subnet_mask = 0;
|
||||||
|
u16 tot_len;
|
||||||
|
|
||||||
|
exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
|
||||||
|
if (exten_len < 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
b = (const struct bootp_pkt *) &buf[ETH_HLEN];
|
||||||
|
tot_len = ntohs(b->iph.tot_len);
|
||||||
|
if (tot_len > (unsigned int) (len - ETH_HLEN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Parse DHCP options */
|
||||||
|
end = (const u8 *) b + tot_len;
|
||||||
|
pos = &b->exten[4];
|
||||||
|
while (pos < end && *pos != DHCP_OPT_END) {
|
||||||
|
const u8 *opt = pos++;
|
||||||
|
|
||||||
|
if (*opt == DHCP_OPT_PAD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (pos >= end || 1 + *pos > end - pos)
|
||||||
|
break;
|
||||||
|
pos += *pos + 1;
|
||||||
|
if (pos >= end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (*opt) {
|
||||||
|
case DHCP_OPT_SUBNET_MASK:
|
||||||
|
if (opt[1] == 4)
|
||||||
|
subnet_mask = WPA_GET_BE32(&opt[2]);
|
||||||
|
if (subnet_mask == 0)
|
||||||
|
return;
|
||||||
|
while (!(subnet_mask & 0x1)) {
|
||||||
|
subnet_mask >>= 1;
|
||||||
|
prefixlen--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DHCP_OPT_MSG_TYPE:
|
||||||
|
if (opt[1])
|
||||||
|
msgtype = opt[2];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
|
||||||
|
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||||
|
if (!(sta->flags & WLAN_STA_AUTHORIZED))
|
||||||
|
continue;
|
||||||
|
x_snoop_mcast_to_ucast_convert_send(hapd, sta,
|
||||||
|
(u8 *) buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgtype == DHCPACK) {
|
||||||
|
if (b->your_ip == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* DHCPACK for DHCPREQUEST */
|
||||||
|
sta = ap_get_sta(hapd, b->hw_addr);
|
||||||
|
if (!sta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
|
||||||
|
" @ IPv4 address %s/%d",
|
||||||
|
MAC2STR(sta->addr),
|
||||||
|
ipaddr_str(be_to_host32(b->your_ip)),
|
||||||
|
prefixlen);
|
||||||
|
|
||||||
|
if (sta->ipaddr == b->your_ip)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sta->ipaddr != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
|
||||||
|
ipaddr_str(be_to_host32(sta->ipaddr)));
|
||||||
|
hostapd_drv_br_delete_ip_neigh(hapd, 4,
|
||||||
|
(u8 *) &sta->ipaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
|
||||||
|
prefixlen, sta->addr);
|
||||||
|
if (res) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Adding ip neigh table failed: %d",
|
||||||
|
res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sta->ipaddr = b->your_ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int dhcp_snoop_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
|
||||||
|
L2_PACKET_FILTER_DHCP);
|
||||||
|
if (hapd->sock_dhcp == NULL) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dhcp_snoop_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
l2_packet_deinit(hapd->sock_dhcp);
|
||||||
|
hapd->sock_dhcp = NULL;
|
||||||
|
}
|
30
hostapd-2.9/src/ap/dhcp_snoop.h
Normal file
30
hostapd-2.9/src/ap/dhcp_snoop.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* DHCP snooping for Proxy ARP
|
||||||
|
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DHCP_SNOOP_H
|
||||||
|
#define DHCP_SNOOP_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_PROXYARP
|
||||||
|
|
||||||
|
int dhcp_snoop_init(struct hostapd_data *hapd);
|
||||||
|
void dhcp_snoop_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
#else /* CONFIG_PROXYARP */
|
||||||
|
|
||||||
|
static inline int dhcp_snoop_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_PROXYARP */
|
||||||
|
|
||||||
|
#endif /* DHCP_SNOOP_H */
|
1725
hostapd-2.9/src/ap/dpp_hostapd.c
Normal file
1725
hostapd-2.9/src/ap/dpp_hostapd.c
Normal file
File diff suppressed because it is too large
Load diff
38
hostapd-2.9/src/ap/dpp_hostapd.h
Normal file
38
hostapd-2.9/src/ap/dpp_hostapd.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* hostapd / DPP integration
|
||||||
|
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DPP_HOSTAPD_H
|
||||||
|
#define DPP_HOSTAPD_H
|
||||||
|
|
||||||
|
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
|
||||||
|
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
|
||||||
|
int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
|
||||||
|
void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
|
||||||
|
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
const u8 *buf, size_t len, unsigned int freq);
|
||||||
|
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
|
||||||
|
const u8 *data, size_t data_len, int ok);
|
||||||
|
struct wpabuf *
|
||||||
|
hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
|
||||||
|
const u8 *query, size_t query_len,
|
||||||
|
const u8 *data, size_t data_len);
|
||||||
|
void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
|
||||||
|
int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
|
||||||
|
int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
|
||||||
|
int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
|
||||||
|
int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
|
||||||
|
int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
|
||||||
|
void hostapd_dpp_stop(struct hostapd_data *hapd);
|
||||||
|
int hostapd_dpp_init(struct hostapd_data *hapd);
|
||||||
|
void hostapd_dpp_deinit(struct hostapd_data *hapd);
|
||||||
|
void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
|
||||||
|
void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
|
||||||
|
|
||||||
|
#endif /* DPP_HOSTAPD_H */
|
1931
hostapd-2.9/src/ap/drv_callbacks.c
Normal file
1931
hostapd-2.9/src/ap/drv_callbacks.c
Normal file
File diff suppressed because it is too large
Load diff
290
hostapd-2.9/src/ap/eap_user_db.c
Normal file
290
hostapd-2.9/src/ap/eap_user_db.c
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* hostapd / EAP user database
|
||||||
|
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
#ifdef CONFIG_SQLITE
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#endif /* CONFIG_SQLITE */
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "eap_common/eap_wsc_common.h"
|
||||||
|
#include "eap_server/eap_methods.h"
|
||||||
|
#include "eap_server/eap.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_SQLITE
|
||||||
|
|
||||||
|
static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
|
||||||
|
{
|
||||||
|
char *buf, *start;
|
||||||
|
int num_methods;
|
||||||
|
|
||||||
|
buf = os_strdup(methods);
|
||||||
|
if (buf == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
os_memset(&user->methods, 0, sizeof(user->methods));
|
||||||
|
num_methods = 0;
|
||||||
|
start = buf;
|
||||||
|
while (*start) {
|
||||||
|
char *pos3 = os_strchr(start, ',');
|
||||||
|
if (pos3)
|
||||||
|
*pos3++ = '\0';
|
||||||
|
user->methods[num_methods].method =
|
||||||
|
eap_server_get_type(start,
|
||||||
|
&user->methods[num_methods].vendor);
|
||||||
|
if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
|
||||||
|
user->methods[num_methods].method == EAP_TYPE_NONE) {
|
||||||
|
if (os_strcmp(start, "TTLS-PAP") == 0) {
|
||||||
|
user->ttls_auth |= EAP_TTLS_AUTH_PAP;
|
||||||
|
goto skip_eap;
|
||||||
|
}
|
||||||
|
if (os_strcmp(start, "TTLS-CHAP") == 0) {
|
||||||
|
user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
|
||||||
|
goto skip_eap;
|
||||||
|
}
|
||||||
|
if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
|
||||||
|
user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
|
||||||
|
goto skip_eap;
|
||||||
|
}
|
||||||
|
if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
|
||||||
|
user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
|
||||||
|
goto skip_eap;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
|
||||||
|
start);
|
||||||
|
os_free(buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_methods++;
|
||||||
|
if (num_methods >= EAP_MAX_METHODS)
|
||||||
|
break;
|
||||||
|
skip_eap:
|
||||||
|
if (pos3 == NULL)
|
||||||
|
break;
|
||||||
|
start = pos3;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
|
||||||
|
{
|
||||||
|
struct hostapd_eap_user *user = ctx;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
if (os_strcmp(col[i], "password") == 0 && argv[i]) {
|
||||||
|
bin_clear_free(user->password, user->password_len);
|
||||||
|
user->password_len = os_strlen(argv[i]);
|
||||||
|
user->password = (u8 *) os_strdup(argv[i]);
|
||||||
|
user->next = (void *) 1;
|
||||||
|
} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
|
||||||
|
set_user_methods(user, argv[i]);
|
||||||
|
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
|
||||||
|
user->remediation = strlen(argv[i]) > 0;
|
||||||
|
} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
|
||||||
|
user->t_c_timestamp = strtol(argv[i], NULL, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
|
||||||
|
{
|
||||||
|
struct hostapd_eap_user *user = ctx;
|
||||||
|
int i, id = -1, methods = -1;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
if (os_strcmp(col[i], "identity") == 0 && argv[i])
|
||||||
|
id = i;
|
||||||
|
else if (os_strcmp(col[i], "methods") == 0 && argv[i])
|
||||||
|
methods = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id < 0 || methods < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = os_strlen(argv[id]);
|
||||||
|
if (len <= user->identity_len &&
|
||||||
|
os_memcmp(argv[id], user->identity, len) == 0 &&
|
||||||
|
(user->password == NULL || len > user->password_len)) {
|
||||||
|
bin_clear_free(user->password, user->password_len);
|
||||||
|
user->password_len = os_strlen(argv[id]);
|
||||||
|
user->password = (u8 *) os_strdup(argv[id]);
|
||||||
|
user->next = (void *) 1;
|
||||||
|
set_user_methods(user, argv[methods]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct hostapd_eap_user *
|
||||||
|
eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
|
||||||
|
size_t identity_len, int phase2)
|
||||||
|
{
|
||||||
|
sqlite3 *db;
|
||||||
|
struct hostapd_eap_user *user = NULL;
|
||||||
|
char id_str[256], cmd[300];
|
||||||
|
size_t i;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (identity_len >= sizeof(id_str)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
|
||||||
|
__func__, (int) identity_len,
|
||||||
|
(int) (sizeof(id_str)));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
os_memcpy(id_str, identity, identity_len);
|
||||||
|
id_str[identity_len] = '\0';
|
||||||
|
for (i = 0; i < identity_len; i++) {
|
||||||
|
if (id_str[i] >= 'a' && id_str[i] <= 'z')
|
||||||
|
continue;
|
||||||
|
if (id_str[i] >= 'A' && id_str[i] <= 'Z')
|
||||||
|
continue;
|
||||||
|
if (id_str[i] >= '0' && id_str[i] <= '9')
|
||||||
|
continue;
|
||||||
|
if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
|
||||||
|
id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
|
||||||
|
id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
|
||||||
|
id_str[i] == '=' || id_str[i] == ' ')
|
||||||
|
continue;
|
||||||
|
wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bin_clear_free(hapd->tmp_eap_user.identity,
|
||||||
|
hapd->tmp_eap_user.identity_len);
|
||||||
|
bin_clear_free(hapd->tmp_eap_user.password,
|
||||||
|
hapd->tmp_eap_user.password_len);
|
||||||
|
os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
|
||||||
|
hapd->tmp_eap_user.phase2 = phase2;
|
||||||
|
hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
|
||||||
|
if (hapd->tmp_eap_user.identity == NULL)
|
||||||
|
return NULL;
|
||||||
|
os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
|
||||||
|
hapd->tmp_eap_user.identity_len = identity_len;
|
||||||
|
|
||||||
|
if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
|
||||||
|
wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
|
||||||
|
hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
|
||||||
|
sqlite3_close(db);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = os_snprintf(cmd, sizeof(cmd),
|
||||||
|
"SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
|
||||||
|
id_str, phase2);
|
||||||
|
if (os_snprintf_error(sizeof(cmd), res))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
|
||||||
|
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
|
||||||
|
SQLITE_OK) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DB: Failed to complete SQL operation: %s db: %s",
|
||||||
|
sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
|
||||||
|
} else if (hapd->tmp_eap_user.next)
|
||||||
|
user = &hapd->tmp_eap_user;
|
||||||
|
|
||||||
|
if (user == NULL && !phase2) {
|
||||||
|
os_snprintf(cmd, sizeof(cmd),
|
||||||
|
"SELECT identity,methods FROM wildcards;");
|
||||||
|
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
|
||||||
|
if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
|
||||||
|
NULL) != SQLITE_OK) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DB: Failed to complete SQL operation: %s db: %s",
|
||||||
|
sqlite3_errmsg(db),
|
||||||
|
hapd->conf->eap_user_sqlite);
|
||||||
|
} else if (hapd->tmp_eap_user.next) {
|
||||||
|
user = &hapd->tmp_eap_user;
|
||||||
|
os_free(user->identity);
|
||||||
|
user->identity = user->password;
|
||||||
|
user->identity_len = user->password_len;
|
||||||
|
user->password = NULL;
|
||||||
|
user->password_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
sqlite3_close(db);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_SQLITE */
|
||||||
|
|
||||||
|
|
||||||
|
const struct hostapd_eap_user *
|
||||||
|
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
|
||||||
|
size_t identity_len, int phase2)
|
||||||
|
{
|
||||||
|
const struct hostapd_bss_config *conf = hapd->conf;
|
||||||
|
struct hostapd_eap_user *user = conf->eap_user;
|
||||||
|
|
||||||
|
#ifdef CONFIG_WPS
|
||||||
|
if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
|
||||||
|
os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
|
||||||
|
static struct hostapd_eap_user wsc_enrollee;
|
||||||
|
os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
|
||||||
|
wsc_enrollee.methods[0].method = eap_server_get_type(
|
||||||
|
"WSC", &wsc_enrollee.methods[0].vendor);
|
||||||
|
return &wsc_enrollee;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
|
||||||
|
os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
|
||||||
|
static struct hostapd_eap_user wsc_registrar;
|
||||||
|
os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
|
||||||
|
wsc_registrar.methods[0].method = eap_server_get_type(
|
||||||
|
"WSC", &wsc_registrar.methods[0].vendor);
|
||||||
|
wsc_registrar.password = (u8 *) conf->ap_pin;
|
||||||
|
wsc_registrar.password_len = conf->ap_pin ?
|
||||||
|
os_strlen(conf->ap_pin) : 0;
|
||||||
|
return &wsc_registrar;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_WPS */
|
||||||
|
|
||||||
|
while (user) {
|
||||||
|
if (!phase2 && user->identity == NULL) {
|
||||||
|
/* Wildcard match */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user->phase2 == !!phase2 && user->wildcard_prefix &&
|
||||||
|
identity_len >= user->identity_len &&
|
||||||
|
os_memcmp(user->identity, identity, user->identity_len) ==
|
||||||
|
0) {
|
||||||
|
/* Wildcard prefix match */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user->phase2 == !!phase2 &&
|
||||||
|
user->identity_len == identity_len &&
|
||||||
|
os_memcmp(user->identity, identity, identity_len) == 0)
|
||||||
|
break;
|
||||||
|
user = user->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SQLITE
|
||||||
|
if (user == NULL && conf->eap_user_sqlite) {
|
||||||
|
return eap_user_sqlite_get(hapd, identity, identity_len,
|
||||||
|
phase2);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_SQLITE */
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
191
hostapd-2.9/src/ap/eth_p_oui.c
Normal file
191
hostapd-2.9/src/ap/eth_p_oui.c
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802 OUI Extended EtherType 88-B7
|
||||||
|
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "l2_packet/l2_packet.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "eth_p_oui.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
|
||||||
|
* EtherType 88-B7. This file implements this with OUI 00:13:74 and
|
||||||
|
* vendor-specific subtype 0x0001.
|
||||||
|
*/
|
||||||
|
static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
|
||||||
|
|
||||||
|
struct eth_p_oui_iface {
|
||||||
|
struct dl_list list;
|
||||||
|
char ifname[IFNAMSIZ + 1];
|
||||||
|
struct l2_packet_data *l2;
|
||||||
|
struct dl_list receiver;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct eth_p_oui_ctx {
|
||||||
|
struct dl_list list;
|
||||||
|
struct eth_p_oui_iface *iface;
|
||||||
|
/* all data needed to deliver and unregister */
|
||||||
|
u8 oui_suffix; /* last byte of OUI */
|
||||||
|
void (*rx_callback)(void *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, u8 oui_suffix,
|
||||||
|
const u8 *buf, size_t len);
|
||||||
|
void *rx_callback_ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, const u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
|
||||||
|
ctx->oui_suffix, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct eth_p_oui_iface *iface = ctx;
|
||||||
|
struct eth_p_oui_ctx *receiver;
|
||||||
|
const struct l2_ethhdr *ethhdr;
|
||||||
|
|
||||||
|
if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
|
||||||
|
/* too short packet */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ethhdr = (struct l2_ethhdr *) buf;
|
||||||
|
/* trim eth_hdr from buf and len */
|
||||||
|
buf += sizeof(*ethhdr);
|
||||||
|
len -= sizeof(*ethhdr);
|
||||||
|
|
||||||
|
/* verify OUI and vendor-specific subtype match */
|
||||||
|
if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
|
||||||
|
return;
|
||||||
|
buf += sizeof(global_oui);
|
||||||
|
len -= sizeof(global_oui);
|
||||||
|
|
||||||
|
dl_list_for_each(receiver, &iface->receiver,
|
||||||
|
struct eth_p_oui_ctx, list) {
|
||||||
|
if (buf[0] != receiver->oui_suffix)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
|
||||||
|
buf + 1, len - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct eth_p_oui_ctx *
|
||||||
|
eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
|
||||||
|
void (*rx_callback)(void *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, u8 oui_suffix,
|
||||||
|
const u8 *buf, size_t len),
|
||||||
|
void *rx_callback_ctx)
|
||||||
|
{
|
||||||
|
struct eth_p_oui_iface *iface;
|
||||||
|
struct eth_p_oui_ctx *receiver;
|
||||||
|
int found = 0;
|
||||||
|
struct hapd_interfaces *interfaces;
|
||||||
|
|
||||||
|
receiver = os_zalloc(sizeof(*receiver));
|
||||||
|
if (!receiver)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
receiver->oui_suffix = oui_suffix;
|
||||||
|
receiver->rx_callback = rx_callback;
|
||||||
|
receiver->rx_callback_ctx = rx_callback_ctx;
|
||||||
|
|
||||||
|
interfaces = hapd->iface->interfaces;
|
||||||
|
|
||||||
|
dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
|
||||||
|
list) {
|
||||||
|
if (os_strcmp(iface->ifname, ifname) != 0)
|
||||||
|
continue;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
iface = os_zalloc(sizeof(*iface));
|
||||||
|
if (!iface)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
|
||||||
|
iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
|
||||||
|
iface, 1);
|
||||||
|
if (!iface->l2) {
|
||||||
|
os_free(iface);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
dl_list_init(&iface->receiver);
|
||||||
|
|
||||||
|
dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
dl_list_add_tail(&iface->receiver, &receiver->list);
|
||||||
|
receiver->iface = iface;
|
||||||
|
|
||||||
|
return receiver;
|
||||||
|
err:
|
||||||
|
os_free(receiver);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct eth_p_oui_iface *iface;
|
||||||
|
|
||||||
|
if (!ctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
iface = ctx->iface;
|
||||||
|
|
||||||
|
dl_list_del(&ctx->list);
|
||||||
|
os_free(ctx);
|
||||||
|
|
||||||
|
if (dl_list_empty(&iface->receiver)) {
|
||||||
|
dl_list_del(&iface->list);
|
||||||
|
l2_packet_deinit(iface->l2);
|
||||||
|
os_free(iface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, const u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct eth_p_oui_iface *iface = ctx->iface;
|
||||||
|
u8 *packet, *p;
|
||||||
|
size_t packet_len;
|
||||||
|
int ret;
|
||||||
|
struct l2_ethhdr *ethhdr;
|
||||||
|
|
||||||
|
packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
|
||||||
|
packet = os_zalloc(packet_len);
|
||||||
|
if (!packet)
|
||||||
|
return -1;
|
||||||
|
p = packet;
|
||||||
|
|
||||||
|
ethhdr = (struct l2_ethhdr *) packet;
|
||||||
|
os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
|
||||||
|
os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
|
||||||
|
ethhdr->h_proto = host_to_be16(ETH_P_OUI);
|
||||||
|
p += sizeof(*ethhdr);
|
||||||
|
|
||||||
|
os_memcpy(p, global_oui, sizeof(global_oui));
|
||||||
|
p[sizeof(global_oui)] = ctx->oui_suffix;
|
||||||
|
p += sizeof(global_oui) + 1;
|
||||||
|
|
||||||
|
os_memcpy(p, buf, len);
|
||||||
|
|
||||||
|
ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
|
||||||
|
os_free(packet);
|
||||||
|
return ret;
|
||||||
|
}
|
28
hostapd-2.9/src/ap/eth_p_oui.h
Normal file
28
hostapd-2.9/src/ap/eth_p_oui.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802 OUI Extended Ethertype
|
||||||
|
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ETH_P_OUI_H
|
||||||
|
#define ETH_P_OUI_H
|
||||||
|
|
||||||
|
struct eth_p_oui_ctx;
|
||||||
|
struct hostapd_data;
|
||||||
|
|
||||||
|
/* rx_callback only gets payload after OUI passed as buf */
|
||||||
|
struct eth_p_oui_ctx *
|
||||||
|
eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
|
||||||
|
void (*rx_callback)(void *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, u8 oui_suffix,
|
||||||
|
const u8 *buf, size_t len),
|
||||||
|
void *rx_callback_ctx);
|
||||||
|
void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
|
||||||
|
int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, const u8 *buf, size_t len);
|
||||||
|
void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||||
|
const u8 *dst_addr, const u8 *buf, size_t len);
|
||||||
|
|
||||||
|
#endif /* ETH_P_OUI_H */
|
654
hostapd-2.9/src/ap/fils_hlp.c
Normal file
654
hostapd-2.9/src/ap/fils_hlp.c
Normal file
|
@ -0,0 +1,654 @@
|
||||||
|
/*
|
||||||
|
* FILS HLP request processing
|
||||||
|
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "common/dhcp.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "fils_hlp.h"
|
||||||
|
|
||||||
|
|
||||||
|
static be16 ip_checksum(const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
u32 sum = 0;
|
||||||
|
const u16 *pos;
|
||||||
|
|
||||||
|
for (pos = buf; len >= 2; len -= 2)
|
||||||
|
sum += ntohs(*pos++);
|
||||||
|
if (len)
|
||||||
|
sum += ntohs(*pos << 8);
|
||||||
|
|
||||||
|
sum = (sum >> 16) + (sum & 0xffff);
|
||||||
|
sum += sum >> 16;
|
||||||
|
return htons(~sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
|
||||||
|
{
|
||||||
|
u8 *pos, *end;
|
||||||
|
struct dhcp_data *dhcp;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
ssize_t res;
|
||||||
|
const u8 *server_id = NULL;
|
||||||
|
|
||||||
|
if (!sta->hlp_dhcp_discover) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: No pending HLP DHCPDISCOVER available");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert to DHCPREQUEST, remove rapid commit option, replace requested
|
||||||
|
* IP address option with yiaddr. */
|
||||||
|
pos = wpabuf_mhead(sta->hlp_dhcp_discover);
|
||||||
|
end = pos + wpabuf_len(sta->hlp_dhcp_discover);
|
||||||
|
dhcp = (struct dhcp_data *) pos;
|
||||||
|
pos = (u8 *) (dhcp + 1);
|
||||||
|
pos += 4; /* skip magic */
|
||||||
|
while (pos < end && *pos != DHCP_OPT_END) {
|
||||||
|
u8 opt, olen;
|
||||||
|
|
||||||
|
opt = *pos++;
|
||||||
|
if (opt == DHCP_OPT_PAD)
|
||||||
|
continue;
|
||||||
|
if (pos >= end)
|
||||||
|
break;
|
||||||
|
olen = *pos++;
|
||||||
|
if (olen > end - pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case DHCP_OPT_MSG_TYPE:
|
||||||
|
if (olen > 0)
|
||||||
|
*pos = DHCPREQUEST;
|
||||||
|
break;
|
||||||
|
case DHCP_OPT_RAPID_COMMIT:
|
||||||
|
case DHCP_OPT_REQUESTED_IP_ADDRESS:
|
||||||
|
case DHCP_OPT_SERVER_ID:
|
||||||
|
/* Remove option */
|
||||||
|
pos -= 2;
|
||||||
|
os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
|
||||||
|
end -= 2 + olen;
|
||||||
|
olen = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += olen;
|
||||||
|
}
|
||||||
|
if (pos >= end || *pos != DHCP_OPT_END) {
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
|
||||||
|
|
||||||
|
/* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
|
||||||
|
pos = (u8 *) (dhcpoffer + 1);
|
||||||
|
end = dhcpofferend;
|
||||||
|
pos += 4; /* skip magic */
|
||||||
|
while (pos < end && *pos != DHCP_OPT_END) {
|
||||||
|
u8 opt, olen;
|
||||||
|
|
||||||
|
opt = *pos++;
|
||||||
|
if (opt == DHCP_OPT_PAD)
|
||||||
|
continue;
|
||||||
|
if (pos >= end)
|
||||||
|
break;
|
||||||
|
olen = *pos++;
|
||||||
|
if (olen > end - pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case DHCP_OPT_SERVER_ID:
|
||||||
|
server_id = pos - 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += olen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wpabuf_resize(&sta->hlp_dhcp_discover,
|
||||||
|
6 + 1 + (server_id ? 2 + server_id[1] : 0)))
|
||||||
|
return -1;
|
||||||
|
if (server_id)
|
||||||
|
wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
|
||||||
|
2 + server_id[1]);
|
||||||
|
wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
|
||||||
|
wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
|
||||||
|
wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
|
||||||
|
wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
|
||||||
|
|
||||||
|
os_memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||||
|
addr.sin_port = htons(hapd->conf->dhcp_server_port);
|
||||||
|
res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
|
||||||
|
wpabuf_len(sta->hlp_dhcp_discover), 0,
|
||||||
|
(const struct sockaddr *) &addr, sizeof(addr));
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: Acting as DHCP rapid commit proxy for %s:%d",
|
||||||
|
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||||
|
wpabuf_free(sta->hlp_dhcp_discover);
|
||||||
|
sta->hlp_dhcp_discover = NULL;
|
||||||
|
sta->fils_dhcp_rapid_commit_proxy = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = sock_ctx;
|
||||||
|
struct sta_info *sta;
|
||||||
|
u8 buf[1500], *pos, *end, *end_opt = NULL;
|
||||||
|
struct dhcp_data *dhcp;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
socklen_t addr_len;
|
||||||
|
ssize_t res;
|
||||||
|
u8 msgtype = 0;
|
||||||
|
int rapid_commit = 0;
|
||||||
|
struct iphdr *iph;
|
||||||
|
struct udphdr *udph;
|
||||||
|
struct wpabuf *resp;
|
||||||
|
const u8 *rpos;
|
||||||
|
size_t left, len;
|
||||||
|
|
||||||
|
addr_len = sizeof(addr);
|
||||||
|
res = recvfrom(sd, buf, sizeof(buf), 0,
|
||||||
|
(struct sockaddr *) &addr, &addr_len);
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
|
||||||
|
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
|
||||||
|
wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
|
||||||
|
if ((size_t) res < sizeof(*dhcp))
|
||||||
|
return;
|
||||||
|
dhcp = (struct dhcp_data *) buf;
|
||||||
|
if (dhcp->op != 2)
|
||||||
|
return; /* Not a BOOTREPLY */
|
||||||
|
if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP - DHCP response to unknown relay address 0x%x",
|
||||||
|
dhcp->relay_ip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dhcp->relay_ip = 0;
|
||||||
|
pos = (u8 *) (dhcp + 1);
|
||||||
|
end = &buf[res];
|
||||||
|
|
||||||
|
if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos += 4;
|
||||||
|
|
||||||
|
wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
|
||||||
|
pos, end - pos);
|
||||||
|
while (pos < end && *pos != DHCP_OPT_END) {
|
||||||
|
u8 opt, olen;
|
||||||
|
|
||||||
|
opt = *pos++;
|
||||||
|
if (opt == DHCP_OPT_PAD)
|
||||||
|
continue;
|
||||||
|
if (pos >= end)
|
||||||
|
break;
|
||||||
|
olen = *pos++;
|
||||||
|
if (olen > end - pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case DHCP_OPT_MSG_TYPE:
|
||||||
|
if (olen > 0)
|
||||||
|
msgtype = pos[0];
|
||||||
|
break;
|
||||||
|
case DHCP_OPT_RAPID_COMMIT:
|
||||||
|
rapid_commit = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += olen;
|
||||||
|
}
|
||||||
|
if (pos < end && *pos == DHCP_OPT_END)
|
||||||
|
end_opt = pos;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
|
||||||
|
MACSTR ")",
|
||||||
|
msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, dhcp->hw_addr);
|
||||||
|
if (!sta || !sta->fils_pending_assoc_req) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: No pending HLP DHCP exchange with hw_addr "
|
||||||
|
MACSTR, MAC2STR(dhcp->hw_addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
|
||||||
|
!rapid_commit) {
|
||||||
|
/* Use hostapd to take care of 4-message exchange and convert
|
||||||
|
* the final DHCPACK to rapid commit version. */
|
||||||
|
if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
|
||||||
|
return;
|
||||||
|
/* failed, so send the server response as-is */
|
||||||
|
} else if (msgtype != DHCPACK) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
|
||||||
|
sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
|
||||||
|
if (!resp)
|
||||||
|
return;
|
||||||
|
wpabuf_put_data(resp, sta->addr, ETH_ALEN);
|
||||||
|
wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
|
||||||
|
wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
|
||||||
|
wpabuf_put_be16(resp, ETH_P_IP);
|
||||||
|
iph = wpabuf_put(resp, sizeof(*iph));
|
||||||
|
iph->version = 4;
|
||||||
|
iph->ihl = sizeof(*iph) / 4;
|
||||||
|
iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
|
||||||
|
iph->ttl = 1;
|
||||||
|
iph->protocol = 17; /* UDP */
|
||||||
|
iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||||
|
iph->daddr = dhcp->client_ip;
|
||||||
|
iph->check = ip_checksum(iph, sizeof(*iph));
|
||||||
|
udph = wpabuf_put(resp, sizeof(*udph));
|
||||||
|
udph->uh_sport = htons(DHCP_SERVER_PORT);
|
||||||
|
udph->uh_dport = htons(DHCP_CLIENT_PORT);
|
||||||
|
udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
|
||||||
|
udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
|
||||||
|
if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
|
||||||
|
!rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
|
||||||
|
/* Add rapid commit option */
|
||||||
|
wpabuf_put_data(resp, pos, end_opt - pos);
|
||||||
|
wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
|
||||||
|
wpabuf_put_u8(resp, 0);
|
||||||
|
wpabuf_put_data(resp, end_opt, end - end_opt);
|
||||||
|
} else {
|
||||||
|
wpabuf_put_data(resp, pos, end - pos);
|
||||||
|
}
|
||||||
|
if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
|
||||||
|
2 * wpabuf_len(resp) / 255 + 100)) {
|
||||||
|
wpabuf_free(resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpos = wpabuf_head(resp);
|
||||||
|
left = wpabuf_len(resp);
|
||||||
|
|
||||||
|
wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
|
||||||
|
if (left <= 254)
|
||||||
|
len = 1 + left;
|
||||||
|
else
|
||||||
|
len = 255;
|
||||||
|
wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
|
||||||
|
/* Element ID Extension */
|
||||||
|
wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
|
||||||
|
/* Destination MAC Address, Source MAC Address, HLP Packet.
|
||||||
|
* HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
|
||||||
|
* when LPD is used). */
|
||||||
|
wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
|
||||||
|
rpos += len - 1;
|
||||||
|
left -= len - 1;
|
||||||
|
while (left) {
|
||||||
|
wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
|
||||||
|
len = left > 255 ? 255 : left;
|
||||||
|
wpabuf_put_u8(sta->fils_hlp_resp, len);
|
||||||
|
wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
|
||||||
|
rpos += len;
|
||||||
|
left -= len;
|
||||||
|
}
|
||||||
|
wpabuf_free(resp);
|
||||||
|
|
||||||
|
if (sta->fils_drv_assoc_finish)
|
||||||
|
hostapd_notify_assoc_fils_finish(hapd, sta);
|
||||||
|
else
|
||||||
|
fils_hlp_finish_assoc(hapd, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
const u8 *msg, size_t len)
|
||||||
|
{
|
||||||
|
const struct dhcp_data *dhcp;
|
||||||
|
struct wpabuf *dhcp_buf;
|
||||||
|
struct dhcp_data *dhcp_msg;
|
||||||
|
u8 msgtype = 0;
|
||||||
|
int rapid_commit = 0;
|
||||||
|
const u8 *pos = msg, *end;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
|
if (len < sizeof(*dhcp))
|
||||||
|
return 0;
|
||||||
|
dhcp = (const struct dhcp_data *) pos;
|
||||||
|
end = pos + len;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
|
||||||
|
dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
|
||||||
|
ntohl(dhcp->xid));
|
||||||
|
pos += sizeof(*dhcp);
|
||||||
|
if (dhcp->op != 1)
|
||||||
|
return 0; /* Not a BOOTREQUEST */
|
||||||
|
|
||||||
|
if (end - pos < 4)
|
||||||
|
return 0;
|
||||||
|
if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pos += 4;
|
||||||
|
|
||||||
|
wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
|
||||||
|
while (pos < end && *pos != DHCP_OPT_END) {
|
||||||
|
u8 opt, olen;
|
||||||
|
|
||||||
|
opt = *pos++;
|
||||||
|
if (opt == DHCP_OPT_PAD)
|
||||||
|
continue;
|
||||||
|
if (pos >= end)
|
||||||
|
break;
|
||||||
|
olen = *pos++;
|
||||||
|
if (olen > end - pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case DHCP_OPT_MSG_TYPE:
|
||||||
|
if (olen > 0)
|
||||||
|
msgtype = pos[0];
|
||||||
|
break;
|
||||||
|
case DHCP_OPT_RAPID_COMMIT:
|
||||||
|
rapid_commit = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += olen;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
|
||||||
|
if (msgtype != DHCPDISCOVER)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (hapd->conf->dhcp_server.af != AF_INET ||
|
||||||
|
hapd->conf->dhcp_server.u.v4.s_addr == 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP - no DHCPv4 server configured - drop request");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->own_ip_addr.af != AF_INET ||
|
||||||
|
hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP - no IPv4 own_ip_addr configured - drop request");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->dhcp_sock < 0) {
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (s < 0) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"FILS: Failed to open DHCP socket: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->dhcp_relay_port) {
|
||||||
|
os_memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr =
|
||||||
|
hapd->conf->own_ip_addr.u.v4.s_addr;
|
||||||
|
addr.sin_port = htons(hapd->conf->dhcp_relay_port);
|
||||||
|
if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"FILS: Failed to bind DHCP socket: %s",
|
||||||
|
strerror(errno));
|
||||||
|
close(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (eloop_register_sock(s, EVENT_TYPE_READ,
|
||||||
|
fils_dhcp_handler, NULL, hapd)) {
|
||||||
|
close(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hapd->dhcp_sock = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhcp_buf = wpabuf_alloc(len);
|
||||||
|
if (!dhcp_buf)
|
||||||
|
return 0;
|
||||||
|
dhcp_msg = wpabuf_put(dhcp_buf, len);
|
||||||
|
os_memcpy(dhcp_msg, msg, len);
|
||||||
|
dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
|
||||||
|
os_memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||||
|
addr.sin_port = htons(hapd->conf->dhcp_server_port);
|
||||||
|
res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
|
||||||
|
(const struct sockaddr *) &addr, sizeof(addr));
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
wpabuf_free(dhcp_buf);
|
||||||
|
/* Close the socket to try to recover from error */
|
||||||
|
eloop_unregister_read_sock(hapd->dhcp_sock);
|
||||||
|
close(hapd->dhcp_sock);
|
||||||
|
hapd->dhcp_sock = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
|
||||||
|
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
|
||||||
|
rapid_commit);
|
||||||
|
if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
|
||||||
|
/* Store a copy of the DHCPDISCOVER for rapid commit proxying
|
||||||
|
* purposes if the server does not support the rapid commit
|
||||||
|
* option. */
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: Store DHCPDISCOVER for rapid commit proxy");
|
||||||
|
wpabuf_free(sta->hlp_dhcp_discover);
|
||||||
|
sta->hlp_dhcp_discover = dhcp_buf;
|
||||||
|
} else {
|
||||||
|
wpabuf_free(dhcp_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fils_process_hlp_udp(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, const u8 *dst,
|
||||||
|
const u8 *pos, size_t len)
|
||||||
|
{
|
||||||
|
const struct iphdr *iph;
|
||||||
|
const struct udphdr *udph;
|
||||||
|
u16 sport, dport, ulen;
|
||||||
|
|
||||||
|
if (len < sizeof(*iph) + sizeof(*udph))
|
||||||
|
return 0;
|
||||||
|
iph = (const struct iphdr *) pos;
|
||||||
|
udph = (const struct udphdr *) (iph + 1);
|
||||||
|
sport = ntohs(udph->uh_sport);
|
||||||
|
dport = ntohs(udph->uh_dport);
|
||||||
|
ulen = ntohs(udph->uh_ulen);
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
|
||||||
|
sport, dport, ulen, ntohs(udph->uh_sum));
|
||||||
|
/* TODO: Check UDP checksum */
|
||||||
|
if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
|
||||||
|
return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
|
||||||
|
ulen - sizeof(*udph));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fils_process_hlp_ip(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, const u8 *dst,
|
||||||
|
const u8 *pos, size_t len)
|
||||||
|
{
|
||||||
|
const struct iphdr *iph;
|
||||||
|
u16 tot_len;
|
||||||
|
|
||||||
|
if (len < sizeof(*iph))
|
||||||
|
return 0;
|
||||||
|
iph = (const struct iphdr *) pos;
|
||||||
|
if (ip_checksum(iph, sizeof(*iph)) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP request IPv4 packet had invalid header checksum - dropped");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
tot_len = ntohs(iph->tot_len);
|
||||||
|
if (tot_len > len)
|
||||||
|
return 0;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
|
||||||
|
iph->saddr, iph->daddr, iph->protocol);
|
||||||
|
switch (iph->protocol) {
|
||||||
|
case 17:
|
||||||
|
return fils_process_hlp_udp(hapd, sta, dst, pos, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fils_process_hlp_req(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
const u8 *pos, size_t len)
|
||||||
|
{
|
||||||
|
const u8 *pkt, *end;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
|
||||||
|
" src=" MACSTR " len=%u)",
|
||||||
|
MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
|
||||||
|
(unsigned int) len);
|
||||||
|
if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: Ignore HLP request with unexpected source address"
|
||||||
|
MACSTR, MAC2STR(pos + ETH_ALEN));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = pos + len;
|
||||||
|
pkt = pos + 2 * ETH_ALEN;
|
||||||
|
if (end - pkt >= 6 &&
|
||||||
|
os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
|
||||||
|
pkt += 6; /* Remove SNAP/LLC header */
|
||||||
|
wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
|
||||||
|
|
||||||
|
if (end - pkt < 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (WPA_GET_BE16(pkt)) {
|
||||||
|
case ETH_P_IP:
|
||||||
|
return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
|
||||||
|
end - pkt - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *pos, int left)
|
||||||
|
{
|
||||||
|
const u8 *end = pos + left;
|
||||||
|
u8 *tmp, *tmp_pos;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (sta->fils_pending_assoc_req &&
|
||||||
|
eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
|
||||||
|
/* Do not process FILS HLP request again if the station
|
||||||
|
* retransmits (Re)Association Request frame before the previous
|
||||||
|
* HLP response has either been received or timed out. */
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"FILS: Do not relay another HLP request from "
|
||||||
|
MACSTR
|
||||||
|
" before processing of the already pending one has been completed",
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Old DHCPDISCOVER is not needed anymore, if it was still pending */
|
||||||
|
wpabuf_free(sta->hlp_dhcp_discover);
|
||||||
|
sta->hlp_dhcp_discover = NULL;
|
||||||
|
sta->fils_dhcp_rapid_commit_proxy = 0;
|
||||||
|
|
||||||
|
/* Check if there are any FILS HLP Container elements */
|
||||||
|
while (end - pos >= 2) {
|
||||||
|
if (2 + pos[1] > end - pos)
|
||||||
|
return 0;
|
||||||
|
if (pos[0] == WLAN_EID_EXTENSION &&
|
||||||
|
pos[1] >= 1 + 2 * ETH_ALEN &&
|
||||||
|
pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
|
||||||
|
break;
|
||||||
|
pos += 2 + pos[1];
|
||||||
|
}
|
||||||
|
if (end - pos < 2)
|
||||||
|
return 0; /* No FILS HLP Container elements */
|
||||||
|
|
||||||
|
tmp = os_malloc(end - pos);
|
||||||
|
if (!tmp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (end - pos >= 2) {
|
||||||
|
if (2 + pos[1] > end - pos ||
|
||||||
|
pos[0] != WLAN_EID_EXTENSION ||
|
||||||
|
pos[1] < 1 + 2 * ETH_ALEN ||
|
||||||
|
pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
|
||||||
|
break;
|
||||||
|
tmp_pos = tmp;
|
||||||
|
os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
|
||||||
|
tmp_pos += pos[1] - 1;
|
||||||
|
pos += 2 + pos[1];
|
||||||
|
|
||||||
|
/* Add possible fragments */
|
||||||
|
while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
|
||||||
|
2 + pos[1] <= end - pos) {
|
||||||
|
os_memcpy(tmp_pos, pos + 2, pos[1]);
|
||||||
|
tmp_pos += pos[1];
|
||||||
|
pos += 2 + pos[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_free(tmp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fils_hlp_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (hapd->dhcp_sock >= 0) {
|
||||||
|
eloop_unregister_read_sock(hapd->dhcp_sock);
|
||||||
|
close(hapd->dhcp_sock);
|
||||||
|
hapd->dhcp_sock = -1;
|
||||||
|
}
|
||||||
|
}
|
27
hostapd-2.9/src/ap/fils_hlp.h
Normal file
27
hostapd-2.9/src/ap/fils_hlp.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* FILS HLP request processing
|
||||||
|
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILS_HLP_H
|
||||||
|
#define FILS_HLP_H
|
||||||
|
|
||||||
|
int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *pos, int left);
|
||||||
|
|
||||||
|
#ifdef CONFIG_FILS
|
||||||
|
|
||||||
|
void fils_hlp_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
#else /* CONFIG_FILS */
|
||||||
|
|
||||||
|
static inline void fils_hlp_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
|
#endif /* FILS_HLP_H */
|
714
hostapd-2.9/src/ap/gas_query_ap.c
Normal file
714
hostapd-2.9/src/ap/gas_query_ap.c
Normal file
|
@ -0,0 +1,714 @@
|
||||||
|
/*
|
||||||
|
* Generic advertisement service (GAS) query (hostapd)
|
||||||
|
* Copyright (c) 2009, Atheros Communications
|
||||||
|
* Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
|
||||||
|
* Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "utils/list.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/gas.h"
|
||||||
|
#include "common/wpa_ctrl.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "gas_query_ap.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** GAS query timeout in seconds */
|
||||||
|
#define GAS_QUERY_TIMEOUT_PERIOD 2
|
||||||
|
|
||||||
|
/* GAS query wait-time / duration in ms */
|
||||||
|
#define GAS_QUERY_WAIT_TIME_INITIAL 1000
|
||||||
|
#define GAS_QUERY_WAIT_TIME_COMEBACK 150
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct gas_query_pending - Pending GAS query
|
||||||
|
*/
|
||||||
|
struct gas_query_pending {
|
||||||
|
struct dl_list list;
|
||||||
|
struct gas_query_ap *gas;
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
u8 dialog_token;
|
||||||
|
u8 next_frag_id;
|
||||||
|
unsigned int wait_comeback:1;
|
||||||
|
unsigned int offchannel_tx_started:1;
|
||||||
|
unsigned int retry:1;
|
||||||
|
int freq;
|
||||||
|
u16 status_code;
|
||||||
|
struct wpabuf *req;
|
||||||
|
struct wpabuf *adv_proto;
|
||||||
|
struct wpabuf *resp;
|
||||||
|
struct os_reltime last_oper;
|
||||||
|
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
|
||||||
|
enum gas_query_ap_result result,
|
||||||
|
const struct wpabuf *adv_proto,
|
||||||
|
const struct wpabuf *resp, u16 status_code);
|
||||||
|
void *ctx;
|
||||||
|
u8 sa[ETH_ALEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct gas_query_ap - Internal GAS query data
|
||||||
|
*/
|
||||||
|
struct gas_query_ap {
|
||||||
|
struct hostapd_data *hapd;
|
||||||
|
void *msg_ctx;
|
||||||
|
struct dl_list pending; /* struct gas_query_pending */
|
||||||
|
struct gas_query_pending *current;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
|
||||||
|
static void gas_query_timeout(void *eloop_data, void *user_ctx);
|
||||||
|
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
|
||||||
|
static void gas_query_tx_initial_req(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query);
|
||||||
|
static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
|
||||||
|
|
||||||
|
|
||||||
|
static int ms_from_time(struct os_reltime *last)
|
||||||
|
{
|
||||||
|
struct os_reltime now, res;
|
||||||
|
|
||||||
|
os_get_reltime(&now);
|
||||||
|
os_reltime_sub(&now, last, &res);
|
||||||
|
return res.sec * 1000 + res.usec / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gas_query_ap_init - Initialize GAS query component
|
||||||
|
* @hapd: Pointer to hostapd data
|
||||||
|
* Returns: Pointer to GAS query data or %NULL on failure
|
||||||
|
*/
|
||||||
|
struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
|
||||||
|
void *msg_ctx)
|
||||||
|
{
|
||||||
|
struct gas_query_ap *gas;
|
||||||
|
|
||||||
|
gas = os_zalloc(sizeof(*gas));
|
||||||
|
if (!gas)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
gas->hapd = hapd;
|
||||||
|
gas->msg_ctx = msg_ctx;
|
||||||
|
dl_list_init(&gas->pending);
|
||||||
|
|
||||||
|
return gas;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char * gas_result_txt(enum gas_query_ap_result result)
|
||||||
|
{
|
||||||
|
switch (result) {
|
||||||
|
case GAS_QUERY_AP_SUCCESS:
|
||||||
|
return "SUCCESS";
|
||||||
|
case GAS_QUERY_AP_FAILURE:
|
||||||
|
return "FAILURE";
|
||||||
|
case GAS_QUERY_AP_TIMEOUT:
|
||||||
|
return "TIMEOUT";
|
||||||
|
case GAS_QUERY_AP_PEER_ERROR:
|
||||||
|
return "PEER_ERROR";
|
||||||
|
case GAS_QUERY_AP_INTERNAL_ERROR:
|
||||||
|
return "INTERNAL_ERROR";
|
||||||
|
case GAS_QUERY_AP_DELETED_AT_DEINIT:
|
||||||
|
return "DELETED_AT_DEINIT";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_free(struct gas_query_pending *query, int del_list)
|
||||||
|
{
|
||||||
|
if (del_list)
|
||||||
|
dl_list_del(&query->list);
|
||||||
|
|
||||||
|
wpabuf_free(query->req);
|
||||||
|
wpabuf_free(query->adv_proto);
|
||||||
|
wpabuf_free(query->resp);
|
||||||
|
os_free(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_done(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query,
|
||||||
|
enum gas_query_ap_result result)
|
||||||
|
{
|
||||||
|
wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
|
||||||
|
" dialog_token=%u freq=%d status_code=%u result=%s",
|
||||||
|
MAC2STR(query->addr), query->dialog_token, query->freq,
|
||||||
|
query->status_code, gas_result_txt(result));
|
||||||
|
if (gas->current == query)
|
||||||
|
gas->current = NULL;
|
||||||
|
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
|
||||||
|
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||||
|
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
|
||||||
|
dl_list_del(&query->list);
|
||||||
|
query->cb(query->ctx, query->addr, query->dialog_token, result,
|
||||||
|
query->adv_proto, query->resp, query->status_code);
|
||||||
|
gas_query_free(query, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gas_query_ap_deinit - Deinitialize GAS query component
|
||||||
|
* @gas: GAS query data from gas_query_init()
|
||||||
|
*/
|
||||||
|
void gas_query_ap_deinit(struct gas_query_ap *gas)
|
||||||
|
{
|
||||||
|
struct gas_query_pending *query, *next;
|
||||||
|
|
||||||
|
if (gas == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dl_list_for_each_safe(query, next, &gas->pending,
|
||||||
|
struct gas_query_pending, list)
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
|
||||||
|
|
||||||
|
os_free(gas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct gas_query_pending *
|
||||||
|
gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
|
||||||
|
{
|
||||||
|
struct gas_query_pending *q;
|
||||||
|
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
|
||||||
|
if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
|
||||||
|
q->dialog_token == dialog_token)
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int gas_query_append(struct gas_query_pending *query, const u8 *data,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
if (wpabuf_resize(&query->resp, len) < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
wpabuf_put_data(query->resp, data, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
|
||||||
|
const u8 *data, size_t data_len, int ok)
|
||||||
|
{
|
||||||
|
struct gas_query_pending *query;
|
||||||
|
int dur;
|
||||||
|
|
||||||
|
if (!gas || !gas->current) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
|
||||||
|
" ok=%d - no query in progress", MAC2STR(dst), ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = gas->current;
|
||||||
|
|
||||||
|
dur = ms_from_time(&query->last_oper);
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
|
||||||
|
" ok=%d query=%p dialog_token=%u dur=%d ms",
|
||||||
|
MAC2STR(dst), ok, query, query->dialog_token, dur);
|
||||||
|
if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
os_get_reltime(&query->last_oper);
|
||||||
|
|
||||||
|
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||||
|
if (!ok) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
|
||||||
|
eloop_register_timeout(0, 250000, gas_query_timeout,
|
||||||
|
gas, query);
|
||||||
|
} else {
|
||||||
|
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
|
||||||
|
gas_query_timeout, gas, query);
|
||||||
|
}
|
||||||
|
if (query->wait_comeback && !query->retry) {
|
||||||
|
eloop_cancel_timeout(gas_query_rx_comeback_timeout,
|
||||||
|
gas, query);
|
||||||
|
eloop_register_timeout(
|
||||||
|
0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
|
||||||
|
gas_query_rx_comeback_timeout, gas, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
|
||||||
|
{
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
return sta && (sta->flags & WLAN_STA_MFP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int gas_query_tx(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query,
|
||||||
|
struct wpabuf *req, unsigned int wait_time)
|
||||||
|
{
|
||||||
|
int res, prot = pmf_in_use(gas->hapd, query->addr);
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
|
||||||
|
"freq=%d prot=%d using src addr " MACSTR,
|
||||||
|
MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
|
||||||
|
query->freq, prot, MAC2STR(query->sa));
|
||||||
|
if (prot) {
|
||||||
|
u8 *categ = wpabuf_mhead_u8(req);
|
||||||
|
*categ = WLAN_ACTION_PROTECTED_DUAL;
|
||||||
|
}
|
||||||
|
os_get_reltime(&query->last_oper);
|
||||||
|
res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
|
||||||
|
query->addr, wpabuf_head(req),
|
||||||
|
wpabuf_len(req));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query)
|
||||||
|
{
|
||||||
|
struct wpabuf *req;
|
||||||
|
unsigned int wait_time;
|
||||||
|
|
||||||
|
req = gas_build_comeback_req(query->dialog_token);
|
||||||
|
if (req == NULL) {
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_time = (query->retry || !query->offchannel_tx_started) ?
|
||||||
|
GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
|
||||||
|
|
||||||
|
if (gas_query_tx(gas, query, req, wait_time) < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
|
||||||
|
MACSTR, MAC2STR(query->addr));
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpabuf_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
|
||||||
|
{
|
||||||
|
struct gas_query_ap *gas = eloop_data;
|
||||||
|
struct gas_query_pending *query = user_ctx;
|
||||||
|
int dialog_token;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"GAS: No response to comeback request received (retry=%u)",
|
||||||
|
query->retry);
|
||||||
|
if (gas->current != query || query->retry)
|
||||||
|
return;
|
||||||
|
dialog_token = gas_query_new_dialog_token(gas, query->addr);
|
||||||
|
if (dialog_token < 0)
|
||||||
|
return;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"GAS: Retry GAS query due to comeback response timeout");
|
||||||
|
query->retry = 1;
|
||||||
|
query->dialog_token = dialog_token;
|
||||||
|
*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
|
||||||
|
query->wait_comeback = 0;
|
||||||
|
query->next_frag_id = 0;
|
||||||
|
wpabuf_free(query->adv_proto);
|
||||||
|
query->adv_proto = NULL;
|
||||||
|
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
|
||||||
|
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||||
|
gas_query_tx_initial_req(gas, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
|
||||||
|
{
|
||||||
|
struct gas_query_ap *gas = eloop_data;
|
||||||
|
struct gas_query_pending *query = user_ctx;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
|
||||||
|
MAC2STR(query->addr));
|
||||||
|
gas_query_tx_comeback_req(gas, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query,
|
||||||
|
u16 comeback_delay)
|
||||||
|
{
|
||||||
|
unsigned int secs, usecs;
|
||||||
|
|
||||||
|
secs = (comeback_delay * 1024) / 1000000;
|
||||||
|
usecs = comeback_delay * 1024 - secs * 1000000;
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
|
||||||
|
" in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
|
||||||
|
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
|
||||||
|
eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
|
||||||
|
gas, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_rx_initial(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query,
|
||||||
|
const u8 *adv_proto, const u8 *resp,
|
||||||
|
size_t len, u16 comeback_delay)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
|
||||||
|
MACSTR " (dialog_token=%u comeback_delay=%u)",
|
||||||
|
MAC2STR(query->addr), query->dialog_token, comeback_delay);
|
||||||
|
|
||||||
|
query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
|
||||||
|
if (query->adv_proto == NULL) {
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comeback_delay) {
|
||||||
|
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||||
|
query->wait_comeback = 1;
|
||||||
|
gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query was completed without comeback mechanism */
|
||||||
|
if (gas_query_append(query, resp, len) < 0) {
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_rx_comeback(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query,
|
||||||
|
const u8 *adv_proto, const u8 *resp,
|
||||||
|
size_t len, u8 frag_id, u8 more_frags,
|
||||||
|
u16 comeback_delay)
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
|
||||||
|
MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
|
||||||
|
"comeback_delay=%u)",
|
||||||
|
MAC2STR(query->addr), query->dialog_token, frag_id,
|
||||||
|
more_frags, comeback_delay);
|
||||||
|
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
|
||||||
|
|
||||||
|
if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
|
||||||
|
os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
|
||||||
|
wpabuf_len(query->adv_proto)) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
|
||||||
|
"between initial and comeback response from "
|
||||||
|
MACSTR, MAC2STR(query->addr));
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comeback_delay) {
|
||||||
|
if (frag_id) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
|
||||||
|
"with non-zero frag_id and comeback_delay "
|
||||||
|
"from " MACSTR, MAC2STR(query->addr));
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frag_id != query->next_frag_id) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
|
||||||
|
"from " MACSTR, MAC2STR(query->addr));
|
||||||
|
if (frag_id + 1 == query->next_frag_id) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
|
||||||
|
"retry of previous fragment");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
query->next_frag_id++;
|
||||||
|
|
||||||
|
if (gas_query_append(query, resp, len) < 0) {
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (more_frags) {
|
||||||
|
gas_query_tx_comeback_req(gas, query);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
|
||||||
|
* frame
|
||||||
|
* @gas: GAS query data from gas_query_init()
|
||||||
|
* @sa: Source MAC address of the Action frame
|
||||||
|
* @categ: Category of the Action frame
|
||||||
|
* @data: Payload of the Action frame
|
||||||
|
* @len: Length of @data
|
||||||
|
* @freq: Frequency (in MHz) on which the frame was received
|
||||||
|
* Returns: 0 if the Public Action frame was a GAS frame or -1 if not
|
||||||
|
*/
|
||||||
|
int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
|
||||||
|
const u8 *data, size_t len, int freq)
|
||||||
|
{
|
||||||
|
struct gas_query_pending *query;
|
||||||
|
u8 action, dialog_token, frag_id = 0, more_frags = 0;
|
||||||
|
u16 comeback_delay, resp_len;
|
||||||
|
const u8 *pos, *adv_proto;
|
||||||
|
int prot, pmf;
|
||||||
|
unsigned int left;
|
||||||
|
|
||||||
|
if (!gas || len < 4)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = data;
|
||||||
|
action = *pos++;
|
||||||
|
dialog_token = *pos++;
|
||||||
|
|
||||||
|
if (action != WLAN_PA_GAS_INITIAL_RESP &&
|
||||||
|
action != WLAN_PA_GAS_COMEBACK_RESP)
|
||||||
|
return -1; /* Not a GAS response */
|
||||||
|
|
||||||
|
prot = categ == WLAN_ACTION_PROTECTED_DUAL;
|
||||||
|
pmf = pmf_in_use(gas->hapd, sa);
|
||||||
|
if (prot && !pmf) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!prot && pmf) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = gas_query_get_pending(gas, sa, dialog_token);
|
||||||
|
if (query == NULL) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
|
||||||
|
" dialog token %u", MAC2STR(sa), dialog_token);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
|
||||||
|
ms_from_time(&query->last_oper), MAC2STR(sa));
|
||||||
|
|
||||||
|
if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
|
||||||
|
MACSTR " dialog token %u when waiting for comeback "
|
||||||
|
"response", MAC2STR(sa), dialog_token);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
|
||||||
|
MACSTR " dialog token %u when waiting for initial "
|
||||||
|
"response", MAC2STR(sa), dialog_token);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
query->status_code = WPA_GET_LE16(pos);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
|
||||||
|
action == WLAN_PA_GAS_COMEBACK_RESP) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
|
||||||
|
} else if (query->status_code != WLAN_STATUS_SUCCESS) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
|
||||||
|
"%u failed - status code %u",
|
||||||
|
MAC2STR(sa), dialog_token, query->status_code);
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == WLAN_PA_GAS_COMEBACK_RESP) {
|
||||||
|
if (pos + 1 > data + len)
|
||||||
|
return 0;
|
||||||
|
frag_id = *pos & 0x7f;
|
||||||
|
more_frags = (*pos & 0x80) >> 7;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Comeback Delay */
|
||||||
|
if (pos + 2 > data + len)
|
||||||
|
return 0;
|
||||||
|
comeback_delay = WPA_GET_LE16(pos);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
/* Advertisement Protocol element */
|
||||||
|
if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
|
||||||
|
"Protocol element in the response from " MACSTR,
|
||||||
|
MAC2STR(sa));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pos != WLAN_EID_ADV_PROTO) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
|
||||||
|
"Protocol element ID %u in response from " MACSTR,
|
||||||
|
*pos, MAC2STR(sa));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
adv_proto = pos;
|
||||||
|
pos += 2 + pos[1];
|
||||||
|
|
||||||
|
/* Query Response Length */
|
||||||
|
if (pos + 2 > data + len) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
resp_len = WPA_GET_LE16(pos);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
left = data + len - pos;
|
||||||
|
if (resp_len > left) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
|
||||||
|
"response from " MACSTR, MAC2STR(sa));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp_len < left) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
|
||||||
|
"after Query Response from " MACSTR,
|
||||||
|
left - resp_len, MAC2STR(sa));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == WLAN_PA_GAS_COMEBACK_RESP)
|
||||||
|
gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
|
||||||
|
frag_id, more_frags, comeback_delay);
|
||||||
|
else
|
||||||
|
gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
|
||||||
|
comeback_delay);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_timeout(void *eloop_data, void *user_ctx)
|
||||||
|
{
|
||||||
|
struct gas_query_ap *gas = eloop_data;
|
||||||
|
struct gas_query_pending *query = user_ctx;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
|
||||||
|
" dialog token %u",
|
||||||
|
MAC2STR(query->addr), query->dialog_token);
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int gas_query_dialog_token_available(struct gas_query_ap *gas,
|
||||||
|
const u8 *dst, u8 dialog_token)
|
||||||
|
{
|
||||||
|
struct gas_query_pending *q;
|
||||||
|
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
|
||||||
|
if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
|
||||||
|
dialog_token == q->dialog_token)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_query_tx_initial_req(struct gas_query_ap *gas,
|
||||||
|
struct gas_query_pending *query)
|
||||||
|
{
|
||||||
|
if (gas_query_tx(gas, query, query->req,
|
||||||
|
GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
|
||||||
|
MACSTR, MAC2STR(query->addr));
|
||||||
|
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gas->current = query;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
|
||||||
|
query->dialog_token);
|
||||||
|
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
|
||||||
|
gas_query_timeout, gas, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
|
||||||
|
{
|
||||||
|
static int next_start = 0;
|
||||||
|
int dialog_token;
|
||||||
|
|
||||||
|
for (dialog_token = 0; dialog_token < 256; dialog_token++) {
|
||||||
|
if (gas_query_dialog_token_available(
|
||||||
|
gas, dst, (next_start + dialog_token) % 256))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dialog_token == 256)
|
||||||
|
return -1; /* Too many pending queries */
|
||||||
|
dialog_token = (next_start + dialog_token) % 256;
|
||||||
|
next_start = (dialog_token + 1) % 256;
|
||||||
|
return dialog_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gas_query_ap_req - Request a GAS query
|
||||||
|
* @gas: GAS query data from gas_query_init()
|
||||||
|
* @dst: Destination MAC address for the query
|
||||||
|
* @freq: Frequency (in MHz) for the channel on which to send the query
|
||||||
|
* @req: GAS query payload (to be freed by gas_query module in case of success
|
||||||
|
* return)
|
||||||
|
* @cb: Callback function for reporting GAS query result and response
|
||||||
|
* @ctx: Context pointer to use with the @cb call
|
||||||
|
* Returns: dialog token (>= 0) on success or -1 on failure
|
||||||
|
*/
|
||||||
|
int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
|
||||||
|
struct wpabuf *req,
|
||||||
|
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
|
||||||
|
enum gas_query_ap_result result,
|
||||||
|
const struct wpabuf *adv_proto,
|
||||||
|
const struct wpabuf *resp, u16 status_code),
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
struct gas_query_pending *query;
|
||||||
|
int dialog_token;
|
||||||
|
|
||||||
|
if (!gas || wpabuf_len(req) < 3)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
dialog_token = gas_query_new_dialog_token(gas, dst);
|
||||||
|
if (dialog_token < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
query = os_zalloc(sizeof(*query));
|
||||||
|
if (query == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
query->gas = gas;
|
||||||
|
os_memcpy(query->addr, dst, ETH_ALEN);
|
||||||
|
query->dialog_token = dialog_token;
|
||||||
|
query->freq = freq;
|
||||||
|
query->cb = cb;
|
||||||
|
query->ctx = ctx;
|
||||||
|
query->req = req;
|
||||||
|
dl_list_add(&gas->pending, &query->list);
|
||||||
|
|
||||||
|
*(wpabuf_mhead_u8(req) + 2) = dialog_token;
|
||||||
|
|
||||||
|
wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
|
||||||
|
" dialog_token=%u freq=%d",
|
||||||
|
MAC2STR(query->addr), query->dialog_token, query->freq);
|
||||||
|
|
||||||
|
gas_query_tx_initial_req(gas, query);
|
||||||
|
|
||||||
|
return dialog_token;
|
||||||
|
}
|
43
hostapd-2.9/src/ap/gas_query_ap.h
Normal file
43
hostapd-2.9/src/ap/gas_query_ap.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Generic advertisement service (GAS) query
|
||||||
|
* Copyright (c) 2009, Atheros Communications
|
||||||
|
* Copyright (c) 2011-2017, Qualcomm Atheros
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GAS_QUERY_AP_H
|
||||||
|
#define GAS_QUERY_AP_H
|
||||||
|
|
||||||
|
struct gas_query_ap;
|
||||||
|
|
||||||
|
struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
|
||||||
|
void *msg_ctx);
|
||||||
|
void gas_query_ap_deinit(struct gas_query_ap *gas);
|
||||||
|
int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
|
||||||
|
const u8 *data, size_t len, int freq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum gas_query_ap_result - GAS query result
|
||||||
|
*/
|
||||||
|
enum gas_query_ap_result {
|
||||||
|
GAS_QUERY_AP_SUCCESS,
|
||||||
|
GAS_QUERY_AP_FAILURE,
|
||||||
|
GAS_QUERY_AP_TIMEOUT,
|
||||||
|
GAS_QUERY_AP_PEER_ERROR,
|
||||||
|
GAS_QUERY_AP_INTERNAL_ERROR,
|
||||||
|
GAS_QUERY_AP_DELETED_AT_DEINIT
|
||||||
|
};
|
||||||
|
|
||||||
|
int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
|
||||||
|
struct wpabuf *req,
|
||||||
|
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
|
||||||
|
enum gas_query_ap_result result,
|
||||||
|
const struct wpabuf *adv_proto,
|
||||||
|
const struct wpabuf *resp, u16 status_code),
|
||||||
|
void *ctx);
|
||||||
|
void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
|
||||||
|
const u8 *data, size_t data_len, int ok);
|
||||||
|
|
||||||
|
#endif /* GAS_QUERY_AP_H */
|
1890
hostapd-2.9/src/ap/gas_serv.c
Normal file
1890
hostapd-2.9/src/ap/gas_serv.c
Normal file
File diff suppressed because it is too large
Load diff
95
hostapd-2.9/src/ap/gas_serv.h
Normal file
95
hostapd-2.9/src/ap/gas_serv.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Generic advertisement service (GAS) server
|
||||||
|
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GAS_SERV_H
|
||||||
|
#define GAS_SERV_H
|
||||||
|
|
||||||
|
/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
|
||||||
|
#define ANQP_REQ_CAPABILITY_LIST \
|
||||||
|
(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_VENUE_NAME \
|
||||||
|
(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
|
||||||
|
(1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_NETWORK_AUTH_TYPE \
|
||||||
|
(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_ROAMING_CONSORTIUM \
|
||||||
|
(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
|
||||||
|
(1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_NAI_REALM \
|
||||||
|
(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
|
||||||
|
(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
|
||||||
|
(1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_AP_CIVIC_LOCATION \
|
||||||
|
(1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
|
||||||
|
(1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_DOMAIN_NAME \
|
||||||
|
(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_EMERGENCY_ALERT_URI \
|
||||||
|
(1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_TDLS_CAPABILITY \
|
||||||
|
(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
|
||||||
|
#define ANQP_REQ_EMERGENCY_NAI \
|
||||||
|
(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
|
||||||
|
/*
|
||||||
|
* First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
|
||||||
|
* optimized bitmap.
|
||||||
|
*/
|
||||||
|
#define ANQP_REQ_HS_CAPABILITY_LIST \
|
||||||
|
(0x10000 << HS20_STYPE_CAPABILITY_LIST)
|
||||||
|
#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
|
||||||
|
(0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
|
||||||
|
#define ANQP_REQ_WAN_METRICS \
|
||||||
|
(0x10000 << HS20_STYPE_WAN_METRICS)
|
||||||
|
#define ANQP_REQ_CONNECTION_CAPABILITY \
|
||||||
|
(0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
|
||||||
|
#define ANQP_REQ_NAI_HOME_REALM \
|
||||||
|
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
|
||||||
|
#define ANQP_REQ_OPERATING_CLASS \
|
||||||
|
(0x10000 << HS20_STYPE_OPERATING_CLASS)
|
||||||
|
#define ANQP_REQ_OSU_PROVIDERS_LIST \
|
||||||
|
(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
|
||||||
|
#define ANQP_REQ_ICON_REQUEST \
|
||||||
|
(0x10000 << HS20_STYPE_ICON_REQUEST)
|
||||||
|
#define ANQP_REQ_OPERATOR_ICON_METADATA \
|
||||||
|
(0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
|
||||||
|
#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
|
||||||
|
(0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
|
||||||
|
/* The first MBO ANQP-element can be included in the optimized bitmap. */
|
||||||
|
#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
|
||||||
|
(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
|
||||||
|
|
||||||
|
struct gas_dialog_info {
|
||||||
|
u8 valid;
|
||||||
|
struct wpabuf *sd_resp; /* Fragmented response */
|
||||||
|
u8 dialog_token;
|
||||||
|
size_t sd_resp_pos; /* Offset in sd_resp */
|
||||||
|
u8 sd_frag_id;
|
||||||
|
int prot; /* whether Protected Dual of Public Action frame is used */
|
||||||
|
int dpp; /* whether this is a DPP Config Response */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hostapd_data;
|
||||||
|
|
||||||
|
struct gas_dialog_info *
|
||||||
|
gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u8 dialog_token);
|
||||||
|
void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
|
||||||
|
|
||||||
|
int gas_serv_init(struct hostapd_data *hapd);
|
||||||
|
void gas_serv_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
|
||||||
|
const u8 *sa, u8 dialog_token,
|
||||||
|
int prot, struct wpabuf *buf);
|
||||||
|
|
||||||
|
#endif /* GAS_SERV_H */
|
3573
hostapd-2.9/src/ap/hostapd.c
Normal file
3573
hostapd-2.9/src/ap/hostapd.c
Normal file
File diff suppressed because it is too large
Load diff
658
hostapd-2.9/src/ap/hostapd.h
Normal file
658
hostapd-2.9/src/ap/hostapd.h
Normal file
|
@ -0,0 +1,658 @@
|
||||||
|
/*
|
||||||
|
* hostapd / Initialization and configuration
|
||||||
|
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HOSTAPD_H
|
||||||
|
#define HOSTAPD_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_SQLITE
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#endif /* CONFIG_SQLITE */
|
||||||
|
|
||||||
|
#include "common/defs.h"
|
||||||
|
#include "utils/list.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "drivers/driver.h"
|
||||||
|
|
||||||
|
#define OCE_STA_CFON_ENABLED(hapd) \
|
||||||
|
((hapd->conf->oce & OCE_STA_CFON) && \
|
||||||
|
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
|
||||||
|
#define OCE_AP_ENABLED(hapd) \
|
||||||
|
((hapd->conf->oce & OCE_AP) && \
|
||||||
|
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
|
||||||
|
|
||||||
|
struct wpa_ctrl_dst;
|
||||||
|
struct radius_server_data;
|
||||||
|
struct upnp_wps_device_sm;
|
||||||
|
struct hostapd_data;
|
||||||
|
struct sta_info;
|
||||||
|
struct ieee80211_ht_capabilities;
|
||||||
|
struct full_dynamic_vlan;
|
||||||
|
enum wps_event;
|
||||||
|
union wps_event_data;
|
||||||
|
#ifdef CONFIG_MESH
|
||||||
|
struct mesh_conf;
|
||||||
|
#endif /* CONFIG_MESH */
|
||||||
|
|
||||||
|
struct hostapd_iface;
|
||||||
|
|
||||||
|
struct hapd_interfaces {
|
||||||
|
int (*reload_config)(struct hostapd_iface *iface);
|
||||||
|
struct hostapd_config * (*config_read_cb)(const char *config_fname);
|
||||||
|
int (*ctrl_iface_init)(struct hostapd_data *hapd);
|
||||||
|
void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
|
||||||
|
int (*for_each_interface)(struct hapd_interfaces *interfaces,
|
||||||
|
int (*cb)(struct hostapd_iface *iface,
|
||||||
|
void *ctx), void *ctx);
|
||||||
|
int (*driver_init)(struct hostapd_iface *iface);
|
||||||
|
|
||||||
|
size_t count;
|
||||||
|
int global_ctrl_sock;
|
||||||
|
struct dl_list global_ctrl_dst;
|
||||||
|
char *global_iface_path;
|
||||||
|
char *global_iface_name;
|
||||||
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
gid_t ctrl_iface_group;
|
||||||
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||||
|
struct hostapd_iface **iface;
|
||||||
|
|
||||||
|
size_t terminate_on_error;
|
||||||
|
#ifndef CONFIG_NO_VLAN
|
||||||
|
struct dynamic_iface *vlan_priv;
|
||||||
|
#endif /* CONFIG_NO_VLAN */
|
||||||
|
#ifdef CONFIG_ETH_P_OUI
|
||||||
|
struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
|
||||||
|
#endif /* CONFIG_ETH_P_OUI */
|
||||||
|
int eloop_initialized;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DPP
|
||||||
|
struct dpp_global *dpp;
|
||||||
|
#endif /* CONFIG_DPP */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hostapd_chan_status {
|
||||||
|
HOSTAPD_CHAN_VALID = 0, /* channel is ready */
|
||||||
|
HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
|
||||||
|
HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hostapd_probereq_cb {
|
||||||
|
int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
|
||||||
|
const u8 *ie, size_t ie_len, int ssi_signal);
|
||||||
|
void *ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HOSTAPD_RATE_BASIC 0x00000001
|
||||||
|
|
||||||
|
struct hostapd_rate_data {
|
||||||
|
int rate; /* rate in 100 kbps */
|
||||||
|
int flags; /* HOSTAPD_RATE_ flags */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hostapd_frame_info {
|
||||||
|
unsigned int freq;
|
||||||
|
u32 channel;
|
||||||
|
u32 datarate;
|
||||||
|
int ssi_signal; /* dBm */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum wps_status {
|
||||||
|
WPS_STATUS_SUCCESS = 1,
|
||||||
|
WPS_STATUS_FAILURE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum pbc_status {
|
||||||
|
WPS_PBC_STATUS_DISABLE,
|
||||||
|
WPS_PBC_STATUS_ACTIVE,
|
||||||
|
WPS_PBC_STATUS_TIMEOUT,
|
||||||
|
WPS_PBC_STATUS_OVERLAP
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wps_stat {
|
||||||
|
enum wps_status status;
|
||||||
|
enum wps_error_indication failure_reason;
|
||||||
|
enum pbc_status pbc_status;
|
||||||
|
u8 peer_addr[ETH_ALEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hostapd_neighbor_entry {
|
||||||
|
struct dl_list list;
|
||||||
|
u8 bssid[ETH_ALEN];
|
||||||
|
struct wpa_ssid_value ssid;
|
||||||
|
struct wpabuf *nr;
|
||||||
|
struct wpabuf *lci;
|
||||||
|
struct wpabuf *civic;
|
||||||
|
/* LCI update time */
|
||||||
|
struct os_time lci_date;
|
||||||
|
int stationary;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hostapd_sae_commit_queue {
|
||||||
|
struct dl_list list;
|
||||||
|
int rssi;
|
||||||
|
size_t len;
|
||||||
|
u8 msg[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct hostapd_data - hostapd per-BSS data structure
|
||||||
|
*/
|
||||||
|
struct hostapd_data {
|
||||||
|
struct hostapd_iface *iface;
|
||||||
|
struct hostapd_config *iconf;
|
||||||
|
struct hostapd_bss_config *conf;
|
||||||
|
int interface_added; /* virtual interface added for this BSS */
|
||||||
|
unsigned int started:1;
|
||||||
|
unsigned int disabled:1;
|
||||||
|
unsigned int reenable_beacon:1;
|
||||||
|
|
||||||
|
u8 own_addr[ETH_ALEN];
|
||||||
|
|
||||||
|
int num_sta; /* number of entries in sta_list */
|
||||||
|
struct sta_info *sta_list; /* STA info list head */
|
||||||
|
#define STA_HASH_SIZE 256
|
||||||
|
#define STA_HASH(sta) (sta[5])
|
||||||
|
struct sta_info *sta_hash[STA_HASH_SIZE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bitfield for indicating which AIDs are allocated. Only AID values
|
||||||
|
* 1-2007 are used and as such, the bit at index 0 corresponds to AID
|
||||||
|
* 1.
|
||||||
|
*/
|
||||||
|
#define AID_WORDS ((2008 + 31) / 32)
|
||||||
|
u32 sta_aid[AID_WORDS];
|
||||||
|
|
||||||
|
const struct wpa_driver_ops *driver;
|
||||||
|
void *drv_priv;
|
||||||
|
|
||||||
|
void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int reassoc);
|
||||||
|
|
||||||
|
void *msg_ctx; /* ctx for wpa_msg() calls */
|
||||||
|
void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
|
||||||
|
|
||||||
|
struct radius_client_data *radius;
|
||||||
|
u64 acct_session_id;
|
||||||
|
struct radius_das_data *radius_das;
|
||||||
|
|
||||||
|
struct iapp_data *iapp;
|
||||||
|
|
||||||
|
struct hostapd_cached_radius_acl *acl_cache;
|
||||||
|
struct hostapd_acl_query_data *acl_queries;
|
||||||
|
|
||||||
|
struct wpa_authenticator *wpa_auth;
|
||||||
|
struct eapol_authenticator *eapol_auth;
|
||||||
|
|
||||||
|
struct rsn_preauth_interface *preauth_iface;
|
||||||
|
struct os_reltime michael_mic_failure;
|
||||||
|
int michael_mic_failures;
|
||||||
|
int tkip_countermeasures;
|
||||||
|
|
||||||
|
int ctrl_sock;
|
||||||
|
struct dl_list ctrl_dst;
|
||||||
|
|
||||||
|
void *ssl_ctx;
|
||||||
|
void *eap_sim_db_priv;
|
||||||
|
struct radius_server_data *radius_srv;
|
||||||
|
struct dl_list erp_keys; /* struct eap_server_erp_key */
|
||||||
|
|
||||||
|
int parameter_set_count;
|
||||||
|
|
||||||
|
/* Time Advertisement */
|
||||||
|
u8 time_update_counter;
|
||||||
|
struct wpabuf *time_adv;
|
||||||
|
|
||||||
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||||
|
struct full_dynamic_vlan *full_dynamic_vlan;
|
||||||
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||||
|
|
||||||
|
struct l2_packet_data *l2;
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211R_AP
|
||||||
|
struct dl_list l2_queue;
|
||||||
|
struct dl_list l2_oui_queue;
|
||||||
|
struct eth_p_oui_ctx *oui_pull;
|
||||||
|
struct eth_p_oui_ctx *oui_resp;
|
||||||
|
struct eth_p_oui_ctx *oui_push;
|
||||||
|
struct eth_p_oui_ctx *oui_sreq;
|
||||||
|
struct eth_p_oui_ctx *oui_sresp;
|
||||||
|
#endif /* CONFIG_IEEE80211R_AP */
|
||||||
|
|
||||||
|
struct wps_context *wps;
|
||||||
|
|
||||||
|
int beacon_set_done;
|
||||||
|
struct wpabuf *wps_beacon_ie;
|
||||||
|
struct wpabuf *wps_probe_resp_ie;
|
||||||
|
#ifdef CONFIG_WPS
|
||||||
|
unsigned int ap_pin_failures;
|
||||||
|
unsigned int ap_pin_failures_consecutive;
|
||||||
|
struct upnp_wps_device_sm *wps_upnp;
|
||||||
|
unsigned int ap_pin_lockout_time;
|
||||||
|
|
||||||
|
struct wps_stat wps_stats;
|
||||||
|
#endif /* CONFIG_WPS */
|
||||||
|
|
||||||
|
#ifdef CONFIG_MACSEC
|
||||||
|
struct ieee802_1x_kay *kay;
|
||||||
|
#endif /* CONFIG_MACSEC */
|
||||||
|
|
||||||
|
struct hostapd_probereq_cb *probereq_cb;
|
||||||
|
size_t num_probereq_cb;
|
||||||
|
|
||||||
|
void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
|
||||||
|
int freq);
|
||||||
|
void *public_action_cb_ctx;
|
||||||
|
void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
|
||||||
|
int freq);
|
||||||
|
void *public_action_cb2_ctx;
|
||||||
|
|
||||||
|
int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
|
||||||
|
int freq);
|
||||||
|
void *vendor_action_cb_ctx;
|
||||||
|
|
||||||
|
void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
|
||||||
|
const u8 *uuid_e);
|
||||||
|
void *wps_reg_success_cb_ctx;
|
||||||
|
|
||||||
|
void (*wps_event_cb)(void *ctx, enum wps_event event,
|
||||||
|
union wps_event_data *data);
|
||||||
|
void *wps_event_cb_ctx;
|
||||||
|
|
||||||
|
void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
|
||||||
|
int authorized, const u8 *p2p_dev_addr);
|
||||||
|
void *sta_authorized_cb_ctx;
|
||||||
|
|
||||||
|
void (*setup_complete_cb)(void *ctx);
|
||||||
|
void *setup_complete_cb_ctx;
|
||||||
|
|
||||||
|
void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
|
||||||
|
const u8 *p2p_dev_addr, const u8 *psk,
|
||||||
|
size_t psk_len);
|
||||||
|
void *new_psk_cb_ctx;
|
||||||
|
|
||||||
|
/* channel switch parameters */
|
||||||
|
struct hostapd_freq_params cs_freq_params;
|
||||||
|
u8 cs_count;
|
||||||
|
int cs_block_tx;
|
||||||
|
unsigned int cs_c_off_beacon;
|
||||||
|
unsigned int cs_c_off_proberesp;
|
||||||
|
int csa_in_progress;
|
||||||
|
unsigned int cs_c_off_ecsa_beacon;
|
||||||
|
unsigned int cs_c_off_ecsa_proberesp;
|
||||||
|
|
||||||
|
#ifdef CONFIG_P2P
|
||||||
|
struct p2p_data *p2p;
|
||||||
|
struct p2p_group *p2p_group;
|
||||||
|
struct wpabuf *p2p_beacon_ie;
|
||||||
|
struct wpabuf *p2p_probe_resp_ie;
|
||||||
|
|
||||||
|
/* Number of non-P2P association stations */
|
||||||
|
int num_sta_no_p2p;
|
||||||
|
|
||||||
|
/* Periodic NoA (used only when no non-P2P clients in the group) */
|
||||||
|
int noa_enabled;
|
||||||
|
int noa_start;
|
||||||
|
int noa_duration;
|
||||||
|
#endif /* CONFIG_P2P */
|
||||||
|
#ifdef CONFIG_PROXYARP
|
||||||
|
struct l2_packet_data *sock_dhcp;
|
||||||
|
struct l2_packet_data *sock_ndisc;
|
||||||
|
#endif /* CONFIG_PROXYARP */
|
||||||
|
#ifdef CONFIG_MESH
|
||||||
|
int num_plinks;
|
||||||
|
int max_plinks;
|
||||||
|
void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
struct wpabuf *mesh_pending_auth;
|
||||||
|
struct os_reltime mesh_pending_auth_time;
|
||||||
|
u8 mesh_required_peer[ETH_ALEN];
|
||||||
|
#endif /* CONFIG_MESH */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SQLITE
|
||||||
|
struct hostapd_eap_user tmp_eap_user;
|
||||||
|
#endif /* CONFIG_SQLITE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAE
|
||||||
|
/** Key used for generating SAE anti-clogging tokens */
|
||||||
|
u8 sae_token_key[8];
|
||||||
|
struct os_reltime last_sae_token_key_update;
|
||||||
|
u16 sae_token_idx;
|
||||||
|
u16 sae_pending_token_idx[256];
|
||||||
|
int dot11RSNASAERetransPeriod; /* msec */
|
||||||
|
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
|
||||||
|
#endif /* CONFIG_SAE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_TESTING_OPTIONS
|
||||||
|
unsigned int ext_mgmt_frame_handling:1;
|
||||||
|
unsigned int ext_eapol_frame_io:1;
|
||||||
|
|
||||||
|
struct l2_packet_data *l2_test;
|
||||||
|
|
||||||
|
enum wpa_alg last_gtk_alg;
|
||||||
|
int last_gtk_key_idx;
|
||||||
|
u8 last_gtk[WPA_GTK_MAX_LEN];
|
||||||
|
size_t last_gtk_len;
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211W
|
||||||
|
enum wpa_alg last_igtk_alg;
|
||||||
|
int last_igtk_key_idx;
|
||||||
|
u8 last_igtk[WPA_IGTK_MAX_LEN];
|
||||||
|
size_t last_igtk_len;
|
||||||
|
#endif /* CONFIG_IEEE80211W */
|
||||||
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
|
|
||||||
|
#ifdef CONFIG_MBO
|
||||||
|
unsigned int mbo_assoc_disallow;
|
||||||
|
#endif /* CONFIG_MBO */
|
||||||
|
|
||||||
|
struct dl_list nr_db;
|
||||||
|
|
||||||
|
u8 beacon_req_token;
|
||||||
|
u8 lci_req_token;
|
||||||
|
u8 range_req_token;
|
||||||
|
unsigned int lci_req_active:1;
|
||||||
|
unsigned int range_req_active:1;
|
||||||
|
|
||||||
|
int dhcp_sock; /* UDP socket used with the DHCP server */
|
||||||
|
|
||||||
|
#ifdef CONFIG_DPP
|
||||||
|
int dpp_init_done;
|
||||||
|
struct dpp_authentication *dpp_auth;
|
||||||
|
u8 dpp_allowed_roles;
|
||||||
|
int dpp_qr_mutual;
|
||||||
|
int dpp_auth_ok_on_ack;
|
||||||
|
int dpp_in_response_listen;
|
||||||
|
struct gas_query_ap *gas;
|
||||||
|
struct dpp_pkex *dpp_pkex;
|
||||||
|
struct dpp_bootstrap_info *dpp_pkex_bi;
|
||||||
|
char *dpp_pkex_code;
|
||||||
|
char *dpp_pkex_identifier;
|
||||||
|
char *dpp_pkex_auth_cmd;
|
||||||
|
char *dpp_configurator_params;
|
||||||
|
struct os_reltime dpp_last_init;
|
||||||
|
struct os_reltime dpp_init_iter_start;
|
||||||
|
unsigned int dpp_init_max_tries;
|
||||||
|
unsigned int dpp_init_retry_time;
|
||||||
|
unsigned int dpp_resp_wait_time;
|
||||||
|
unsigned int dpp_resp_max_tries;
|
||||||
|
unsigned int dpp_resp_retry_time;
|
||||||
|
#ifdef CONFIG_TESTING_OPTIONS
|
||||||
|
char *dpp_config_obj_override;
|
||||||
|
char *dpp_discovery_override;
|
||||||
|
char *dpp_groups_override;
|
||||||
|
unsigned int dpp_ignore_netaccesskey_mismatch:1;
|
||||||
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
|
#endif /* CONFIG_DPP */
|
||||||
|
|
||||||
|
#ifdef CONFIG_AIRTIME_POLICY
|
||||||
|
unsigned int num_backlogged_sta;
|
||||||
|
unsigned int airtime_weight;
|
||||||
|
#endif /* CONFIG_AIRTIME_POLICY */
|
||||||
|
|
||||||
|
u8 last_1x_eapol_key_replay_counter[8];
|
||||||
|
|
||||||
|
#ifdef CONFIG_SQLITE
|
||||||
|
sqlite3 *rad_attr_db;
|
||||||
|
#endif /* CONFIG_SQLITE */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct hostapd_sta_info {
|
||||||
|
struct dl_list list;
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
struct os_reltime last_seen;
|
||||||
|
int ssi_signal;
|
||||||
|
#ifdef CONFIG_TAXONOMY
|
||||||
|
struct wpabuf *probe_ie_taxonomy;
|
||||||
|
#endif /* CONFIG_TAXONOMY */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct hostapd_iface - hostapd per-interface data structure
|
||||||
|
*/
|
||||||
|
struct hostapd_iface {
|
||||||
|
struct hapd_interfaces *interfaces;
|
||||||
|
void *owner;
|
||||||
|
char *config_fname;
|
||||||
|
struct hostapd_config *conf;
|
||||||
|
char phy[16]; /* Name of the PHY (radio) */
|
||||||
|
|
||||||
|
enum hostapd_iface_state {
|
||||||
|
HAPD_IFACE_UNINITIALIZED,
|
||||||
|
HAPD_IFACE_DISABLED,
|
||||||
|
HAPD_IFACE_COUNTRY_UPDATE,
|
||||||
|
HAPD_IFACE_ACS,
|
||||||
|
HAPD_IFACE_HT_SCAN,
|
||||||
|
HAPD_IFACE_DFS,
|
||||||
|
HAPD_IFACE_ENABLED
|
||||||
|
} state;
|
||||||
|
|
||||||
|
#ifdef CONFIG_MESH
|
||||||
|
struct mesh_conf *mconf;
|
||||||
|
#endif /* CONFIG_MESH */
|
||||||
|
|
||||||
|
size_t num_bss;
|
||||||
|
struct hostapd_data **bss;
|
||||||
|
|
||||||
|
unsigned int wait_channel_update:1;
|
||||||
|
unsigned int cac_started:1;
|
||||||
|
#ifdef CONFIG_FST
|
||||||
|
struct fst_iface *fst;
|
||||||
|
const struct wpabuf *fst_ies;
|
||||||
|
#endif /* CONFIG_FST */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set, indicates that the driver will handle the AP
|
||||||
|
* teardown: delete global keys, station keys, and stations.
|
||||||
|
*/
|
||||||
|
unsigned int driver_ap_teardown:1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set, indicates that this interface is part of list of
|
||||||
|
* interfaces that need to be started together (synchronously).
|
||||||
|
*/
|
||||||
|
unsigned int need_to_start_in_sync:1;
|
||||||
|
|
||||||
|
/* Ready to start but waiting for other interfaces to become ready. */
|
||||||
|
unsigned int ready_to_start_in_sync:1;
|
||||||
|
|
||||||
|
int num_ap; /* number of entries in ap_list */
|
||||||
|
struct ap_info *ap_list; /* AP info list head */
|
||||||
|
struct ap_info *ap_hash[STA_HASH_SIZE];
|
||||||
|
|
||||||
|
u64 drv_flags;
|
||||||
|
|
||||||
|
/* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
|
||||||
|
unsigned int smps_modes;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A bitmap of supported protocols for probe response offload. See
|
||||||
|
* struct wpa_driver_capa in driver.h
|
||||||
|
*/
|
||||||
|
unsigned int probe_resp_offloads;
|
||||||
|
|
||||||
|
/* extended capabilities supported by the driver */
|
||||||
|
const u8 *extended_capa, *extended_capa_mask;
|
||||||
|
unsigned int extended_capa_len;
|
||||||
|
|
||||||
|
unsigned int drv_max_acl_mac_addrs;
|
||||||
|
|
||||||
|
struct hostapd_hw_modes *hw_features;
|
||||||
|
int num_hw_features;
|
||||||
|
struct hostapd_hw_modes *current_mode;
|
||||||
|
/* Rates that are currently used (i.e., filtered copy of
|
||||||
|
* current_mode->channels */
|
||||||
|
int num_rates;
|
||||||
|
struct hostapd_rate_data *current_rates;
|
||||||
|
int *basic_rates;
|
||||||
|
int freq;
|
||||||
|
|
||||||
|
u16 hw_flags;
|
||||||
|
|
||||||
|
/* Number of associated Non-ERP stations (i.e., stations using 802.11b
|
||||||
|
* in 802.11g BSS) */
|
||||||
|
int num_sta_non_erp;
|
||||||
|
|
||||||
|
/* Number of associated stations that do not support Short Slot Time */
|
||||||
|
int num_sta_no_short_slot_time;
|
||||||
|
|
||||||
|
/* Number of associated stations that do not support Short Preamble */
|
||||||
|
int num_sta_no_short_preamble;
|
||||||
|
|
||||||
|
int olbc; /* Overlapping Legacy BSS Condition */
|
||||||
|
|
||||||
|
/* Number of HT associated stations that do not support greenfield */
|
||||||
|
int num_sta_ht_no_gf;
|
||||||
|
|
||||||
|
/* Number of associated non-HT stations */
|
||||||
|
int num_sta_no_ht;
|
||||||
|
|
||||||
|
/* Number of HT associated stations 20 MHz */
|
||||||
|
int num_sta_ht_20mhz;
|
||||||
|
|
||||||
|
/* Number of HT40 intolerant stations */
|
||||||
|
int num_sta_ht40_intolerant;
|
||||||
|
|
||||||
|
/* Overlapping BSS information */
|
||||||
|
int olbc_ht;
|
||||||
|
|
||||||
|
u16 ht_op_mode;
|
||||||
|
|
||||||
|
/* surveying helpers */
|
||||||
|
|
||||||
|
/* number of channels surveyed */
|
||||||
|
unsigned int chans_surveyed;
|
||||||
|
|
||||||
|
/* lowest observed noise floor in dBm */
|
||||||
|
s8 lowest_nf;
|
||||||
|
|
||||||
|
/* channel utilization calculation */
|
||||||
|
u64 last_channel_time;
|
||||||
|
u64 last_channel_time_busy;
|
||||||
|
u8 channel_utilization;
|
||||||
|
|
||||||
|
unsigned int chan_util_samples_sum;
|
||||||
|
unsigned int chan_util_num_sample_periods;
|
||||||
|
unsigned int chan_util_average;
|
||||||
|
|
||||||
|
/* eCSA IE will be added only if operating class is specified */
|
||||||
|
u8 cs_oper_class;
|
||||||
|
|
||||||
|
unsigned int dfs_cac_ms;
|
||||||
|
struct os_reltime dfs_cac_start;
|
||||||
|
|
||||||
|
/* Latched with the actual secondary channel information and will be
|
||||||
|
* used while juggling between HT20 and HT40 modes. */
|
||||||
|
int secondary_ch;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACS
|
||||||
|
unsigned int acs_num_completed_scans;
|
||||||
|
#endif /* CONFIG_ACS */
|
||||||
|
|
||||||
|
void (*scan_cb)(struct hostapd_iface *iface);
|
||||||
|
int num_ht40_scan_tries;
|
||||||
|
|
||||||
|
struct dl_list sta_seen; /* struct hostapd_sta_info */
|
||||||
|
unsigned int num_sta_seen;
|
||||||
|
|
||||||
|
u8 dfs_domain;
|
||||||
|
#ifdef CONFIG_AIRTIME_POLICY
|
||||||
|
unsigned int airtime_quantum;
|
||||||
|
#endif /* CONFIG_AIRTIME_POLICY */
|
||||||
|
|
||||||
|
/* Previous WMM element information */
|
||||||
|
struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* hostapd.c */
|
||||||
|
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
|
||||||
|
int (*cb)(struct hostapd_iface *iface,
|
||||||
|
void *ctx), void *ctx);
|
||||||
|
int hostapd_reload_config(struct hostapd_iface *iface);
|
||||||
|
void hostapd_reconfig_encryption(struct hostapd_data *hapd);
|
||||||
|
struct hostapd_data *
|
||||||
|
hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
|
||||||
|
struct hostapd_config *conf,
|
||||||
|
struct hostapd_bss_config *bss);
|
||||||
|
int hostapd_setup_interface(struct hostapd_iface *iface);
|
||||||
|
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
|
||||||
|
void hostapd_interface_deinit(struct hostapd_iface *iface);
|
||||||
|
void hostapd_interface_free(struct hostapd_iface *iface);
|
||||||
|
struct hostapd_iface * hostapd_alloc_iface(void);
|
||||||
|
struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
|
||||||
|
const char *config_file);
|
||||||
|
struct hostapd_iface *
|
||||||
|
hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
|
||||||
|
const char *config_fname, int debug);
|
||||||
|
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
int reassoc);
|
||||||
|
void hostapd_interface_deinit_free(struct hostapd_iface *iface);
|
||||||
|
int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
|
||||||
|
int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
|
||||||
|
int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
|
||||||
|
int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
|
||||||
|
int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
|
||||||
|
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
|
||||||
|
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
|
||||||
|
const char * hostapd_state_text(enum hostapd_iface_state s);
|
||||||
|
int hostapd_csa_in_progress(struct hostapd_iface *iface);
|
||||||
|
void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled);
|
||||||
|
int hostapd_switch_channel(struct hostapd_data *hapd,
|
||||||
|
struct csa_settings *settings);
|
||||||
|
void
|
||||||
|
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
|
||||||
|
const struct hostapd_freq_params *freq_params);
|
||||||
|
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
|
||||||
|
void hostapd_periodic_iface(struct hostapd_iface *iface);
|
||||||
|
int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
/* utils.c */
|
||||||
|
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
|
||||||
|
int (*cb)(void *ctx, const u8 *sa,
|
||||||
|
const u8 *da, const u8 *bssid,
|
||||||
|
const u8 *ie, size_t ie_len,
|
||||||
|
int ssi_signal),
|
||||||
|
void *ctx);
|
||||||
|
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
|
||||||
|
|
||||||
|
/* drv_callbacks.c (TODO: move to somewhere else?) */
|
||||||
|
void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
const u8 *ie, size_t ielen, int reassoc);
|
||||||
|
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
|
||||||
|
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
|
||||||
|
void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, int reason_code);
|
||||||
|
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
|
||||||
|
const u8 *bssid, const u8 *ie, size_t ie_len,
|
||||||
|
int ssi_signal);
|
||||||
|
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
|
||||||
|
int offset, int width, int cf1, int cf2,
|
||||||
|
int finished);
|
||||||
|
struct survey_results;
|
||||||
|
void hostapd_event_get_survey(struct hostapd_iface *iface,
|
||||||
|
struct survey_results *survey_results);
|
||||||
|
void hostapd_acs_channel_selected(struct hostapd_data *hapd,
|
||||||
|
struct acs_selected_channels *acs_res);
|
||||||
|
|
||||||
|
const struct hostapd_eap_user *
|
||||||
|
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
|
||||||
|
size_t identity_len, int phase2);
|
||||||
|
|
||||||
|
struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
|
||||||
|
const char *ifname);
|
||||||
|
void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
enum smps_mode smps_mode,
|
||||||
|
enum chan_width chan_width, u8 rx_nss);
|
||||||
|
|
||||||
|
#ifdef CONFIG_FST
|
||||||
|
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
|
||||||
|
struct fst_wpa_obj *iface_obj);
|
||||||
|
#endif /* CONFIG_FST */
|
||||||
|
|
||||||
|
#endif /* HOSTAPD_H */
|
255
hostapd-2.9/src/ap/hs20.c
Normal file
255
hostapd-2.9/src/ap/hs20.c
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
* Hotspot 2.0 AP ANQP processing
|
||||||
|
* Copyright (c) 2009, Atheros Communications, Inc.
|
||||||
|
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/wpa_ctrl.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "hs20.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
u8 conf;
|
||||||
|
if (!hapd->conf->hs20)
|
||||||
|
return eid;
|
||||||
|
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||||
|
*eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
|
||||||
|
WPA_PUT_BE24(eid, OUI_WFA);
|
||||||
|
eid += 3;
|
||||||
|
*eid++ = HS20_INDICATION_OUI_TYPE;
|
||||||
|
conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
|
||||||
|
if (hapd->conf->hs20_release >= 2)
|
||||||
|
conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
|
||||||
|
if (hapd->conf->disable_dgaf)
|
||||||
|
conf |= HS20_DGAF_DISABLED;
|
||||||
|
*eid++ = conf;
|
||||||
|
if (hapd->conf->hs20_release >= 2) {
|
||||||
|
WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
|
||||||
|
eid += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
u8 *len;
|
||||||
|
u16 capab;
|
||||||
|
|
||||||
|
if (!hapd->conf->osen)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||||
|
len = eid++; /* to be filled */
|
||||||
|
WPA_PUT_BE24(eid, OUI_WFA);
|
||||||
|
eid += 3;
|
||||||
|
*eid++ = HS20_OSEN_OUI_TYPE;
|
||||||
|
|
||||||
|
/* Group Data Cipher Suite */
|
||||||
|
RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
|
||||||
|
eid += RSN_SELECTOR_LEN;
|
||||||
|
|
||||||
|
/* Pairwise Cipher Suite Count and List */
|
||||||
|
WPA_PUT_LE16(eid, 1);
|
||||||
|
eid += 2;
|
||||||
|
RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
|
||||||
|
eid += RSN_SELECTOR_LEN;
|
||||||
|
|
||||||
|
/* AKM Suite Count and List */
|
||||||
|
WPA_PUT_LE16(eid, 1);
|
||||||
|
eid += 2;
|
||||||
|
RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
|
||||||
|
eid += RSN_SELECTOR_LEN;
|
||||||
|
|
||||||
|
/* RSN Capabilities */
|
||||||
|
capab = 0;
|
||||||
|
if (hapd->conf->wmm_enabled) {
|
||||||
|
/* 4 PTKSA replay counters when using WMM */
|
||||||
|
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_IEEE80211W
|
||||||
|
if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
|
||||||
|
capab |= WPA_CAPABILITY_MFPC;
|
||||||
|
if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
|
||||||
|
capab |= WPA_CAPABILITY_MFPR;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211W */
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
if (hapd->conf->ocv)
|
||||||
|
capab |= WPA_CAPABILITY_OCVC;
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
|
WPA_PUT_LE16(eid, capab);
|
||||||
|
eid += 2;
|
||||||
|
|
||||||
|
*len = eid - len - 1;
|
||||||
|
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u8 osu_method, const char *url)
|
||||||
|
{
|
||||||
|
struct wpabuf *buf;
|
||||||
|
size_t len = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* TODO: should refuse to send notification if the STA is not associated
|
||||||
|
* or if the STA did not indicate support for WNM-Notification */
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
len = 1 + os_strlen(url);
|
||||||
|
if (5 + len > 255) {
|
||||||
|
wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
|
||||||
|
"WNM-Notification: '%s'", url);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = wpabuf_alloc(4 + 7 + len);
|
||||||
|
if (buf == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
||||||
|
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
|
||||||
|
wpabuf_put_u8(buf, 1); /* Dialog token */
|
||||||
|
wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
|
||||||
|
|
||||||
|
/* Subscription Remediation subelement */
|
||||||
|
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
||||||
|
wpabuf_put_u8(buf, 5 + len);
|
||||||
|
wpabuf_put_be24(buf, OUI_WFA);
|
||||||
|
wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
|
||||||
|
if (url) {
|
||||||
|
wpabuf_put_u8(buf, len - 1);
|
||||||
|
wpabuf_put_data(buf, url, len - 1);
|
||||||
|
wpabuf_put_u8(buf, osu_method);
|
||||||
|
} else {
|
||||||
|
/* Server URL and Server Method fields not included */
|
||||||
|
wpabuf_put_u8(buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||||
|
wpabuf_head(buf), wpabuf_len(buf));
|
||||||
|
|
||||||
|
wpabuf_free(buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr,
|
||||||
|
const struct wpabuf *payload)
|
||||||
|
{
|
||||||
|
struct wpabuf *buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* TODO: should refuse to send notification if the STA is not associated
|
||||||
|
* or if the STA did not indicate support for WNM-Notification */
|
||||||
|
|
||||||
|
buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
|
||||||
|
if (buf == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
||||||
|
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
|
||||||
|
wpabuf_put_u8(buf, 1); /* Dialog token */
|
||||||
|
wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
|
||||||
|
|
||||||
|
/* Deauthentication Imminent Notice subelement */
|
||||||
|
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
||||||
|
wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
|
||||||
|
wpabuf_put_be24(buf, OUI_WFA);
|
||||||
|
wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
|
||||||
|
wpabuf_put_buf(buf, payload);
|
||||||
|
|
||||||
|
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||||
|
wpabuf_head(buf), wpabuf_len(buf));
|
||||||
|
|
||||||
|
wpabuf_free(buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, const char *url)
|
||||||
|
{
|
||||||
|
struct wpabuf *buf;
|
||||||
|
int ret;
|
||||||
|
size_t url_len;
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
url_len = os_strlen(url);
|
||||||
|
if (5 + url_len > 255) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
|
||||||
|
url);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = wpabuf_alloc(4 + 7 + url_len);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
||||||
|
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
|
||||||
|
wpabuf_put_u8(buf, 1); /* Dialog token */
|
||||||
|
wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
|
||||||
|
|
||||||
|
/* Terms and Conditions Acceptance subelement */
|
||||||
|
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
||||||
|
wpabuf_put_u8(buf, 4 + 1 + url_len);
|
||||||
|
wpabuf_put_be24(buf, OUI_WFA);
|
||||||
|
wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
|
||||||
|
wpabuf_put_u8(buf, url_len);
|
||||||
|
wpabuf_put_str(buf, url);
|
||||||
|
|
||||||
|
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||||
|
wpabuf_head(buf), wpabuf_len(buf));
|
||||||
|
|
||||||
|
wpabuf_free(buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
int enabled)
|
||||||
|
{
|
||||||
|
if (enabled) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"HS 2.0: Terms and Conditions filtering required for "
|
||||||
|
MACSTR, MAC2STR(sta->addr));
|
||||||
|
sta->hs20_t_c_filtering = 1;
|
||||||
|
/* TODO: Enable firewall filtering for the STA */
|
||||||
|
wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
|
||||||
|
MAC2STR(sta->addr));
|
||||||
|
} else {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"HS 2.0: Terms and Conditions filtering not required for "
|
||||||
|
MACSTR, MAC2STR(sta->addr));
|
||||||
|
sta->hs20_t_c_filtering = 0;
|
||||||
|
/* TODO: Disable firewall filtering for the STA */
|
||||||
|
wpa_msg(hapd->msg_ctx, MSG_INFO,
|
||||||
|
HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
|
||||||
|
}
|
||||||
|
}
|
26
hostapd-2.9/src/ap/hs20.h
Normal file
26
hostapd-2.9/src/ap/hs20.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Hotspot 2.0 AP ANQP processing
|
||||||
|
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HS20_H
|
||||||
|
#define HS20_H
|
||||||
|
|
||||||
|
struct hostapd_data;
|
||||||
|
|
||||||
|
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u8 osu_method, const char *url);
|
||||||
|
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr,
|
||||||
|
const struct wpabuf *payload);
|
||||||
|
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, const char *url);
|
||||||
|
void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
int enabled);
|
||||||
|
|
||||||
|
#endif /* HS20_H */
|
974
hostapd-2.9/src/ap/hw_features.c
Normal file
974
hostapd-2.9/src/ap/hw_features.c
Normal file
|
@ -0,0 +1,974 @@
|
||||||
|
/*
|
||||||
|
* hostapd / Hardware feature query and different modes
|
||||||
|
* Copyright 2002-2003, Instant802 Networks, Inc.
|
||||||
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||||
|
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "common/ieee802_11_common.h"
|
||||||
|
#include "common/wpa_ctrl.h"
|
||||||
|
#include "common/hw_features_common.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "acs.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "beacon.h"
|
||||||
|
#include "hw_features.h"
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
||||||
|
size_t num_hw_features)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (hw_features == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < num_hw_features; i++) {
|
||||||
|
os_free(hw_features[i].channels);
|
||||||
|
os_free(hw_features[i].rates);
|
||||||
|
}
|
||||||
|
|
||||||
|
os_free(hw_features);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_STDOUT_DEBUG
|
||||||
|
static char * dfs_info(struct hostapd_channel_data *chan)
|
||||||
|
{
|
||||||
|
static char info[256];
|
||||||
|
char *state;
|
||||||
|
|
||||||
|
switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
|
||||||
|
case HOSTAPD_CHAN_DFS_UNKNOWN:
|
||||||
|
state = "unknown";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_CHAN_DFS_USABLE:
|
||||||
|
state = "usable";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_CHAN_DFS_UNAVAILABLE:
|
||||||
|
state = "unavailable";
|
||||||
|
break;
|
||||||
|
case HOSTAPD_CHAN_DFS_AVAILABLE:
|
||||||
|
state = "available";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
|
||||||
|
info[sizeof(info) - 1] = '\0';
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_get_hw_features(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = iface->bss[0];
|
||||||
|
int i, j;
|
||||||
|
u16 num_modes, flags;
|
||||||
|
struct hostapd_hw_modes *modes;
|
||||||
|
u8 dfs_domain;
|
||||||
|
|
||||||
|
if (hostapd_drv_none(hapd))
|
||||||
|
return -1;
|
||||||
|
modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
|
||||||
|
&dfs_domain);
|
||||||
|
if (modes == NULL) {
|
||||||
|
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"Fetching hardware channel/rate support not "
|
||||||
|
"supported.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->hw_flags = flags;
|
||||||
|
iface->dfs_domain = dfs_domain;
|
||||||
|
|
||||||
|
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
|
||||||
|
iface->hw_features = modes;
|
||||||
|
iface->num_hw_features = num_modes;
|
||||||
|
|
||||||
|
for (i = 0; i < num_modes; i++) {
|
||||||
|
struct hostapd_hw_modes *feature = &modes[i];
|
||||||
|
int dfs_enabled = hapd->iconf->ieee80211h &&
|
||||||
|
(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
|
||||||
|
|
||||||
|
/* set flag for channels we can use in current regulatory
|
||||||
|
* domain */
|
||||||
|
for (j = 0; j < feature->num_channels; j++) {
|
||||||
|
int dfs = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable all channels that are marked not to allow
|
||||||
|
* to initiate radiation (a.k.a. passive scan and no
|
||||||
|
* IBSS).
|
||||||
|
* Use radar channels only if the driver supports DFS.
|
||||||
|
*/
|
||||||
|
if ((feature->channels[j].flag &
|
||||||
|
HOSTAPD_CHAN_RADAR) && dfs_enabled) {
|
||||||
|
dfs = 1;
|
||||||
|
} else if (((feature->channels[j].flag &
|
||||||
|
HOSTAPD_CHAN_RADAR) &&
|
||||||
|
!(iface->drv_flags &
|
||||||
|
WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
|
||||||
|
(feature->channels[j].flag &
|
||||||
|
HOSTAPD_CHAN_NO_IR)) {
|
||||||
|
feature->channels[j].flag |=
|
||||||
|
HOSTAPD_CHAN_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
|
||||||
|
"chan=%d freq=%d MHz max_tx_power=%d dBm%s",
|
||||||
|
feature->mode,
|
||||||
|
feature->channels[j].chan,
|
||||||
|
feature->channels[j].freq,
|
||||||
|
feature->channels[j].max_tx_power,
|
||||||
|
dfs ? dfs_info(&feature->channels[j]) : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_prepare_rates(struct hostapd_iface *iface,
|
||||||
|
struct hostapd_hw_modes *mode)
|
||||||
|
{
|
||||||
|
int i, num_basic_rates = 0;
|
||||||
|
int basic_rates_a[] = { 60, 120, 240, -1 };
|
||||||
|
int basic_rates_b[] = { 10, 20, -1 };
|
||||||
|
int basic_rates_g[] = { 10, 20, 55, 110, -1 };
|
||||||
|
int *basic_rates;
|
||||||
|
|
||||||
|
if (iface->conf->basic_rates)
|
||||||
|
basic_rates = iface->conf->basic_rates;
|
||||||
|
else switch (mode->mode) {
|
||||||
|
case HOSTAPD_MODE_IEEE80211A:
|
||||||
|
basic_rates = basic_rates_a;
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODE_IEEE80211B:
|
||||||
|
basic_rates = basic_rates_b;
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODE_IEEE80211G:
|
||||||
|
basic_rates = basic_rates_g;
|
||||||
|
break;
|
||||||
|
case HOSTAPD_MODE_IEEE80211AD:
|
||||||
|
return 0; /* No basic rates for 11ad */
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (basic_rates[i] >= 0)
|
||||||
|
i++;
|
||||||
|
if (i)
|
||||||
|
i++; /* -1 termination */
|
||||||
|
os_free(iface->basic_rates);
|
||||||
|
iface->basic_rates = os_malloc(i * sizeof(int));
|
||||||
|
if (iface->basic_rates)
|
||||||
|
os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
|
||||||
|
|
||||||
|
os_free(iface->current_rates);
|
||||||
|
iface->num_rates = 0;
|
||||||
|
|
||||||
|
iface->current_rates =
|
||||||
|
os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
|
||||||
|
if (!iface->current_rates) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
|
||||||
|
"table.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < mode->num_rates; i++) {
|
||||||
|
struct hostapd_rate_data *rate;
|
||||||
|
|
||||||
|
if (iface->conf->supported_rates &&
|
||||||
|
!hostapd_rate_found(iface->conf->supported_rates,
|
||||||
|
mode->rates[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rate = &iface->current_rates[iface->num_rates];
|
||||||
|
rate->rate = mode->rates[i];
|
||||||
|
if (hostapd_rate_found(basic_rates, rate->rate)) {
|
||||||
|
rate->flags |= HOSTAPD_RATE_BASIC;
|
||||||
|
num_basic_rates++;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
|
||||||
|
iface->num_rates, rate->rate, rate->flags);
|
||||||
|
iface->num_rates++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((iface->num_rates == 0 || num_basic_rates == 0) &&
|
||||||
|
(!iface->conf->ieee80211n || !iface->conf->require_ht)) {
|
||||||
|
wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
|
||||||
|
"rate sets (%d,%d).",
|
||||||
|
iface->num_rates, num_basic_rates);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211N
|
||||||
|
static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int pri_chan, sec_chan;
|
||||||
|
|
||||||
|
pri_chan = iface->conf->channel;
|
||||||
|
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
||||||
|
|
||||||
|
return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
|
||||||
|
sec_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
if (iface->conf->secondary_channel > 0) {
|
||||||
|
iface->conf->channel += 4;
|
||||||
|
iface->conf->secondary_channel = -1;
|
||||||
|
} else {
|
||||||
|
iface->conf->channel -= 4;
|
||||||
|
iface->conf->secondary_channel = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
|
||||||
|
struct wpa_scan_results *scan_res)
|
||||||
|
{
|
||||||
|
int pri_chan, sec_chan;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
pri_chan = iface->conf->channel;
|
||||||
|
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
||||||
|
|
||||||
|
res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
|
||||||
|
|
||||||
|
if (res == 2) {
|
||||||
|
if (iface->conf->no_pri_sec_switch) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Cannot switch PRI/SEC channels due to local constraint");
|
||||||
|
} else {
|
||||||
|
ieee80211n_switch_pri_sec(iface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
|
||||||
|
struct wpa_scan_results *scan_res)
|
||||||
|
{
|
||||||
|
int pri_chan, sec_chan;
|
||||||
|
|
||||||
|
pri_chan = iface->conf->channel;
|
||||||
|
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
||||||
|
|
||||||
|
return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
|
||||||
|
sec_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ieee80211n_check_scan(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct wpa_scan_results *scan_res;
|
||||||
|
int oper40;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
|
||||||
|
* allowed per IEEE Std 802.11-2012, 10.15.3.2 */
|
||||||
|
|
||||||
|
iface->scan_cb = NULL;
|
||||||
|
|
||||||
|
scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
|
||||||
|
if (scan_res == NULL) {
|
||||||
|
hostapd_setup_interface_complete(iface, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
|
||||||
|
oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
|
||||||
|
else
|
||||||
|
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
|
||||||
|
wpa_scan_results_free(scan_res);
|
||||||
|
|
||||||
|
iface->secondary_ch = iface->conf->secondary_channel;
|
||||||
|
if (!oper40) {
|
||||||
|
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
|
||||||
|
"channel pri=%d sec=%d based on overlapping BSSes",
|
||||||
|
iface->conf->channel,
|
||||||
|
iface->conf->channel +
|
||||||
|
iface->conf->secondary_channel * 4);
|
||||||
|
iface->conf->secondary_channel = 0;
|
||||||
|
if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
|
||||||
|
/*
|
||||||
|
* TODO: Could consider scheduling another scan to check
|
||||||
|
* if channel width can be changed if no coex reports
|
||||||
|
* are received from associating stations.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = ieee80211n_allowed_ht40_channel_pair(iface);
|
||||||
|
if (!res) {
|
||||||
|
iface->conf->secondary_channel = 0;
|
||||||
|
hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
|
||||||
|
hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
|
||||||
|
hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
|
||||||
|
res = 1;
|
||||||
|
wpa_printf(MSG_INFO, "Fallback to 20 MHz");
|
||||||
|
}
|
||||||
|
|
||||||
|
hostapd_setup_interface_complete(iface, !res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
|
||||||
|
struct wpa_driver_scan_params *params)
|
||||||
|
{
|
||||||
|
/* Scan only the affected frequency range */
|
||||||
|
int pri_freq, sec_freq;
|
||||||
|
int affected_start, affected_end;
|
||||||
|
int i, pos;
|
||||||
|
struct hostapd_hw_modes *mode;
|
||||||
|
|
||||||
|
if (iface->current_mode == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
|
||||||
|
if (iface->conf->secondary_channel > 0)
|
||||||
|
sec_freq = pri_freq + 20;
|
||||||
|
else
|
||||||
|
sec_freq = pri_freq - 20;
|
||||||
|
/*
|
||||||
|
* Note: Need to find the PRI channel also in cases where the affected
|
||||||
|
* channel is the SEC channel of a 40 MHz BSS, so need to include the
|
||||||
|
* scanning coverage here to be 40 MHz from the center frequency.
|
||||||
|
*/
|
||||||
|
affected_start = (pri_freq + sec_freq) / 2 - 40;
|
||||||
|
affected_end = (pri_freq + sec_freq) / 2 + 40;
|
||||||
|
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
|
||||||
|
affected_start, affected_end);
|
||||||
|
|
||||||
|
mode = iface->current_mode;
|
||||||
|
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
|
||||||
|
if (params->freqs == NULL)
|
||||||
|
return;
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < mode->num_channels; i++) {
|
||||||
|
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||||
|
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||||
|
continue;
|
||||||
|
if (chan->freq < affected_start ||
|
||||||
|
chan->freq > affected_end)
|
||||||
|
continue;
|
||||||
|
params->freqs[pos++] = chan->freq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
|
||||||
|
struct wpa_driver_scan_params *params)
|
||||||
|
{
|
||||||
|
/* Scan only the affected frequency range */
|
||||||
|
int pri_freq;
|
||||||
|
int affected_start, affected_end;
|
||||||
|
int i, pos;
|
||||||
|
struct hostapd_hw_modes *mode;
|
||||||
|
|
||||||
|
if (iface->current_mode == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
|
||||||
|
if (iface->conf->secondary_channel > 0) {
|
||||||
|
affected_start = pri_freq - 10;
|
||||||
|
affected_end = pri_freq + 30;
|
||||||
|
} else {
|
||||||
|
affected_start = pri_freq - 30;
|
||||||
|
affected_end = pri_freq + 10;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
|
||||||
|
affected_start, affected_end);
|
||||||
|
|
||||||
|
mode = iface->current_mode;
|
||||||
|
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
|
||||||
|
if (params->freqs == NULL)
|
||||||
|
return;
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < mode->num_channels; i++) {
|
||||||
|
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||||
|
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||||
|
continue;
|
||||||
|
if (chan->freq < affected_start ||
|
||||||
|
chan->freq > affected_end)
|
||||||
|
continue;
|
||||||
|
params->freqs[pos++] = chan->freq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
|
||||||
|
{
|
||||||
|
#define HT2040_COEX_SCAN_RETRY 15
|
||||||
|
struct hostapd_iface *iface = eloop_data;
|
||||||
|
struct wpa_driver_scan_params params;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
|
||||||
|
ieee80211n_scan_channels_2g4(iface, ¶ms);
|
||||||
|
else
|
||||||
|
ieee80211n_scan_channels_5g(iface, ¶ms);
|
||||||
|
|
||||||
|
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
|
||||||
|
iface->num_ht40_scan_tries++;
|
||||||
|
os_free(params.freqs);
|
||||||
|
|
||||||
|
if (ret == -EBUSY &&
|
||||||
|
iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
|
||||||
|
ret, strerror(-ret), iface->num_ht40_scan_tries);
|
||||||
|
eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
iface->scan_cb = ieee80211n_check_scan;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Failed to request a scan in device, bringing up in HT20 mode");
|
||||||
|
iface->conf->secondary_channel = 0;
|
||||||
|
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
|
||||||
|
hostapd_setup_interface_complete(iface, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_stop_setup_timers(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct wpa_driver_scan_params params;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check that HT40 is used and PRI / SEC switch is allowed */
|
||||||
|
if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
|
||||||
|
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
|
||||||
|
"40 MHz channel");
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
|
||||||
|
ieee80211n_scan_channels_2g4(iface, ¶ms);
|
||||||
|
else
|
||||||
|
ieee80211n_scan_channels_5g(iface, ¶ms);
|
||||||
|
|
||||||
|
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
|
||||||
|
os_free(params.freqs);
|
||||||
|
|
||||||
|
if (ret == -EBUSY) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
|
||||||
|
ret, strerror(-ret));
|
||||||
|
iface->num_ht40_scan_tries = 1;
|
||||||
|
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
|
||||||
|
eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
|
||||||
|
|
||||||
|
//DRIVER_RTW Modify
|
||||||
|
//return -1;
|
||||||
|
return 0;//ignore this error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Failed to request a scan of neighboring BSSes ret=%d (%s)",
|
||||||
|
ret, strerror(-ret));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->scan_cb = ieee80211n_check_scan;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
u16 hw = iface->current_mode->ht_capab;
|
||||||
|
u16 conf = iface->conf->ht_capab;
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
|
||||||
|
!(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [LDPC]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Driver ACS chosen channel may not be HT40 due to internal driver
|
||||||
|
* restrictions.
|
||||||
|
*/
|
||||||
|
if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
|
||||||
|
!(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [HT40*]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (conf & HT_CAP_INFO_SMPS_MASK) {
|
||||||
|
case HT_CAP_INFO_SMPS_STATIC:
|
||||||
|
if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Driver does not support configured HT capability [SMPS-STATIC]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HT_CAP_INFO_SMPS_DYNAMIC:
|
||||||
|
if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Driver does not support configured HT capability [SMPS-DYNAMIC]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HT_CAP_INFO_SMPS_DISABLED:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
|
||||||
|
!(hw & HT_CAP_INFO_GREEN_FIELD)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [GF]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
|
||||||
|
!(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [SHORT-GI-20]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
|
||||||
|
!(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [SHORT-GI-40]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [TX-STBC]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
|
||||||
|
(hw & HT_CAP_INFO_RX_STBC_MASK)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [RX-STBC*]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_DELAYED_BA) &&
|
||||||
|
!(hw & HT_CAP_INFO_DELAYED_BA)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [DELAYED-BA]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
|
||||||
|
!(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [MAX-AMSDU-7935]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
|
||||||
|
!(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [DSSS_CCK-40]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
|
||||||
|
!(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
|
||||||
|
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||||
|
"HT capability [LSIG-TXOP-PROT]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211AC
|
||||||
|
static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
struct hostapd_hw_modes *mode = iface->current_mode;
|
||||||
|
u32 hw = mode->vht_capab;
|
||||||
|
u32 conf = iface->conf->vht_capab;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
|
||||||
|
hw, conf);
|
||||||
|
|
||||||
|
if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
|
||||||
|
iface->conf->bss[0]->vendor_vht &&
|
||||||
|
mode->vht_capab == 0 && iface->hw_features) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->num_hw_features; i++) {
|
||||||
|
if (iface->hw_features[i].mode ==
|
||||||
|
HOSTAPD_MODE_IEEE80211A) {
|
||||||
|
mode = &iface->hw_features[i];
|
||||||
|
hw = mode->vht_capab;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"update hw vht capab based on 5 GHz band: 0x%x",
|
||||||
|
hw);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ieee80211ac_cap_check(hw, conf);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211AC */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211AX
|
||||||
|
static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211AX */
|
||||||
|
|
||||||
|
#endif /* CONFIG_IEEE80211N */
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_check_ht_capab(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_IEEE80211N
|
||||||
|
int ret;
|
||||||
|
if (!iface->conf->ieee80211n)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
|
||||||
|
iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
|
||||||
|
(iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Disable HT capability [DSSS_CCK-40] on 5 GHz band");
|
||||||
|
iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ieee80211n_supported_ht_capab(iface))
|
||||||
|
return -1;
|
||||||
|
#ifdef CONFIG_IEEE80211AX
|
||||||
|
if (iface->conf->ieee80211ax &&
|
||||||
|
!ieee80211ax_supported_he_capab(iface))
|
||||||
|
return -1;
|
||||||
|
#endif /* CONFIG_IEEE80211AX */
|
||||||
|
#ifdef CONFIG_IEEE80211AC
|
||||||
|
if (iface->conf->ieee80211ac &&
|
||||||
|
!ieee80211ac_supported_vht_capab(iface))
|
||||||
|
return -1;
|
||||||
|
#endif /* CONFIG_IEEE80211AC */
|
||||||
|
ret = ieee80211n_check_40mhz(iface);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (!ieee80211n_allowed_ht40_channel_pair(iface))
|
||||||
|
return -1;
|
||||||
|
#endif /* CONFIG_IEEE80211N */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
|
||||||
|
int channel, int primary)
|
||||||
|
{
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
|
||||||
|
if (!iface->current_mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
|
||||||
|
if (!chan)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((primary && chan_pri_allowed(chan)) ||
|
||||||
|
(!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
|
||||||
|
channel, primary ? "primary" : "secondary",
|
||||||
|
chan->flag,
|
||||||
|
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
|
||||||
|
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int secondary_chan;
|
||||||
|
struct hostapd_channel_data *pri_chan;
|
||||||
|
|
||||||
|
pri_chan = hw_get_channel_chan(iface->current_mode,
|
||||||
|
iface->conf->channel, NULL);
|
||||||
|
if (!pri_chan)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!iface->conf->secondary_channel)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!iface->conf->ht40_plus_minus_allowed)
|
||||||
|
return hostapd_is_usable_chan(
|
||||||
|
iface, iface->conf->channel +
|
||||||
|
iface->conf->secondary_channel * 4, 0);
|
||||||
|
|
||||||
|
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
|
||||||
|
secondary_chan = iface->conf->channel + 4;
|
||||||
|
if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
|
||||||
|
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
|
||||||
|
iface->conf->secondary_channel = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary_chan = iface->conf->channel - 4;
|
||||||
|
if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
|
||||||
|
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
|
||||||
|
iface->conf->secondary_channel = -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum hostapd_chan_status
|
||||||
|
hostapd_check_chans(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
if (iface->conf->channel) {
|
||||||
|
if (hostapd_is_usable_chans(iface))
|
||||||
|
return HOSTAPD_CHAN_VALID;
|
||||||
|
else
|
||||||
|
return HOSTAPD_CHAN_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The user set channel=0 or channel=acs_survey
|
||||||
|
* which is used to trigger ACS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (acs_init(iface)) {
|
||||||
|
case HOSTAPD_CHAN_ACS:
|
||||||
|
return HOSTAPD_CHAN_ACS;
|
||||||
|
case HOSTAPD_CHAN_VALID:
|
||||||
|
case HOSTAPD_CHAN_INVALID:
|
||||||
|
default:
|
||||||
|
return HOSTAPD_CHAN_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
if (!iface->current_mode) {
|
||||||
|
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_WARNING,
|
||||||
|
"Hardware does not support configured mode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hostapd_logger(iface->bss[0], NULL,
|
||||||
|
HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_WARNING,
|
||||||
|
"Configured channel (%d) not found from the "
|
||||||
|
"channel list of current mode (%d) %s",
|
||||||
|
iface->conf->channel,
|
||||||
|
iface->current_mode->mode,
|
||||||
|
hostapd_hw_mode_txt(iface->current_mode->mode));
|
||||||
|
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_WARNING,
|
||||||
|
"Hardware does not support configured channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_acs_completed(struct hostapd_iface *iface, int err)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
switch (hostapd_check_chans(iface)) {
|
||||||
|
case HOSTAPD_CHAN_VALID:
|
||||||
|
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
|
||||||
|
ACS_EVENT_COMPLETED "freq=%d channel=%d",
|
||||||
|
hostapd_hw_get_freq(iface->bss[0],
|
||||||
|
iface->conf->channel),
|
||||||
|
iface->conf->channel);
|
||||||
|
break;
|
||||||
|
case HOSTAPD_CHAN_ACS:
|
||||||
|
wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
|
||||||
|
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
|
||||||
|
hostapd_notify_bad_chans(iface);
|
||||||
|
goto out;
|
||||||
|
case HOSTAPD_CHAN_INVALID:
|
||||||
|
default:
|
||||||
|
wpa_printf(MSG_ERROR, "ACS picked unusable channels");
|
||||||
|
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
|
||||||
|
hostapd_notify_bad_chans(iface);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hostapd_check_ht_capab(iface);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
if (ret == 1) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
return hostapd_setup_interface_complete(iface, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_select_hw_mode - Select the hardware mode
|
||||||
|
* @iface: Pointer to interface data.
|
||||||
|
* Returns: 0 on success, < 0 on failure
|
||||||
|
*
|
||||||
|
* Sets up the hardware mode, channel, rates, and passive scanning
|
||||||
|
* based on the configuration.
|
||||||
|
*/
|
||||||
|
int hostapd_select_hw_mode(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (iface->num_hw_features < 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
|
||||||
|
iface->conf->ieee80211n || iface->conf->ieee80211ac ||
|
||||||
|
iface->conf->ieee80211ax) &&
|
||||||
|
iface->conf->channel == 14) {
|
||||||
|
wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
|
||||||
|
iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
|
||||||
|
iface->conf->ieee80211n = 0;
|
||||||
|
iface->conf->ieee80211ac = 0;
|
||||||
|
iface->conf->ieee80211ax = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->current_mode = NULL;
|
||||||
|
for (i = 0; i < iface->num_hw_features; i++) {
|
||||||
|
struct hostapd_hw_modes *mode = &iface->hw_features[i];
|
||||||
|
if (mode->mode == iface->conf->hw_mode) {
|
||||||
|
iface->current_mode = mode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->current_mode == NULL) {
|
||||||
|
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
|
||||||
|
!(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
|
||||||
|
{
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"Hardware does not support configured mode");
|
||||||
|
hostapd_logger(iface->bss[0], NULL,
|
||||||
|
HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_WARNING,
|
||||||
|
"Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
|
||||||
|
(int) iface->conf->hw_mode);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (hostapd_check_chans(iface)) {
|
||||||
|
case HOSTAPD_CHAN_VALID:
|
||||||
|
return 0;
|
||||||
|
case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
|
||||||
|
return 1;
|
||||||
|
case HOSTAPD_CHAN_INVALID:
|
||||||
|
default:
|
||||||
|
hostapd_notify_bad_chans(iface);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char * hostapd_hw_mode_txt(int mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case HOSTAPD_MODE_IEEE80211A:
|
||||||
|
return "IEEE 802.11a";
|
||||||
|
case HOSTAPD_MODE_IEEE80211B:
|
||||||
|
return "IEEE 802.11b";
|
||||||
|
case HOSTAPD_MODE_IEEE80211G:
|
||||||
|
return "IEEE 802.11g";
|
||||||
|
case HOSTAPD_MODE_IEEE80211AD:
|
||||||
|
return "IEEE 802.11ad";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
|
||||||
|
{
|
||||||
|
return hw_get_freq(hapd->iface->current_mode, chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
|
||||||
|
{
|
||||||
|
int i, channel;
|
||||||
|
struct hostapd_hw_modes *mode;
|
||||||
|
|
||||||
|
if (hapd->iface->current_mode) {
|
||||||
|
channel = hw_get_chan(hapd->iface->current_mode, freq);
|
||||||
|
if (channel)
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check other available modes since the channel list for the current
|
||||||
|
* mode did not include the specified frequency. */
|
||||||
|
if (!hapd->iface->hw_features)
|
||||||
|
return 0;
|
||||||
|
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||||
|
mode = &hapd->iface->hw_features[i];
|
||||||
|
channel = hw_get_chan(mode, freq);
|
||||||
|
if (channel)
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
76
hostapd-2.9/src/ap/hw_features.h
Normal file
76
hostapd-2.9/src/ap/hw_features.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* hostapd / Hardware feature query and different modes
|
||||||
|
* Copyright 2002-2003, Instant802 Networks, Inc.
|
||||||
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||||
|
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_FEATURES_H
|
||||||
|
#define HW_FEATURES_H
|
||||||
|
|
||||||
|
#ifdef NEED_AP_MLME
|
||||||
|
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
||||||
|
size_t num_hw_features);
|
||||||
|
int hostapd_get_hw_features(struct hostapd_iface *iface);
|
||||||
|
int hostapd_acs_completed(struct hostapd_iface *iface, int err);
|
||||||
|
int hostapd_select_hw_mode(struct hostapd_iface *iface);
|
||||||
|
const char * hostapd_hw_mode_txt(int mode);
|
||||||
|
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
|
||||||
|
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
|
||||||
|
int hostapd_check_ht_capab(struct hostapd_iface *iface);
|
||||||
|
int hostapd_prepare_rates(struct hostapd_iface *iface,
|
||||||
|
struct hostapd_hw_modes *mode);
|
||||||
|
void hostapd_stop_setup_timers(struct hostapd_iface *iface);
|
||||||
|
#else /* NEED_AP_MLME */
|
||||||
|
static inline void
|
||||||
|
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
||||||
|
size_t num_hw_features)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return -100;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char * hostapd_hw_mode_txt(int mode)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
|
||||||
|
struct hostapd_hw_modes *mode)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* NEED_AP_MLME */
|
||||||
|
|
||||||
|
#endif /* HW_FEATURES_H */
|
542
hostapd-2.9/src/ap/iapp.c
Normal file
542
hostapd-2.9/src/ap/iapp.c
Normal file
|
@ -0,0 +1,542 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
|
||||||
|
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*
|
||||||
|
* Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
|
||||||
|
* and IEEE has withdrawn it. In other words, it is likely better to look at
|
||||||
|
* using some other mechanism for AP-to-AP communication than extending the
|
||||||
|
* implementation here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* Level 1: no administrative or security support
|
||||||
|
* (e.g., static BSSID to IP address mapping in each AP)
|
||||||
|
* Level 2: support for dynamic mapping of BSSID to IP address
|
||||||
|
* Level 3: support for encryption and authentication of IAPP messages
|
||||||
|
* - add support for MOVE-notify and MOVE-response (this requires support for
|
||||||
|
* finding out IP address for previous AP using RADIUS)
|
||||||
|
* - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
|
||||||
|
* reassociation to another AP
|
||||||
|
* - implement counters etc. for IAPP MIB
|
||||||
|
* - verify endianness of fields in IAPP messages; are they big-endian as
|
||||||
|
* used here?
|
||||||
|
* - RADIUS connection for AP registration and BSSID to IP address mapping
|
||||||
|
* - TCP connection for IAPP MOVE, CACHE
|
||||||
|
* - broadcast ESP for IAPP ADD-notify
|
||||||
|
* - ESP for IAPP MOVE messages
|
||||||
|
* - security block sending/processing
|
||||||
|
* - IEEE 802.11 context transfer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <netpacket/packet.h>
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "iapp.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define IAPP_MULTICAST "224.0.1.178"
|
||||||
|
#define IAPP_UDP_PORT 3517
|
||||||
|
#define IAPP_TCP_PORT 3517
|
||||||
|
|
||||||
|
struct iapp_hdr {
|
||||||
|
u8 version;
|
||||||
|
u8 command;
|
||||||
|
be16 identifier;
|
||||||
|
be16 length;
|
||||||
|
/* followed by length-6 octets of data */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
#define IAPP_VERSION 0
|
||||||
|
|
||||||
|
enum IAPP_COMMAND {
|
||||||
|
IAPP_CMD_ADD_notify = 0,
|
||||||
|
IAPP_CMD_MOVE_notify = 1,
|
||||||
|
IAPP_CMD_MOVE_response = 2,
|
||||||
|
IAPP_CMD_Send_Security_Block = 3,
|
||||||
|
IAPP_CMD_ACK_Security_Block = 4,
|
||||||
|
IAPP_CMD_CACHE_notify = 5,
|
||||||
|
IAPP_CMD_CACHE_response = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ADD-notify - multicast UDP on the local LAN */
|
||||||
|
struct iapp_add_notify {
|
||||||
|
u8 addr_len; /* ETH_ALEN */
|
||||||
|
u8 reserved;
|
||||||
|
u8 mac_addr[ETH_ALEN];
|
||||||
|
be16 seq_num;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
|
||||||
|
struct iapp_layer2_update {
|
||||||
|
u8 da[ETH_ALEN]; /* broadcast */
|
||||||
|
u8 sa[ETH_ALEN]; /* STA addr */
|
||||||
|
be16 len; /* 6 */
|
||||||
|
u8 dsap; /* null DSAP address */
|
||||||
|
u8 ssap; /* null SSAP address, CR=Response */
|
||||||
|
u8 control;
|
||||||
|
u8 xid_info[3];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/* MOVE-notify - unicast TCP */
|
||||||
|
struct iapp_move_notify {
|
||||||
|
u8 addr_len; /* ETH_ALEN */
|
||||||
|
u8 reserved;
|
||||||
|
u8 mac_addr[ETH_ALEN];
|
||||||
|
u16 seq_num;
|
||||||
|
u16 ctx_block_len;
|
||||||
|
/* followed by ctx_block_len bytes */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/* MOVE-response - unicast TCP */
|
||||||
|
struct iapp_move_response {
|
||||||
|
u8 addr_len; /* ETH_ALEN */
|
||||||
|
u8 status;
|
||||||
|
u8 mac_addr[ETH_ALEN];
|
||||||
|
u16 seq_num;
|
||||||
|
u16 ctx_block_len;
|
||||||
|
/* followed by ctx_block_len bytes */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IAPP_MOVE_SUCCESSFUL = 0,
|
||||||
|
IAPP_MOVE_DENIED = 1,
|
||||||
|
IAPP_MOVE_STALE_MOVE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* CACHE-notify */
|
||||||
|
struct iapp_cache_notify {
|
||||||
|
u8 addr_len; /* ETH_ALEN */
|
||||||
|
u8 reserved;
|
||||||
|
u8 mac_addr[ETH_ALEN];
|
||||||
|
u16 seq_num;
|
||||||
|
u8 current_ap[ETH_ALEN];
|
||||||
|
u16 ctx_block_len;
|
||||||
|
/* ctx_block_len bytes of context block followed by 16-bit context
|
||||||
|
* timeout */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/* CACHE-response - unicast TCP */
|
||||||
|
struct iapp_cache_response {
|
||||||
|
u8 addr_len; /* ETH_ALEN */
|
||||||
|
u8 status;
|
||||||
|
u8 mac_addr[ETH_ALEN];
|
||||||
|
u16 seq_num;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IAPP_CACHE_SUCCESSFUL = 0,
|
||||||
|
IAPP_CACHE_STALE_CACHE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Send-Security-Block - unicast TCP */
|
||||||
|
struct iapp_send_security_block {
|
||||||
|
u8 iv[8];
|
||||||
|
u16 sec_block_len;
|
||||||
|
/* followed by sec_block_len bytes of security block */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/* ACK-Security-Block - unicast TCP */
|
||||||
|
struct iapp_ack_security_block {
|
||||||
|
u8 iv[8];
|
||||||
|
u8 new_ap_ack_authenticator[48];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
struct iapp_data {
|
||||||
|
struct hostapd_data *hapd;
|
||||||
|
u16 identifier; /* next IAPP identifier */
|
||||||
|
struct in_addr own, multicast;
|
||||||
|
int udp_sock;
|
||||||
|
int packet_sock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
struct iapp_hdr *hdr;
|
||||||
|
struct iapp_add_notify *add;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
/* Send IAPP ADD-notify to remove possible association from other APs
|
||||||
|
*/
|
||||||
|
|
||||||
|
hdr = (struct iapp_hdr *) buf;
|
||||||
|
hdr->version = IAPP_VERSION;
|
||||||
|
hdr->command = IAPP_CMD_ADD_notify;
|
||||||
|
hdr->identifier = host_to_be16(iapp->identifier++);
|
||||||
|
hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
|
||||||
|
|
||||||
|
add = (struct iapp_add_notify *) (hdr + 1);
|
||||||
|
add->addr_len = ETH_ALEN;
|
||||||
|
add->reserved = 0;
|
||||||
|
os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
|
||||||
|
|
||||||
|
add->seq_num = host_to_be16(seq_num);
|
||||||
|
|
||||||
|
os_memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = iapp->multicast.s_addr;
|
||||||
|
addr.sin_port = htons(IAPP_UDP_PORT);
|
||||||
|
if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
|
||||||
|
(struct sockaddr *) &addr, sizeof(addr)) < 0)
|
||||||
|
wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
|
||||||
|
{
|
||||||
|
struct iapp_layer2_update msg;
|
||||||
|
|
||||||
|
/* Send Level 2 Update Frame to update forwarding tables in layer 2
|
||||||
|
* bridge devices */
|
||||||
|
|
||||||
|
/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
|
||||||
|
* Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
|
||||||
|
|
||||||
|
os_memset(msg.da, 0xff, ETH_ALEN);
|
||||||
|
os_memcpy(msg.sa, addr, ETH_ALEN);
|
||||||
|
msg.len = host_to_be16(6);
|
||||||
|
msg.dsap = 0; /* NULL DSAP address */
|
||||||
|
msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
|
||||||
|
msg.control = 0xaf; /* XID response lsb.1111F101.
|
||||||
|
* F=0 (no poll command; unsolicited frame) */
|
||||||
|
msg.xid_info[0] = 0x81; /* XID format identifier */
|
||||||
|
msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
|
||||||
|
msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
|
||||||
|
* FIX: what is correct RW with 802.11? */
|
||||||
|
|
||||||
|
if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
|
||||||
|
wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iapp_new_station - IAPP processing for a new STA
|
||||||
|
* @iapp: IAPP data
|
||||||
|
* @sta: The associated station
|
||||||
|
*/
|
||||||
|
void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
u16 seq = 0; /* TODO */
|
||||||
|
|
||||||
|
if (iapp == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
|
||||||
|
hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
|
||||||
|
HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
|
||||||
|
iapp_send_layer2_update(iapp, sta->addr);
|
||||||
|
iapp_send_add(iapp, sta->addr, seq);
|
||||||
|
|
||||||
|
/* TODO: If this was reassociation:
|
||||||
|
* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
|
||||||
|
* Context Block, Timeout)
|
||||||
|
* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
|
||||||
|
* IP address */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void iapp_process_add_notify(struct iapp_data *iapp,
|
||||||
|
struct sockaddr_in *from,
|
||||||
|
struct iapp_hdr *hdr, int len)
|
||||||
|
{
|
||||||
|
struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
if (len != sizeof(*add)) {
|
||||||
|
wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
|
||||||
|
len, (unsigned long) sizeof(*add));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta = ap_get_sta(iapp->hapd, add->mac_addr);
|
||||||
|
|
||||||
|
/* IAPP-ADD.indication(MAC Address, Sequence Number) */
|
||||||
|
hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
|
||||||
|
HOSTAPD_LEVEL_INFO,
|
||||||
|
"Received IAPP ADD-notify (seq# %d) from %s:%d%s",
|
||||||
|
be_to_host16(add->seq_num),
|
||||||
|
inet_ntoa(from->sin_addr), ntohs(from->sin_port),
|
||||||
|
sta ? "" : " (STA not found)");
|
||||||
|
|
||||||
|
if (!sta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* TODO: could use seq_num to try to determine whether last association
|
||||||
|
* to this AP is newer than the one advertised in IAPP-ADD. Although,
|
||||||
|
* this is not really a reliable verification. */
|
||||||
|
|
||||||
|
hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"Removing STA due to IAPP ADD-notify");
|
||||||
|
ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iapp_receive_udp - Process IAPP UDP frames
|
||||||
|
* @sock: File descriptor for the socket
|
||||||
|
* @eloop_ctx: IAPP data (struct iapp_data *)
|
||||||
|
* @sock_ctx: Not used
|
||||||
|
*/
|
||||||
|
static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
|
{
|
||||||
|
struct iapp_data *iapp = eloop_ctx;
|
||||||
|
int len, hlen;
|
||||||
|
unsigned char buf[128];
|
||||||
|
struct sockaddr_in from;
|
||||||
|
socklen_t fromlen;
|
||||||
|
struct iapp_hdr *hdr;
|
||||||
|
|
||||||
|
/* Handle incoming IAPP frames (over UDP/IP) */
|
||||||
|
|
||||||
|
fromlen = sizeof(from);
|
||||||
|
len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
|
||||||
|
(struct sockaddr *) &from, &fromlen);
|
||||||
|
if (len < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from.sin_addr.s_addr == iapp->own.s_addr)
|
||||||
|
return; /* ignore own IAPP messages */
|
||||||
|
|
||||||
|
hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"Received %d byte IAPP frame from %s%s\n",
|
||||||
|
len, inet_ntoa(from.sin_addr),
|
||||||
|
len < (int) sizeof(*hdr) ? " (too short)" : "");
|
||||||
|
|
||||||
|
if (len < (int) sizeof(*hdr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hdr = (struct iapp_hdr *) buf;
|
||||||
|
hlen = be_to_host16(hdr->length);
|
||||||
|
hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"RX: version=%d command=%d id=%d len=%d\n",
|
||||||
|
hdr->version, hdr->command,
|
||||||
|
be_to_host16(hdr->identifier), hlen);
|
||||||
|
if (hdr->version != IAPP_VERSION) {
|
||||||
|
wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
|
||||||
|
hdr->version);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hlen > len) {
|
||||||
|
wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
|
||||||
|
hlen, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hlen < len) {
|
||||||
|
wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
|
||||||
|
len - hlen);
|
||||||
|
len = hlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (hdr->command) {
|
||||||
|
case IAPP_CMD_ADD_notify:
|
||||||
|
iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
|
||||||
|
break;
|
||||||
|
case IAPP_CMD_MOVE_notify:
|
||||||
|
/* TODO: MOVE is using TCP; so move this to TCP handler once it
|
||||||
|
* is implemented.. */
|
||||||
|
/* IAPP-MOVE.indication(MAC Address, New BSSID,
|
||||||
|
* Sequence Number, AP Address, Context Block) */
|
||||||
|
/* TODO: process */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
|
||||||
|
{
|
||||||
|
struct ifreq ifr;
|
||||||
|
struct sockaddr_ll addr;
|
||||||
|
int ifindex;
|
||||||
|
struct sockaddr_in *paddr, uaddr;
|
||||||
|
struct iapp_data *iapp;
|
||||||
|
struct ip_mreqn mreq;
|
||||||
|
int reuseaddr = 1;
|
||||||
|
|
||||||
|
iapp = os_zalloc(sizeof(*iapp));
|
||||||
|
if (iapp == NULL)
|
||||||
|
return NULL;
|
||||||
|
iapp->hapd = hapd;
|
||||||
|
iapp->udp_sock = iapp->packet_sock = -1;
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* open socket for sending and receiving IAPP frames over TCP
|
||||||
|
*/
|
||||||
|
|
||||||
|
iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (iapp->udp_sock < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memset(&ifr, 0, sizeof(ifr));
|
||||||
|
os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
|
||||||
|
if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ifindex = ifr.ifr_ifindex;
|
||||||
|
|
||||||
|
if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
paddr = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||||
|
if (paddr->sin_family != AF_INET) {
|
||||||
|
wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
|
||||||
|
paddr->sin_family);
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
iapp->own.s_addr = paddr->sin_addr.s_addr;
|
||||||
|
|
||||||
|
if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
paddr = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||||
|
if (paddr->sin_family != AF_INET) {
|
||||||
|
wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
|
||||||
|
paddr->sin_family);
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
inet_aton(IAPP_MULTICAST, &iapp->multicast);
|
||||||
|
|
||||||
|
os_memset(&uaddr, 0, sizeof(uaddr));
|
||||||
|
uaddr.sin_family = AF_INET;
|
||||||
|
uaddr.sin_port = htons(IAPP_UDP_PORT);
|
||||||
|
|
||||||
|
if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
|
||||||
|
sizeof(reuseaddr)) < 0) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
/*
|
||||||
|
* Ignore this and try to continue. This is fine for single
|
||||||
|
* BSS cases, but may fail if multiple BSSes enable IAPP.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
|
||||||
|
sizeof(uaddr)) < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memset(&mreq, 0, sizeof(mreq));
|
||||||
|
mreq.imr_multiaddr = iapp->multicast;
|
||||||
|
mreq.imr_address.s_addr = INADDR_ANY;
|
||||||
|
mreq.imr_ifindex = 0;
|
||||||
|
if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
|
||||||
|
sizeof(mreq)) < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||||
|
if (iapp->packet_sock < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sll_family = AF_PACKET;
|
||||||
|
addr.sll_ifindex = ifindex;
|
||||||
|
if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
|
||||||
|
sizeof(addr)) < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
|
||||||
|
iapp, NULL)) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
|
||||||
|
iapp_deinit(iapp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
|
||||||
|
|
||||||
|
/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
|
||||||
|
* RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
|
||||||
|
* be openned only after receiving Initiate-Accept. If Initiate-Reject
|
||||||
|
* is received, IAPP is not started. */
|
||||||
|
|
||||||
|
return iapp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void iapp_deinit(struct iapp_data *iapp)
|
||||||
|
{
|
||||||
|
struct ip_mreqn mreq;
|
||||||
|
|
||||||
|
if (iapp == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (iapp->udp_sock >= 0) {
|
||||||
|
os_memset(&mreq, 0, sizeof(mreq));
|
||||||
|
mreq.imr_multiaddr = iapp->multicast;
|
||||||
|
mreq.imr_address.s_addr = INADDR_ANY;
|
||||||
|
mreq.imr_ifindex = 0;
|
||||||
|
if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
|
||||||
|
&mreq, sizeof(mreq)) < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
eloop_unregister_read_sock(iapp->udp_sock);
|
||||||
|
close(iapp->udp_sock);
|
||||||
|
}
|
||||||
|
if (iapp->packet_sock >= 0) {
|
||||||
|
eloop_unregister_read_sock(iapp->packet_sock);
|
||||||
|
close(iapp->packet_sock);
|
||||||
|
}
|
||||||
|
os_free(iapp);
|
||||||
|
}
|
39
hostapd-2.9/src/ap/iapp.h
Normal file
39
hostapd-2.9/src/ap/iapp.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
|
||||||
|
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IAPP_H
|
||||||
|
#define IAPP_H
|
||||||
|
|
||||||
|
struct iapp_data;
|
||||||
|
|
||||||
|
#ifdef CONFIG_IAPP
|
||||||
|
|
||||||
|
void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
|
||||||
|
struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
|
||||||
|
void iapp_deinit(struct iapp_data *iapp);
|
||||||
|
|
||||||
|
#else /* CONFIG_IAPP */
|
||||||
|
|
||||||
|
static inline void iapp_new_station(struct iapp_data *iapp,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
|
||||||
|
const char *iface)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void iapp_deinit(struct iapp_data *iapp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_IAPP */
|
||||||
|
|
||||||
|
#endif /* IAPP_H */
|
5278
hostapd-2.9/src/ap/ieee802_11.c
Normal file
5278
hostapd-2.9/src/ap/ieee802_11.c
Normal file
File diff suppressed because it is too large
Load diff
196
hostapd-2.9/src/ap/ieee802_11.h
Normal file
196
hostapd-2.9/src/ap/ieee802_11.h
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11 Management
|
||||||
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IEEE802_11_H
|
||||||
|
#define IEEE802_11_H
|
||||||
|
|
||||||
|
struct hostapd_iface;
|
||||||
|
struct hostapd_data;
|
||||||
|
struct sta_info;
|
||||||
|
struct hostapd_frame_info;
|
||||||
|
struct ieee80211_ht_capabilities;
|
||||||
|
struct ieee80211_vht_capabilities;
|
||||||
|
struct ieee80211_mgmt;
|
||||||
|
struct vlan_description;
|
||||||
|
struct hostapd_sta_wpa_psk_short;
|
||||||
|
enum ieee80211_op_mode;
|
||||||
|
|
||||||
|
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
||||||
|
struct hostapd_frame_info *fi);
|
||||||
|
void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
||||||
|
u16 stype, int ok);
|
||||||
|
void hostapd_2040_coex_action(struct hostapd_data *hapd,
|
||||||
|
const struct ieee80211_mgmt *mgmt, size_t len);
|
||||||
|
#ifdef NEED_AP_MLME
|
||||||
|
int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
|
||||||
|
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
#else /* NEED_AP_MLME */
|
||||||
|
static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
|
||||||
|
size_t buflen)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* NEED_AP_MLME */
|
||||||
|
u16 hostapd_own_capab_info(struct hostapd_data *hapd);
|
||||||
|
void ap_ht2040_timeout(void *eloop_data, void *user_data);
|
||||||
|
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
|
||||||
|
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
|
||||||
|
enum ieee80211_op_mode opmode);
|
||||||
|
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
|
||||||
|
int hostapd_ht_operation_update(struct hostapd_iface *iface);
|
||||||
|
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
|
||||||
|
const u8 *addr, const u8 *trans_id);
|
||||||
|
void hostapd_get_ht_capab(struct hostapd_data *hapd,
|
||||||
|
struct ieee80211_ht_capabilities *ht_cap,
|
||||||
|
struct ieee80211_ht_capabilities *neg_ht_cap);
|
||||||
|
void hostapd_get_vht_capab(struct hostapd_data *hapd,
|
||||||
|
struct ieee80211_vht_capabilities *vht_cap,
|
||||||
|
struct ieee80211_vht_capabilities *neg_vht_cap);
|
||||||
|
void hostapd_get_he_capab(struct hostapd_data *hapd,
|
||||||
|
const struct ieee80211_he_capabilities *he_cap,
|
||||||
|
struct ieee80211_he_capabilities *neg_he_cap,
|
||||||
|
size_t he_capab_len);
|
||||||
|
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *ht_capab);
|
||||||
|
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *ie, size_t len);
|
||||||
|
|
||||||
|
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
|
||||||
|
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
|
||||||
|
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *vht_capab);
|
||||||
|
u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *vht_oper);
|
||||||
|
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *vht_opmode);
|
||||||
|
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
enum ieee80211_op_mode opmode, const u8 *he_capab,
|
||||||
|
size_t he_capab_len);
|
||||||
|
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
const u8 *buf, size_t len, int ack);
|
||||||
|
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
|
||||||
|
const u8 *data, size_t len, int ack);
|
||||||
|
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
int wds);
|
||||||
|
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, u8 *eid);
|
||||||
|
void ieee802_11_sa_query_action(struct hostapd_data *hapd,
|
||||||
|
const struct ieee80211_mgmt *mgmt,
|
||||||
|
size_t len);
|
||||||
|
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
int hostapd_update_time_adv(struct hostapd_data *hapd);
|
||||||
|
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
|
||||||
|
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
|
||||||
|
|
||||||
|
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
#ifdef CONFIG_SAE
|
||||||
|
void sae_clear_retransmit_timer(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta);
|
||||||
|
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
#else /* CONFIG_SAE */
|
||||||
|
static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_SAE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_MBO
|
||||||
|
|
||||||
|
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
|
||||||
|
|
||||||
|
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
|
||||||
|
size_t len, int delta);
|
||||||
|
|
||||||
|
#else /* CONFIG_MBO */
|
||||||
|
|
||||||
|
static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_MBO */
|
||||||
|
|
||||||
|
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
|
||||||
|
const u8 *supp_op_classes,
|
||||||
|
size_t supp_op_classes_len);
|
||||||
|
|
||||||
|
u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
|
||||||
|
void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int success,
|
||||||
|
struct wpabuf *erp_resp,
|
||||||
|
const u8 *msk, size_t msk_len);
|
||||||
|
u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *owe_dh, u8 owe_dh_len,
|
||||||
|
u8 *owe_buf, size_t owe_buf_len, u16 *reason);
|
||||||
|
u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *rsn_ie, size_t rsn_ie_len,
|
||||||
|
const u8 *owe_dh, size_t owe_dh_len);
|
||||||
|
u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
|
||||||
|
const u8 *rsn_ie, size_t rsn_ie_len,
|
||||||
|
const u8 *owe_dh, size_t owe_dh_len);
|
||||||
|
void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
|
||||||
|
void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *pos, size_t len, u16 auth_alg,
|
||||||
|
u16 auth_transaction, u16 status_code,
|
||||||
|
void (*cb)(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
u16 resp, struct wpabuf *data, int pub));
|
||||||
|
|
||||||
|
size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
|
||||||
|
u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
|
||||||
|
int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
const u8 *msg, size_t len, u32 *session_timeout,
|
||||||
|
u32 *acct_interim_interval,
|
||||||
|
struct vlan_description *vlan_id,
|
||||||
|
struct hostapd_sta_wpa_psk_short **psk,
|
||||||
|
char **identity, char **radius_cui,
|
||||||
|
int is_probe_req);
|
||||||
|
|
||||||
|
int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
|
||||||
|
int ap_seg1_idx, int *bandwidth, int *seg1_idx);
|
||||||
|
|
||||||
|
void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
|
||||||
|
|
||||||
|
#endif /* IEEE802_11_H */
|
701
hostapd-2.9/src/ap/ieee802_11_auth.c
Normal file
701
hostapd-2.9/src/ap/ieee802_11_auth.c
Normal file
|
@ -0,0 +1,701 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11 authentication (ACL)
|
||||||
|
* Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*
|
||||||
|
* Access control list for IEEE 802.11 authentication can uses statically
|
||||||
|
* configured ACL from configuration files or an external RADIUS server.
|
||||||
|
* Results from external RADIUS queries are cached to allow faster
|
||||||
|
* authentication frame processing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "radius/radius.h"
|
||||||
|
#include "radius/radius_client.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "ieee802_1x.h"
|
||||||
|
#include "ieee802_11_auth.h"
|
||||||
|
|
||||||
|
#define RADIUS_ACL_TIMEOUT 30
|
||||||
|
|
||||||
|
|
||||||
|
struct hostapd_cached_radius_acl {
|
||||||
|
struct os_reltime timestamp;
|
||||||
|
macaddr addr;
|
||||||
|
int accepted; /* HOSTAPD_ACL_* */
|
||||||
|
struct hostapd_cached_radius_acl *next;
|
||||||
|
u32 session_timeout;
|
||||||
|
u32 acct_interim_interval;
|
||||||
|
struct vlan_description vlan_id;
|
||||||
|
struct hostapd_sta_wpa_psk_short *psk;
|
||||||
|
char *identity;
|
||||||
|
char *radius_cui;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct hostapd_acl_query_data {
|
||||||
|
struct os_reltime timestamp;
|
||||||
|
u8 radius_id;
|
||||||
|
macaddr addr;
|
||||||
|
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
|
||||||
|
size_t auth_msg_len;
|
||||||
|
struct hostapd_acl_query_data *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_RADIUS
|
||||||
|
static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
|
||||||
|
{
|
||||||
|
os_free(e->identity);
|
||||||
|
os_free(e->radius_cui);
|
||||||
|
hostapd_free_psk_list(e->psk);
|
||||||
|
os_free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
|
||||||
|
{
|
||||||
|
struct hostapd_cached_radius_acl *prev;
|
||||||
|
|
||||||
|
while (acl_cache) {
|
||||||
|
prev = acl_cache;
|
||||||
|
acl_cache = acl_cache->next;
|
||||||
|
hostapd_acl_cache_free_entry(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
|
||||||
|
struct hostapd_sta_wpa_psk_short *src)
|
||||||
|
{
|
||||||
|
if (!psk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (src)
|
||||||
|
src->ref++;
|
||||||
|
|
||||||
|
*psk = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
u32 *session_timeout,
|
||||||
|
u32 *acct_interim_interval,
|
||||||
|
struct vlan_description *vlan_id,
|
||||||
|
struct hostapd_sta_wpa_psk_short **psk,
|
||||||
|
char **identity, char **radius_cui)
|
||||||
|
{
|
||||||
|
struct hostapd_cached_radius_acl *entry;
|
||||||
|
struct os_reltime now;
|
||||||
|
|
||||||
|
os_get_reltime(&now);
|
||||||
|
|
||||||
|
for (entry = hapd->acl_cache; entry; entry = entry->next) {
|
||||||
|
if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (os_reltime_expired(&now, &entry->timestamp,
|
||||||
|
RADIUS_ACL_TIMEOUT))
|
||||||
|
return -1; /* entry has expired */
|
||||||
|
if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
|
||||||
|
if (session_timeout)
|
||||||
|
*session_timeout = entry->session_timeout;
|
||||||
|
if (acct_interim_interval)
|
||||||
|
*acct_interim_interval =
|
||||||
|
entry->acct_interim_interval;
|
||||||
|
if (vlan_id)
|
||||||
|
*vlan_id = entry->vlan_id;
|
||||||
|
copy_psk_list(psk, entry->psk);
|
||||||
|
if (identity) {
|
||||||
|
if (entry->identity)
|
||||||
|
*identity = os_strdup(entry->identity);
|
||||||
|
else
|
||||||
|
*identity = NULL;
|
||||||
|
}
|
||||||
|
if (radius_cui) {
|
||||||
|
if (entry->radius_cui)
|
||||||
|
*radius_cui = os_strdup(entry->radius_cui);
|
||||||
|
else
|
||||||
|
*radius_cui = NULL;
|
||||||
|
}
|
||||||
|
return entry->accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
|
||||||
|
{
|
||||||
|
if (query == NULL)
|
||||||
|
return;
|
||||||
|
os_free(query->auth_msg);
|
||||||
|
os_free(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_RADIUS
|
||||||
|
static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
struct hostapd_acl_query_data *query)
|
||||||
|
{
|
||||||
|
struct radius_msg *msg;
|
||||||
|
char buf[128];
|
||||||
|
|
||||||
|
query->radius_id = radius_client_get_id(hapd->radius);
|
||||||
|
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
|
||||||
|
if (msg == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (radius_msg_make_authenticator(msg) < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "Could not make Request Authenticator");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
|
||||||
|
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
|
||||||
|
os_strlen(buf))) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Could not add User-Name");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!radius_msg_add_attr_user_password(
|
||||||
|
msg, (u8 *) buf, os_strlen(buf),
|
||||||
|
hapd->conf->radius->auth_server->shared_secret,
|
||||||
|
hapd->conf->radius->auth_server->shared_secret_len)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Could not add User-Password");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
|
||||||
|
NULL, msg) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
|
||||||
|
MAC2STR(addr));
|
||||||
|
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
|
||||||
|
(u8 *) buf, os_strlen(buf))) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
|
||||||
|
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
|
||||||
|
(u8 *) buf, os_strlen(buf))) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
|
||||||
|
goto fail;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
radius_msg_free(msg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_check_acl - Check a specified STA against accept/deny ACLs
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* @addr: MAC address of the STA
|
||||||
|
* @vlan_id: Buffer for returning VLAN ID
|
||||||
|
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
|
||||||
|
*/
|
||||||
|
int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
struct vlan_description *vlan_id)
|
||||||
|
{
|
||||||
|
if (hostapd_maclist_found(hapd->conf->accept_mac,
|
||||||
|
hapd->conf->num_accept_mac, addr, vlan_id))
|
||||||
|
return HOSTAPD_ACL_ACCEPT;
|
||||||
|
|
||||||
|
if (hostapd_maclist_found(hapd->conf->deny_mac,
|
||||||
|
hapd->conf->num_deny_mac, addr, vlan_id))
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
|
||||||
|
if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
|
||||||
|
return HOSTAPD_ACL_ACCEPT;
|
||||||
|
if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
|
||||||
|
return HOSTAPD_ACL_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_allowed_address - Check whether a specified STA can be authenticated
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* @addr: MAC address of the STA
|
||||||
|
* @msg: Authentication message
|
||||||
|
* @len: Length of msg in octets
|
||||||
|
* @session_timeout: Buffer for returning session timeout (from RADIUS)
|
||||||
|
* @acct_interim_interval: Buffer for returning account interval (from RADIUS)
|
||||||
|
* @vlan_id: Buffer for returning VLAN ID
|
||||||
|
* @psk: Linked list buffer for returning WPA PSK
|
||||||
|
* @identity: Buffer for returning identity (from RADIUS)
|
||||||
|
* @radius_cui: Buffer for returning CUI (from RADIUS)
|
||||||
|
* @is_probe_req: Whether this query for a Probe Request frame
|
||||||
|
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the returned *identity and *radius_cui
|
||||||
|
* values with os_free().
|
||||||
|
*/
|
||||||
|
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
const u8 *msg, size_t len, u32 *session_timeout,
|
||||||
|
u32 *acct_interim_interval,
|
||||||
|
struct vlan_description *vlan_id,
|
||||||
|
struct hostapd_sta_wpa_psk_short **psk,
|
||||||
|
char **identity, char **radius_cui,
|
||||||
|
int is_probe_req)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (session_timeout)
|
||||||
|
*session_timeout = 0;
|
||||||
|
if (acct_interim_interval)
|
||||||
|
*acct_interim_interval = 0;
|
||||||
|
if (vlan_id)
|
||||||
|
os_memset(vlan_id, 0, sizeof(*vlan_id));
|
||||||
|
if (psk)
|
||||||
|
*psk = NULL;
|
||||||
|
if (identity)
|
||||||
|
*identity = NULL;
|
||||||
|
if (radius_cui)
|
||||||
|
*radius_cui = NULL;
|
||||||
|
|
||||||
|
res = hostapd_check_acl(hapd, addr, vlan_id);
|
||||||
|
if (res != HOSTAPD_ACL_PENDING)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
|
||||||
|
#ifdef CONFIG_NO_RADIUS
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
#else /* CONFIG_NO_RADIUS */
|
||||||
|
struct hostapd_acl_query_data *query;
|
||||||
|
|
||||||
|
if (is_probe_req) {
|
||||||
|
/* Skip RADIUS queries for Probe Request frames to avoid
|
||||||
|
* excessive load on the authentication server. */
|
||||||
|
return HOSTAPD_ACL_ACCEPT;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
|
||||||
|
vlan_id = NULL;
|
||||||
|
|
||||||
|
/* Check whether ACL cache has an entry for this station */
|
||||||
|
res = hostapd_acl_cache_get(hapd, addr, session_timeout,
|
||||||
|
acct_interim_interval, vlan_id, psk,
|
||||||
|
identity, radius_cui);
|
||||||
|
if (res == HOSTAPD_ACL_ACCEPT ||
|
||||||
|
res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
|
||||||
|
return res;
|
||||||
|
if (res == HOSTAPD_ACL_REJECT)
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
|
||||||
|
query = hapd->acl_queries;
|
||||||
|
while (query) {
|
||||||
|
if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
|
||||||
|
/* pending query in RADIUS retransmit queue;
|
||||||
|
* do not generate a new one */
|
||||||
|
if (identity) {
|
||||||
|
os_free(*identity);
|
||||||
|
*identity = NULL;
|
||||||
|
}
|
||||||
|
if (radius_cui) {
|
||||||
|
os_free(*radius_cui);
|
||||||
|
*radius_cui = NULL;
|
||||||
|
}
|
||||||
|
return HOSTAPD_ACL_PENDING;
|
||||||
|
}
|
||||||
|
query = query->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hapd->conf->radius->auth_server)
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
|
||||||
|
/* No entry in the cache - query external RADIUS server */
|
||||||
|
query = os_zalloc(sizeof(*query));
|
||||||
|
if (query == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "malloc for query data failed");
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
}
|
||||||
|
os_get_reltime(&query->timestamp);
|
||||||
|
os_memcpy(query->addr, addr, ETH_ALEN);
|
||||||
|
if (hostapd_radius_acl_query(hapd, addr, query)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
|
||||||
|
"for ACL query.");
|
||||||
|
hostapd_acl_query_free(query);
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
query->auth_msg = os_memdup(msg, len);
|
||||||
|
if (query->auth_msg == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
|
||||||
|
"auth frame.");
|
||||||
|
hostapd_acl_query_free(query);
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
}
|
||||||
|
query->auth_msg_len = len;
|
||||||
|
query->next = hapd->acl_queries;
|
||||||
|
hapd->acl_queries = query;
|
||||||
|
|
||||||
|
/* Queued data will be processed in hostapd_acl_recv_radius()
|
||||||
|
* when RADIUS server replies to the sent Access-Request. */
|
||||||
|
return HOSTAPD_ACL_PENDING;
|
||||||
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
}
|
||||||
|
|
||||||
|
return HOSTAPD_ACL_REJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_RADIUS
|
||||||
|
static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
|
||||||
|
struct os_reltime *now)
|
||||||
|
{
|
||||||
|
struct hostapd_cached_radius_acl *prev, *entry, *tmp;
|
||||||
|
|
||||||
|
prev = NULL;
|
||||||
|
entry = hapd->acl_cache;
|
||||||
|
|
||||||
|
while (entry) {
|
||||||
|
if (os_reltime_expired(now, &entry->timestamp,
|
||||||
|
RADIUS_ACL_TIMEOUT)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
|
||||||
|
" has expired.", MAC2STR(entry->addr));
|
||||||
|
if (prev)
|
||||||
|
prev->next = entry->next;
|
||||||
|
else
|
||||||
|
hapd->acl_cache = entry->next;
|
||||||
|
hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
|
||||||
|
tmp = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
hostapd_acl_cache_free_entry(tmp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
|
||||||
|
struct os_reltime *now)
|
||||||
|
{
|
||||||
|
struct hostapd_acl_query_data *prev, *entry, *tmp;
|
||||||
|
|
||||||
|
prev = NULL;
|
||||||
|
entry = hapd->acl_queries;
|
||||||
|
|
||||||
|
while (entry) {
|
||||||
|
if (os_reltime_expired(now, &entry->timestamp,
|
||||||
|
RADIUS_ACL_TIMEOUT)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
|
||||||
|
" has expired.", MAC2STR(entry->addr));
|
||||||
|
if (prev)
|
||||||
|
prev->next = entry->next;
|
||||||
|
else
|
||||||
|
hapd->acl_queries = entry->next;
|
||||||
|
|
||||||
|
tmp = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
hostapd_acl_query_free(tmp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_acl_expire - ACL cache expiration callback
|
||||||
|
* @hapd: struct hostapd_data *
|
||||||
|
*/
|
||||||
|
void hostapd_acl_expire(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct os_reltime now;
|
||||||
|
|
||||||
|
os_get_reltime(&now);
|
||||||
|
hostapd_acl_expire_cache(hapd, &now);
|
||||||
|
hostapd_acl_expire_queries(hapd, &now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void decode_tunnel_passwords(struct hostapd_data *hapd,
|
||||||
|
const u8 *shared_secret,
|
||||||
|
size_t shared_secret_len,
|
||||||
|
struct radius_msg *msg,
|
||||||
|
struct radius_msg *req,
|
||||||
|
struct hostapd_cached_radius_acl *cache)
|
||||||
|
{
|
||||||
|
int passphraselen;
|
||||||
|
char *passphrase;
|
||||||
|
size_t i;
|
||||||
|
struct hostapd_sta_wpa_psk_short *psk;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode all tunnel passwords as PSK and save them into a linked list.
|
||||||
|
*/
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
passphrase = radius_msg_get_tunnel_password(
|
||||||
|
msg, &passphraselen, shared_secret, shared_secret_len,
|
||||||
|
req, i);
|
||||||
|
/*
|
||||||
|
* Passphrase is NULL iff there is no i-th Tunnel-Password
|
||||||
|
* attribute in msg.
|
||||||
|
*/
|
||||||
|
if (passphrase == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Passphase should be 8..63 chars (to be hashed with SSID)
|
||||||
|
* or 64 chars hex string (no separate hashing with SSID).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (passphraselen < MIN_PASSPHRASE_LEN ||
|
||||||
|
passphraselen > MAX_PASSPHRASE_LEN + 1)
|
||||||
|
goto free_pass;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* passphrase does not contain the NULL termination.
|
||||||
|
* Add it here as pbkdf2_sha1() requires it.
|
||||||
|
*/
|
||||||
|
psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
|
||||||
|
if (psk) {
|
||||||
|
if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
|
||||||
|
(hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
|
||||||
|
hostapd_logger(hapd, cache->addr,
|
||||||
|
HOSTAPD_MODULE_RADIUS,
|
||||||
|
HOSTAPD_LEVEL_WARNING,
|
||||||
|
"invalid hex string (%d chars) in Tunnel-Password",
|
||||||
|
passphraselen);
|
||||||
|
goto skip;
|
||||||
|
} else if (passphraselen <= MAX_PASSPHRASE_LEN) {
|
||||||
|
os_memcpy(psk->passphrase, passphrase,
|
||||||
|
passphraselen);
|
||||||
|
psk->is_passphrase = 1;
|
||||||
|
}
|
||||||
|
psk->next = cache->psk;
|
||||||
|
cache->psk = psk;
|
||||||
|
psk = NULL;
|
||||||
|
}
|
||||||
|
skip:
|
||||||
|
os_free(psk);
|
||||||
|
free_pass:
|
||||||
|
os_free(passphrase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
|
||||||
|
* @msg: RADIUS response message
|
||||||
|
* @req: RADIUS request message
|
||||||
|
* @shared_secret: RADIUS shared secret
|
||||||
|
* @shared_secret_len: Length of shared_secret in octets
|
||||||
|
* @data: Context data (struct hostapd_data *)
|
||||||
|
* Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
|
||||||
|
* was processed here) or RADIUS_RX_UNKNOWN if not.
|
||||||
|
*/
|
||||||
|
static RadiusRxResult
|
||||||
|
hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
|
||||||
|
const u8 *shared_secret, size_t shared_secret_len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = data;
|
||||||
|
struct hostapd_acl_query_data *query, *prev;
|
||||||
|
struct hostapd_cached_radius_acl *cache;
|
||||||
|
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
|
||||||
|
|
||||||
|
query = hapd->acl_queries;
|
||||||
|
prev = NULL;
|
||||||
|
while (query) {
|
||||||
|
if (query->radius_id == hdr->identifier)
|
||||||
|
break;
|
||||||
|
prev = query;
|
||||||
|
query = query->next;
|
||||||
|
}
|
||||||
|
if (query == NULL)
|
||||||
|
return RADIUS_RX_UNKNOWN;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
|
||||||
|
"message (id=%d)", query->radius_id);
|
||||||
|
|
||||||
|
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
|
||||||
|
wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
|
||||||
|
"correct authenticator - dropped\n");
|
||||||
|
return RADIUS_RX_INVALID_AUTHENTICATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
|
||||||
|
hdr->code != RADIUS_CODE_ACCESS_REJECT) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
|
||||||
|
"query", hdr->code);
|
||||||
|
return RADIUS_RX_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert Accept/Reject info into ACL cache */
|
||||||
|
cache = os_zalloc(sizeof(*cache));
|
||||||
|
if (cache == NULL) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
os_get_reltime(&cache->timestamp);
|
||||||
|
os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
|
||||||
|
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
|
||||||
|
u8 *buf;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
|
||||||
|
&cache->session_timeout) == 0)
|
||||||
|
cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
|
||||||
|
else
|
||||||
|
cache->accepted = HOSTAPD_ACL_ACCEPT;
|
||||||
|
|
||||||
|
if (radius_msg_get_attr_int32(
|
||||||
|
msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
|
||||||
|
&cache->acct_interim_interval) == 0 &&
|
||||||
|
cache->acct_interim_interval < 60) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Ignored too small "
|
||||||
|
"Acct-Interim-Interval %d for STA " MACSTR,
|
||||||
|
cache->acct_interim_interval,
|
||||||
|
MAC2STR(query->addr));
|
||||||
|
cache->acct_interim_interval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
|
||||||
|
cache->vlan_id.notempty = !!radius_msg_get_vlanid(
|
||||||
|
msg, &cache->vlan_id.untagged,
|
||||||
|
MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged);
|
||||||
|
|
||||||
|
decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
|
||||||
|
msg, req, cache);
|
||||||
|
|
||||||
|
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
|
||||||
|
&buf, &len, NULL) == 0) {
|
||||||
|
cache->identity = os_zalloc(len + 1);
|
||||||
|
if (cache->identity)
|
||||||
|
os_memcpy(cache->identity, buf, len);
|
||||||
|
}
|
||||||
|
if (radius_msg_get_attr_ptr(
|
||||||
|
msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||||
|
&buf, &len, NULL) == 0) {
|
||||||
|
cache->radius_cui = os_zalloc(len + 1);
|
||||||
|
if (cache->radius_cui)
|
||||||
|
os_memcpy(cache->radius_cui, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
|
||||||
|
!cache->psk)
|
||||||
|
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||||
|
|
||||||
|
if (cache->vlan_id.notempty &&
|
||||||
|
!hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) {
|
||||||
|
hostapd_logger(hapd, query->addr,
|
||||||
|
HOSTAPD_MODULE_RADIUS,
|
||||||
|
HOSTAPD_LEVEL_INFO,
|
||||||
|
"Invalid VLAN %d%s received from RADIUS server",
|
||||||
|
cache->vlan_id.untagged,
|
||||||
|
cache->vlan_id.tagged[0] ? "+" : "");
|
||||||
|
os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id));
|
||||||
|
}
|
||||||
|
if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
|
||||||
|
!cache->vlan_id.notempty)
|
||||||
|
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||||
|
} else
|
||||||
|
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||||
|
cache->next = hapd->acl_cache;
|
||||||
|
hapd->acl_cache = cache;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DRIVER_RADIUS_ACL
|
||||||
|
hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
|
||||||
|
cache->session_timeout);
|
||||||
|
#else /* CONFIG_DRIVER_RADIUS_ACL */
|
||||||
|
#ifdef NEED_AP_MLME
|
||||||
|
/* Re-send original authentication frame for 802.11 processing */
|
||||||
|
wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
|
||||||
|
"successful RADIUS ACL query");
|
||||||
|
ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
|
||||||
|
#endif /* NEED_AP_MLME */
|
||||||
|
#endif /* CONFIG_DRIVER_RADIUS_ACL */
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (prev == NULL)
|
||||||
|
hapd->acl_queries = query->next;
|
||||||
|
else
|
||||||
|
prev->next = query->next;
|
||||||
|
|
||||||
|
hostapd_acl_query_free(query);
|
||||||
|
|
||||||
|
return RADIUS_RX_PROCESSED;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_acl_init: Initialize IEEE 802.11 ACL
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int hostapd_acl_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_NO_RADIUS
|
||||||
|
if (radius_client_register(hapd->radius, RADIUS_AUTH,
|
||||||
|
hostapd_acl_recv_radius, hapd))
|
||||||
|
return -1;
|
||||||
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
|
||||||
|
* @hapd: hostapd BSS data
|
||||||
|
*/
|
||||||
|
void hostapd_acl_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct hostapd_acl_query_data *query, *prev;
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_RADIUS
|
||||||
|
hostapd_acl_cache_free(hapd->acl_cache);
|
||||||
|
hapd->acl_cache = NULL;
|
||||||
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
|
query = hapd->acl_queries;
|
||||||
|
hapd->acl_queries = NULL;
|
||||||
|
while (query) {
|
||||||
|
prev = query;
|
||||||
|
query = query->next;
|
||||||
|
hostapd_acl_query_free(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
|
||||||
|
{
|
||||||
|
if (psk && psk->ref) {
|
||||||
|
/* This will be freed when the last reference is dropped. */
|
||||||
|
psk->ref--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (psk) {
|
||||||
|
struct hostapd_sta_wpa_psk_short *prev = psk;
|
||||||
|
psk = psk->next;
|
||||||
|
os_free(prev);
|
||||||
|
}
|
||||||
|
}
|
33
hostapd-2.9/src/ap/ieee802_11_auth.h
Normal file
33
hostapd-2.9/src/ap/ieee802_11_auth.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11 authentication (ACL)
|
||||||
|
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IEEE802_11_AUTH_H
|
||||||
|
#define IEEE802_11_AUTH_H
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HOSTAPD_ACL_REJECT = 0,
|
||||||
|
HOSTAPD_ACL_ACCEPT = 1,
|
||||||
|
HOSTAPD_ACL_PENDING = 2,
|
||||||
|
HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
struct vlan_description *vlan_id);
|
||||||
|
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
const u8 *msg, size_t len, u32 *session_timeout,
|
||||||
|
u32 *acct_interim_interval,
|
||||||
|
struct vlan_description *vlan_id,
|
||||||
|
struct hostapd_sta_wpa_psk_short **psk,
|
||||||
|
char **identity, char **radius_cui,
|
||||||
|
int is_probe_req);
|
||||||
|
int hostapd_acl_init(struct hostapd_data *hapd);
|
||||||
|
void hostapd_acl_deinit(struct hostapd_data *hapd);
|
||||||
|
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
|
||||||
|
void hostapd_acl_expire(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
#endif /* IEEE802_11_AUTH_H */
|
348
hostapd-2.9/src/ap/ieee802_11_he.c
Normal file
348
hostapd-2.9/src/ap/ieee802_11_he.c
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11ax HE
|
||||||
|
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
|
||||||
|
* Copyright (c) 2019 John Crispin <john@phrozen.org>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "beacon.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "dfs.h"
|
||||||
|
|
||||||
|
static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
|
||||||
|
{
|
||||||
|
u8 sz = 0, ru;
|
||||||
|
|
||||||
|
if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
|
||||||
|
HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
|
||||||
|
HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
|
||||||
|
while (ru) {
|
||||||
|
if (ru & 0x1)
|
||||||
|
sz++;
|
||||||
|
ru >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
|
||||||
|
sz = (sz * 6) + 7;
|
||||||
|
if (sz % 8)
|
||||||
|
sz += 8;
|
||||||
|
sz /= 8;
|
||||||
|
|
||||||
|
return sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
|
||||||
|
enum ieee80211_op_mode opmode)
|
||||||
|
{
|
||||||
|
struct ieee80211_he_capabilities *cap;
|
||||||
|
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
|
||||||
|
u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
|
||||||
|
u8 *pos = eid;
|
||||||
|
u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
|
||||||
|
|
||||||
|
if (!mode)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
ie_size = sizeof(struct ieee80211_he_capabilities);
|
||||||
|
ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
|
||||||
|
mode->he_capab[opmode].phy_cap);
|
||||||
|
|
||||||
|
switch (hapd->iface->conf->he_oper_chwidth) {
|
||||||
|
case CHANWIDTH_80P80MHZ:
|
||||||
|
he_oper_chwidth |=
|
||||||
|
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
|
||||||
|
mcs_nss_size += 4;
|
||||||
|
/* fall through */
|
||||||
|
case CHANWIDTH_160MHZ:
|
||||||
|
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||||
|
mcs_nss_size += 4;
|
||||||
|
/* fall through */
|
||||||
|
case CHANWIDTH_80MHZ:
|
||||||
|
case CHANWIDTH_USE_HT:
|
||||||
|
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
|
||||||
|
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
|
||||||
|
mcs_nss_size += 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ie_size += mcs_nss_size + ppet_size;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_EXTENSION;
|
||||||
|
*pos++ = 1 + ie_size;
|
||||||
|
*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
|
||||||
|
|
||||||
|
cap = (struct ieee80211_he_capabilities *) pos;
|
||||||
|
os_memset(cap, 0, sizeof(*cap));
|
||||||
|
|
||||||
|
os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
|
||||||
|
HE_MAX_MAC_CAPAB_SIZE);
|
||||||
|
os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
|
||||||
|
HE_MAX_PHY_CAPAB_SIZE);
|
||||||
|
os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
|
||||||
|
if (ppet_size)
|
||||||
|
os_memcpy(&cap->optional[mcs_nss_size],
|
||||||
|
mode->he_capab[opmode].ppet, ppet_size);
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
|
||||||
|
HE_PHYCAP_SU_BEAMFORMER_CAPAB;
|
||||||
|
else
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
|
||||||
|
~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
|
||||||
|
HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
|
||||||
|
else
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
|
||||||
|
~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
|
||||||
|
HE_PHYCAP_MU_BEAMFORMER_CAPAB;
|
||||||
|
else
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
|
||||||
|
~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
|
||||||
|
|
||||||
|
cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
|
||||||
|
he_oper_chwidth;
|
||||||
|
|
||||||
|
pos += ie_size;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct ieee80211_he_operation *oper;
|
||||||
|
u8 *pos = eid;
|
||||||
|
int oper_size = 6;
|
||||||
|
u32 params = 0;
|
||||||
|
|
||||||
|
if (!hapd->iface->current_mode)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_EXTENSION;
|
||||||
|
*pos++ = 1 + oper_size;
|
||||||
|
*pos++ = WLAN_EID_EXT_HE_OPERATION;
|
||||||
|
|
||||||
|
oper = (struct ieee80211_he_operation *) pos;
|
||||||
|
os_memset(oper, 0, sizeof(*oper));
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_op.he_default_pe_duration)
|
||||||
|
params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
|
||||||
|
HE_OPERATION_DFLT_PE_DURATION_OFFSET);
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_op.he_twt_required)
|
||||||
|
params |= HE_OPERATION_TWT_REQUIRED;
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_op.he_rts_threshold)
|
||||||
|
params |= (hapd->iface->conf->he_op.he_rts_threshold <<
|
||||||
|
HE_OPERATION_RTS_THRESHOLD_OFFSET);
|
||||||
|
|
||||||
|
if (hapd->iface->conf->he_op.he_bss_color)
|
||||||
|
params |= (hapd->iface->conf->he_op.he_bss_color <<
|
||||||
|
HE_OPERATION_BSS_COLOR_OFFSET);
|
||||||
|
|
||||||
|
/* HE minimum required basic MCS and NSS for STAs */
|
||||||
|
oper->he_mcs_nss_set =
|
||||||
|
host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
|
||||||
|
|
||||||
|
/* TODO: conditional MaxBSSID Indicator subfield */
|
||||||
|
|
||||||
|
oper->he_oper_params = host_to_le32(params);
|
||||||
|
|
||||||
|
pos += oper_size;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct ieee80211_he_mu_edca_parameter_set *edca;
|
||||||
|
u8 *pos;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
pos = (u8 *) &hapd->iface->conf->he_mu_edca;
|
||||||
|
for (i = 0; i < sizeof(*edca); i++) {
|
||||||
|
if (pos[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == sizeof(*edca))
|
||||||
|
return eid; /* no MU EDCA Parameters configured */
|
||||||
|
|
||||||
|
pos = eid;
|
||||||
|
*pos++ = WLAN_EID_EXTENSION;
|
||||||
|
*pos++ = 1 + sizeof(*edca);
|
||||||
|
*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
|
||||||
|
|
||||||
|
edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
|
||||||
|
os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
|
||||||
|
|
||||||
|
wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
|
||||||
|
pos, sizeof(*edca));
|
||||||
|
|
||||||
|
pos += sizeof(*edca);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct ieee80211_spatial_reuse *spr;
|
||||||
|
u8 *pos = eid, *spr_param;
|
||||||
|
u8 sz = 1;
|
||||||
|
|
||||||
|
if (!hapd->iface->conf->spr.sr_control)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
if (hapd->iface->conf->spr.sr_control &
|
||||||
|
SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
|
||||||
|
sz++;
|
||||||
|
|
||||||
|
if (hapd->iface->conf->spr.sr_control &
|
||||||
|
SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
|
||||||
|
sz += 18;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_EXTENSION;
|
||||||
|
*pos++ = 1 + sz;
|
||||||
|
*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
|
||||||
|
|
||||||
|
spr = (struct ieee80211_spatial_reuse *) pos;
|
||||||
|
os_memset(spr, 0, sizeof(*spr));
|
||||||
|
|
||||||
|
spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
|
||||||
|
pos++;
|
||||||
|
spr_param = spr->params;
|
||||||
|
if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
|
||||||
|
*spr_param++ =
|
||||||
|
hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
|
||||||
|
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
|
||||||
|
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
|
||||||
|
pos += 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_get_he_capab(struct hostapd_data *hapd,
|
||||||
|
const struct ieee80211_he_capabilities *he_cap,
|
||||||
|
struct ieee80211_he_capabilities *neg_he_cap,
|
||||||
|
size_t he_capab_len)
|
||||||
|
{
|
||||||
|
if (!he_cap)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (he_capab_len > sizeof(*neg_he_cap))
|
||||||
|
he_capab_len = sizeof(*neg_he_cap);
|
||||||
|
/* TODO: mask out unsupported features */
|
||||||
|
|
||||||
|
os_memcpy(neg_he_cap, he_cap, he_capab_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
|
||||||
|
enum ieee80211_op_mode opmode)
|
||||||
|
{
|
||||||
|
u16 sta_rx_mcs_set, ap_tx_mcs_set;
|
||||||
|
u8 mcs_count = 0;
|
||||||
|
const u16 *ap_mcs_set, *sta_mcs_set;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!hapd->iface->current_mode)
|
||||||
|
return 1;
|
||||||
|
ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
|
||||||
|
sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
|
||||||
|
sta_he_capab)->optional;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable HE capabilities for STAs for which there is not even a single
|
||||||
|
* allowed MCS in any supported number of streams, i.e., STA is
|
||||||
|
* advertising 3 (not supported) as HE MCS rates for all supported
|
||||||
|
* band/stream cases.
|
||||||
|
*/
|
||||||
|
switch (hapd->iface->conf->he_oper_chwidth) {
|
||||||
|
case CHANWIDTH_80P80MHZ:
|
||||||
|
mcs_count = 3;
|
||||||
|
break;
|
||||||
|
case CHANWIDTH_160MHZ:
|
||||||
|
mcs_count = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mcs_count = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < mcs_count; i++) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
/* AP Tx MCS map vs. STA Rx MCS map */
|
||||||
|
sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
|
||||||
|
ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
|
||||||
|
&ap_mcs_set[(i * 2) + 1]);
|
||||||
|
|
||||||
|
for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
|
||||||
|
if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"No matching HE MCS found between AP TX and STA RX");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
enum ieee80211_op_mode opmode, const u8 *he_capab,
|
||||||
|
size_t he_capab_len)
|
||||||
|
{
|
||||||
|
if (!he_capab || !hapd->iconf->ieee80211ax ||
|
||||||
|
!check_valid_he_mcs(hapd, he_capab, opmode) ||
|
||||||
|
he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
|
||||||
|
sta->flags &= ~WLAN_STA_HE;
|
||||||
|
os_free(sta->he_capab);
|
||||||
|
sta->he_capab = NULL;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sta->he_capab) {
|
||||||
|
sta->he_capab =
|
||||||
|
os_zalloc(sizeof(struct ieee80211_he_capabilities));
|
||||||
|
if (!sta->he_capab)
|
||||||
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->flags |= WLAN_STA_HE;
|
||||||
|
os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
|
||||||
|
os_memcpy(sta->he_capab, he_capab, he_capab_len);
|
||||||
|
sta->he_capab_len = he_capab_len;
|
||||||
|
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
557
hostapd-2.9/src/ap/ieee802_11_ht.c
Normal file
557
hostapd-2.9/src/ap/ieee802_11_ht.c
Normal file
|
@ -0,0 +1,557 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11n HT
|
||||||
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2007-2008, Intel Corporation
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "beacon.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "hw_features.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct ieee80211_ht_capabilities *cap;
|
||||||
|
u8 *pos = eid;
|
||||||
|
|
||||||
|
if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
|
||||||
|
hapd->conf->disable_11n)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_HT_CAP;
|
||||||
|
*pos++ = sizeof(*cap);
|
||||||
|
|
||||||
|
cap = (struct ieee80211_ht_capabilities *) pos;
|
||||||
|
os_memset(cap, 0, sizeof(*cap));
|
||||||
|
cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
|
||||||
|
cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
|
||||||
|
os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
|
||||||
|
16);
|
||||||
|
|
||||||
|
/* TODO: ht_extended_capabilities (now fully disabled) */
|
||||||
|
/* TODO: tx_bf_capability_info (now fully disabled) */
|
||||||
|
/* TODO: asel_capabilities (now fully disabled) */
|
||||||
|
|
||||||
|
pos += sizeof(*cap);
|
||||||
|
|
||||||
|
if (hapd->iconf->obss_interval) {
|
||||||
|
struct ieee80211_obss_scan_parameters *scan_params;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
|
||||||
|
*pos++ = sizeof(*scan_params);
|
||||||
|
|
||||||
|
scan_params = (struct ieee80211_obss_scan_parameters *) pos;
|
||||||
|
os_memset(scan_params, 0, sizeof(*scan_params));
|
||||||
|
scan_params->width_trigger_scan_interval =
|
||||||
|
host_to_le16(hapd->iconf->obss_interval);
|
||||||
|
|
||||||
|
/* Fill in default values for remaining parameters
|
||||||
|
* (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
|
||||||
|
scan_params->scan_passive_dwell =
|
||||||
|
host_to_le16(20);
|
||||||
|
scan_params->scan_active_dwell =
|
||||||
|
host_to_le16(10);
|
||||||
|
scan_params->scan_passive_total_per_channel =
|
||||||
|
host_to_le16(200);
|
||||||
|
scan_params->scan_active_total_per_channel =
|
||||||
|
host_to_le16(20);
|
||||||
|
scan_params->channel_transition_delay_factor =
|
||||||
|
host_to_le16(5);
|
||||||
|
scan_params->scan_activity_threshold =
|
||||||
|
host_to_le16(25);
|
||||||
|
|
||||||
|
pos += sizeof(*scan_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct ieee80211_ht_operation *oper;
|
||||||
|
u8 *pos = eid;
|
||||||
|
|
||||||
|
if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_HT_OPERATION;
|
||||||
|
*pos++ = sizeof(*oper);
|
||||||
|
|
||||||
|
oper = (struct ieee80211_ht_operation *) pos;
|
||||||
|
os_memset(oper, 0, sizeof(*oper));
|
||||||
|
|
||||||
|
oper->primary_chan = hapd->iconf->channel;
|
||||||
|
oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
|
||||||
|
if (hapd->iconf->secondary_channel == 1)
|
||||||
|
oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
|
||||||
|
HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
|
||||||
|
if (hapd->iconf->secondary_channel == -1)
|
||||||
|
oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
|
||||||
|
HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
|
||||||
|
|
||||||
|
pos += sizeof(*oper);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
u8 sec_ch;
|
||||||
|
|
||||||
|
if (!hapd->cs_freq_params.channel ||
|
||||||
|
!hapd->cs_freq_params.sec_channel_offset)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
if (hapd->cs_freq_params.sec_channel_offset == -1)
|
||||||
|
sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
|
||||||
|
else if (hapd->cs_freq_params.sec_channel_offset == 1)
|
||||||
|
sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
|
||||||
|
else
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
|
||||||
|
*eid++ = 1;
|
||||||
|
*eid++ = sec_ch;
|
||||||
|
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
op_mode
|
||||||
|
Set to 0 (HT pure) under the followign conditions
|
||||||
|
- all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
|
||||||
|
- all STAs in the BSS are 20 MHz HT in 20 MHz BSS
|
||||||
|
Set to 1 (HT non-member protection) if there may be non-HT STAs
|
||||||
|
in both the primary and the secondary channel
|
||||||
|
Set to 2 if only HT STAs are associated in BSS,
|
||||||
|
however and at least one 20 MHz HT STA is associated
|
||||||
|
Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
|
||||||
|
*/
|
||||||
|
int hostapd_ht_operation_update(struct hostapd_iface *iface)
|
||||||
|
{
|
||||||
|
u16 cur_op_mode, new_op_mode;
|
||||||
|
int op_mode_changes = 0;
|
||||||
|
|
||||||
|
if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
|
||||||
|
__func__, iface->ht_op_mode);
|
||||||
|
|
||||||
|
if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
|
||||||
|
&& iface->num_sta_ht_no_gf) {
|
||||||
|
iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
|
||||||
|
op_mode_changes++;
|
||||||
|
} else if ((iface->ht_op_mode &
|
||||||
|
HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
|
||||||
|
iface->num_sta_ht_no_gf == 0) {
|
||||||
|
iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
|
||||||
|
op_mode_changes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
|
||||||
|
(iface->num_sta_no_ht || iface->olbc_ht)) {
|
||||||
|
iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
|
||||||
|
op_mode_changes++;
|
||||||
|
} else if ((iface->ht_op_mode &
|
||||||
|
HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
|
||||||
|
(iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
|
||||||
|
iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
|
||||||
|
op_mode_changes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->num_sta_no_ht)
|
||||||
|
new_op_mode = HT_PROT_NON_HT_MIXED;
|
||||||
|
else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
|
||||||
|
new_op_mode = HT_PROT_20MHZ_PROTECTION;
|
||||||
|
else if (iface->olbc_ht)
|
||||||
|
new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
|
||||||
|
else
|
||||||
|
new_op_mode = HT_PROT_NO_PROTECTION;
|
||||||
|
|
||||||
|
cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
|
||||||
|
if (cur_op_mode != new_op_mode) {
|
||||||
|
iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
|
||||||
|
iface->ht_op_mode |= new_op_mode;
|
||||||
|
op_mode_changes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
|
||||||
|
__func__, iface->ht_op_mode, op_mode_changes);
|
||||||
|
|
||||||
|
return op_mode_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int is_40_allowed(struct hostapd_iface *iface, int channel)
|
||||||
|
{
|
||||||
|
int pri_freq, sec_freq;
|
||||||
|
int affected_start, affected_end;
|
||||||
|
int pri = 2407 + 5 * channel;
|
||||||
|
|
||||||
|
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
|
||||||
|
|
||||||
|
if (iface->conf->secondary_channel > 0)
|
||||||
|
sec_freq = pri_freq + 20;
|
||||||
|
else
|
||||||
|
sec_freq = pri_freq - 20;
|
||||||
|
|
||||||
|
affected_start = (pri_freq + sec_freq) / 2 - 25;
|
||||||
|
affected_end = (pri_freq + sec_freq) / 2 + 25;
|
||||||
|
if ((pri < affected_start || pri > affected_end))
|
||||||
|
return 1; /* not within affected channel range */
|
||||||
|
|
||||||
|
wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
|
||||||
|
affected_start, affected_end);
|
||||||
|
wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_2040_coex_action(struct hostapd_data *hapd,
|
||||||
|
const struct ieee80211_mgmt *mgmt, size_t len)
|
||||||
|
{
|
||||||
|
struct hostapd_iface *iface = hapd->iface;
|
||||||
|
struct ieee80211_2040_bss_coex_ie *bc_ie;
|
||||||
|
struct ieee80211_2040_intol_chan_report *ic_report;
|
||||||
|
int is_ht40_allowed = 1;
|
||||||
|
int i;
|
||||||
|
const u8 *start = (const u8 *) mgmt;
|
||||||
|
const u8 *data = start + IEEE80211_HDRLEN + 2;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"HT: Received 20/40 BSS Coexistence Management frame from "
|
||||||
|
MACSTR, MAC2STR(mgmt->sa));
|
||||||
|
|
||||||
|
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
|
||||||
|
mgmt->u.action.u.public_action.action);
|
||||||
|
|
||||||
|
if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Ignore too short 20/40 BSS Coexistence Management frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 20/40 BSS Coexistence element */
|
||||||
|
bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
|
||||||
|
if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
|
||||||
|
bc_ie->length < 1) {
|
||||||
|
wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
|
||||||
|
bc_ie->element_id, bc_ie->length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Truncated 20/40 BSS Coexistence element");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data += 2 + bc_ie->length;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
|
||||||
|
bc_ie->coex_param,
|
||||||
|
(bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
|
||||||
|
(bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
|
||||||
|
(bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
|
||||||
|
(bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
|
||||||
|
(bc_ie->coex_param & BIT(4)) ?
|
||||||
|
"[OBSSScanExemptionGrant]" : "",
|
||||||
|
(bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
|
||||||
|
"[Reserved]" : "");
|
||||||
|
|
||||||
|
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
|
||||||
|
/* Intra-BSS communication prohibiting 20/40 MHz BSS operation
|
||||||
|
*/
|
||||||
|
sta = ap_get_sta(hapd, mgmt->sa);
|
||||||
|
if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostapd_logger(hapd, mgmt->sa,
|
||||||
|
HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"20 MHz BSS width request bit is set in BSS coexistence information field");
|
||||||
|
is_ht40_allowed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
|
||||||
|
/* Inter-BSS communication prohibiting 20/40 MHz BSS operation
|
||||||
|
*/
|
||||||
|
hostapd_logger(hapd, mgmt->sa,
|
||||||
|
HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"40 MHz intolerant bit is set in BSS coexistence information field");
|
||||||
|
is_ht40_allowed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 20/40 BSS Intolerant Channel Report element (zero or more times) */
|
||||||
|
while (start + len - data >= 3 &&
|
||||||
|
data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
|
||||||
|
u8 ielen = data[1];
|
||||||
|
|
||||||
|
if (ielen > start + len - data - 2) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Truncated 20/40 BSS Intolerant Channel Report element");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ic_report = (struct ieee80211_2040_intol_chan_report *) data;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"20/40 BSS Intolerant Channel Report: Operating Class %u",
|
||||||
|
ic_report->op_class);
|
||||||
|
|
||||||
|
/* Go through the channel report to find any BSS there in the
|
||||||
|
* affected channel range */
|
||||||
|
for (i = 0; i < ielen - 1; i++) {
|
||||||
|
u8 chan = ic_report->variable[i];
|
||||||
|
|
||||||
|
if (chan == iface->conf->channel)
|
||||||
|
continue; /* matching own primary channel */
|
||||||
|
if (is_40_allowed(iface, chan))
|
||||||
|
continue; /* not within affected channels */
|
||||||
|
hostapd_logger(hapd, mgmt->sa,
|
||||||
|
HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
|
"20_40_INTOLERANT channel %d reported",
|
||||||
|
chan);
|
||||||
|
is_ht40_allowed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += 2 + ielen;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
|
||||||
|
is_ht40_allowed, iface->num_sta_ht40_intolerant);
|
||||||
|
|
||||||
|
if (!is_ht40_allowed &&
|
||||||
|
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||||
|
if (iface->conf->secondary_channel) {
|
||||||
|
hostapd_logger(hapd, mgmt->sa,
|
||||||
|
HOSTAPD_MODULE_IEEE80211,
|
||||||
|
HOSTAPD_LEVEL_INFO,
|
||||||
|
"Switching to 20 MHz operation");
|
||||||
|
iface->conf->secondary_channel = 0;
|
||||||
|
ieee802_11_set_beacons(iface);
|
||||||
|
}
|
||||||
|
if (!iface->num_sta_ht40_intolerant &&
|
||||||
|
iface->conf->obss_interval) {
|
||||||
|
unsigned int delay_time;
|
||||||
|
delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
|
||||||
|
iface->conf->obss_interval;
|
||||||
|
eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
|
||||||
|
NULL);
|
||||||
|
eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
|
||||||
|
hapd->iface, NULL);
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"Reschedule HT 20/40 timeout to occur in %u seconds",
|
||||||
|
delay_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *ht_capab)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Disable HT caps for STAs associated to no-HT BSSes, or for stations
|
||||||
|
* that did not specify a valid WMM IE in the (Re)Association Request
|
||||||
|
* frame.
|
||||||
|
*/
|
||||||
|
if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
|
||||||
|
!hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
|
||||||
|
sta->flags &= ~WLAN_STA_HT;
|
||||||
|
os_free(sta->ht_capabilities);
|
||||||
|
sta->ht_capabilities = NULL;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta->ht_capabilities == NULL) {
|
||||||
|
sta->ht_capabilities =
|
||||||
|
os_zalloc(sizeof(struct ieee80211_ht_capabilities));
|
||||||
|
if (sta->ht_capabilities == NULL)
|
||||||
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->flags |= WLAN_STA_HT;
|
||||||
|
os_memcpy(sta->ht_capabilities, ht_capab,
|
||||||
|
sizeof(struct ieee80211_ht_capabilities));
|
||||||
|
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
|
||||||
|
" in Association Request", MAC2STR(sta->addr));
|
||||||
|
|
||||||
|
if (sta->ht40_intolerant_set)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sta->ht40_intolerant_set = 1;
|
||||||
|
iface->num_sta_ht40_intolerant++;
|
||||||
|
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||||
|
|
||||||
|
if (iface->conf->secondary_channel &&
|
||||||
|
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||||
|
iface->conf->secondary_channel = 0;
|
||||||
|
ieee802_11_set_beacons(iface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
if (!sta->ht40_intolerant_set)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sta->ht40_intolerant_set = 0;
|
||||||
|
iface->num_sta_ht40_intolerant--;
|
||||||
|
|
||||||
|
if (iface->num_sta_ht40_intolerant == 0 &&
|
||||||
|
(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
|
||||||
|
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||||
|
unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
|
||||||
|
iface->conf->obss_interval;
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"HT: Start 20->40 MHz transition timer (%d seconds)",
|
||||||
|
delay_time);
|
||||||
|
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||||
|
eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
|
||||||
|
iface, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
u16 ht_capab;
|
||||||
|
|
||||||
|
ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
|
||||||
|
wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
|
||||||
|
"0x%04x", MAC2STR(sta->addr), ht_capab);
|
||||||
|
if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
|
||||||
|
if (!sta->no_ht_gf_set) {
|
||||||
|
sta->no_ht_gf_set = 1;
|
||||||
|
hapd->iface->num_sta_ht_no_gf++;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
|
||||||
|
"of non-gf stations %d",
|
||||||
|
__func__, MAC2STR(sta->addr),
|
||||||
|
hapd->iface->num_sta_ht_no_gf);
|
||||||
|
}
|
||||||
|
if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
|
||||||
|
if (!sta->ht_20mhz_set) {
|
||||||
|
sta->ht_20mhz_set = 1;
|
||||||
|
hapd->iface->num_sta_ht_20mhz++;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
|
||||||
|
"20MHz HT STAs %d",
|
||||||
|
__func__, MAC2STR(sta->addr),
|
||||||
|
hapd->iface->num_sta_ht_20mhz);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
|
||||||
|
ht40_intolerant_add(hapd->iface, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
if (!sta->no_ht_set) {
|
||||||
|
sta->no_ht_set = 1;
|
||||||
|
hapd->iface->num_sta_no_ht++;
|
||||||
|
}
|
||||||
|
if (hapd->iconf->ieee80211n) {
|
||||||
|
wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
|
||||||
|
"non-HT stations %d",
|
||||||
|
__func__, MAC2STR(sta->addr),
|
||||||
|
hapd->iface->num_sta_no_ht);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
|
{
|
||||||
|
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
|
||||||
|
update_sta_ht(hapd, sta);
|
||||||
|
else
|
||||||
|
update_sta_no_ht(hapd, sta);
|
||||||
|
|
||||||
|
if (hostapd_ht_operation_update(hapd->iface) > 0)
|
||||||
|
ieee802_11_set_beacons(hapd->iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_get_ht_capab(struct hostapd_data *hapd,
|
||||||
|
struct ieee80211_ht_capabilities *ht_cap,
|
||||||
|
struct ieee80211_ht_capabilities *neg_ht_cap)
|
||||||
|
{
|
||||||
|
u16 cap;
|
||||||
|
|
||||||
|
if (ht_cap == NULL)
|
||||||
|
return;
|
||||||
|
os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
|
||||||
|
cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mask out HT features we don't support, but don't overwrite
|
||||||
|
* non-symmetric features like STBC and SMPS. Just because
|
||||||
|
* we're not in dynamic SMPS mode the STA might still be.
|
||||||
|
*/
|
||||||
|
cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
|
||||||
|
HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STBC needs to be handled specially
|
||||||
|
* if we don't support RX STBC, mask out TX STBC in the STA's HT caps
|
||||||
|
* if we don't support TX STBC, mask out RX STBC in the STA's HT caps
|
||||||
|
*/
|
||||||
|
if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
|
||||||
|
cap &= ~HT_CAP_INFO_TX_STBC;
|
||||||
|
if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
|
||||||
|
cap &= ~HT_CAP_INFO_RX_STBC_MASK;
|
||||||
|
|
||||||
|
neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ap_ht2040_timeout(void *eloop_data, void *user_data)
|
||||||
|
{
|
||||||
|
struct hostapd_iface *iface = eloop_data;
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
|
||||||
|
|
||||||
|
iface->conf->secondary_channel = iface->secondary_ch;
|
||||||
|
ieee802_11_set_beacons(iface);
|
||||||
|
}
|
1002
hostapd-2.9/src/ap/ieee802_11_shared.c
Normal file
1002
hostapd-2.9/src/ap/ieee802_11_shared.c
Normal file
File diff suppressed because it is too large
Load diff
517
hostapd-2.9/src/ap/ieee802_11_vht.c
Normal file
517
hostapd-2.9/src/ap/ieee802_11_vht.c
Normal file
|
@ -0,0 +1,517 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.11ac VHT
|
||||||
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of BSD license
|
||||||
|
*
|
||||||
|
* See README and COPYING for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/includes.h"
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "common/ieee802_11_defs.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "ap_config.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "beacon.h"
|
||||||
|
#include "ieee802_11.h"
|
||||||
|
#include "dfs.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
|
||||||
|
{
|
||||||
|
struct ieee80211_vht_capabilities *cap;
|
||||||
|
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
|
||||||
|
u8 *pos = eid;
|
||||||
|
|
||||||
|
if (!mode)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
|
||||||
|
mode->vht_capab == 0 && hapd->iface->hw_features) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||||
|
if (hapd->iface->hw_features[i].mode ==
|
||||||
|
HOSTAPD_MODE_IEEE80211A) {
|
||||||
|
mode = &hapd->iface->hw_features[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_VHT_CAP;
|
||||||
|
*pos++ = sizeof(*cap);
|
||||||
|
|
||||||
|
cap = (struct ieee80211_vht_capabilities *) pos;
|
||||||
|
os_memset(cap, 0, sizeof(*cap));
|
||||||
|
cap->vht_capabilities_info = host_to_le32(
|
||||||
|
hapd->iface->conf->vht_capab);
|
||||||
|
|
||||||
|
if (nsts != 0) {
|
||||||
|
u32 hapd_nsts;
|
||||||
|
|
||||||
|
hapd_nsts = le_to_host32(cap->vht_capabilities_info);
|
||||||
|
hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
|
||||||
|
cap->vht_capabilities_info &=
|
||||||
|
~(host_to_le32(hapd_nsts <<
|
||||||
|
VHT_CAP_BEAMFORMEE_STS_OFFSET));
|
||||||
|
cap->vht_capabilities_info |=
|
||||||
|
host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Supported MCS set comes from hw */
|
||||||
|
os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
|
||||||
|
|
||||||
|
pos += sizeof(*cap);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct ieee80211_vht_operation *oper;
|
||||||
|
u8 *pos = eid;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_VHT_OPERATION;
|
||||||
|
*pos++ = sizeof(*oper);
|
||||||
|
|
||||||
|
oper = (struct ieee80211_vht_operation *) pos;
|
||||||
|
os_memset(oper, 0, sizeof(*oper));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* center freq = 5 GHz + (5 * index)
|
||||||
|
* So index 42 gives center freq 5.210 GHz
|
||||||
|
* which is channel 42 in 5G band
|
||||||
|
*/
|
||||||
|
oper->vht_op_info_chan_center_freq_seg0_idx =
|
||||||
|
hapd->iconf->vht_oper_centr_freq_seg0_idx;
|
||||||
|
oper->vht_op_info_chan_center_freq_seg1_idx =
|
||||||
|
hapd->iconf->vht_oper_centr_freq_seg1_idx;
|
||||||
|
|
||||||
|
oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
|
||||||
|
if (hapd->iconf->vht_oper_chwidth == 2) {
|
||||||
|
/*
|
||||||
|
* Convert 160 MHz channel width to new style as interop
|
||||||
|
* workaround.
|
||||||
|
*/
|
||||||
|
oper->vht_op_info_chwidth = 1;
|
||||||
|
oper->vht_op_info_chan_center_freq_seg1_idx =
|
||||||
|
oper->vht_op_info_chan_center_freq_seg0_idx;
|
||||||
|
if (hapd->iconf->channel <
|
||||||
|
hapd->iconf->vht_oper_centr_freq_seg0_idx)
|
||||||
|
oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
|
||||||
|
else
|
||||||
|
oper->vht_op_info_chan_center_freq_seg0_idx += 8;
|
||||||
|
} else if (hapd->iconf->vht_oper_chwidth == 3) {
|
||||||
|
/*
|
||||||
|
* Convert 80+80 MHz channel width to new style as interop
|
||||||
|
* workaround.
|
||||||
|
*/
|
||||||
|
oper->vht_op_info_chwidth = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VHT Basic MCS set comes from hw */
|
||||||
|
/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
|
||||||
|
oper->vht_basic_mcs_set = host_to_le16(0xfffc);
|
||||||
|
pos += sizeof(*oper);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
|
||||||
|
const u8 *sta_vht_capab)
|
||||||
|
{
|
||||||
|
const struct ieee80211_vht_capabilities *vht_cap;
|
||||||
|
struct ieee80211_vht_capabilities ap_vht_cap;
|
||||||
|
u16 sta_rx_mcs_set, ap_tx_mcs_set;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!mode)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable VHT caps for STAs for which there is not even a single
|
||||||
|
* allowed MCS in any supported number of streams, i.e., STA is
|
||||||
|
* advertising 3 (not supported) as VHT MCS rates for all supported
|
||||||
|
* stream cases.
|
||||||
|
*/
|
||||||
|
os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
|
||||||
|
sizeof(ap_vht_cap.vht_supported_mcs_set));
|
||||||
|
vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
|
||||||
|
|
||||||
|
/* AP Tx MCS map vs. STA Rx MCS map */
|
||||||
|
sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
|
||||||
|
ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
|
||||||
|
|
||||||
|
for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
|
||||||
|
if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"No matching VHT MCS found between AP TX and STA RX");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
u8 bw, chan1, chan2 = 0;
|
||||||
|
int freq1;
|
||||||
|
|
||||||
|
if (!hapd->cs_freq_params.channel ||
|
||||||
|
!hapd->cs_freq_params.vht_enabled)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
|
||||||
|
switch (hapd->cs_freq_params.bandwidth) {
|
||||||
|
case 40:
|
||||||
|
bw = 0;
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
/* check if it's 80+80 */
|
||||||
|
if (!hapd->cs_freq_params.center_freq2)
|
||||||
|
bw = 1;
|
||||||
|
else
|
||||||
|
bw = 3;
|
||||||
|
break;
|
||||||
|
case 160:
|
||||||
|
bw = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* not valid VHT bandwidth or not in CSA */
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
freq1 = hapd->cs_freq_params.center_freq1 ?
|
||||||
|
hapd->cs_freq_params.center_freq1 :
|
||||||
|
hapd->cs_freq_params.freq;
|
||||||
|
if (ieee80211_freq_to_chan(freq1, &chan1) !=
|
||||||
|
HOSTAPD_MODE_IEEE80211A)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
if (hapd->cs_freq_params.center_freq2 &&
|
||||||
|
ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
|
||||||
|
&chan2) != HOSTAPD_MODE_IEEE80211A)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
|
||||||
|
*eid++ = 5; /* Length of Channel Switch Wrapper */
|
||||||
|
*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
|
||||||
|
*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
|
||||||
|
*eid++ = bw; /* New Channel Width */
|
||||||
|
*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
|
||||||
|
*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
|
||||||
|
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
struct hostapd_iface *iface = hapd->iface;
|
||||||
|
struct hostapd_config *iconf = iface->conf;
|
||||||
|
struct hostapd_hw_modes *mode = iface->current_mode;
|
||||||
|
struct hostapd_channel_data *chan;
|
||||||
|
int dfs, i;
|
||||||
|
u8 channel, tx_pwr_count, local_pwr_constraint;
|
||||||
|
int max_tx_power;
|
||||||
|
u8 tx_pwr;
|
||||||
|
|
||||||
|
if (!mode)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
for (i = 0; i < mode->num_channels; i++) {
|
||||||
|
if (mode->channels[i].freq == iface->freq)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == mode->num_channels)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
switch (iface->conf->vht_oper_chwidth) {
|
||||||
|
case CHANWIDTH_USE_HT:
|
||||||
|
if (iconf->secondary_channel == 0) {
|
||||||
|
/* Max Transmit Power count = 0 (20 MHz) */
|
||||||
|
tx_pwr_count = 0;
|
||||||
|
} else {
|
||||||
|
/* Max Transmit Power count = 1 (20, 40 MHz) */
|
||||||
|
tx_pwr_count = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHANWIDTH_80MHZ:
|
||||||
|
/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
|
||||||
|
tx_pwr_count = 2;
|
||||||
|
break;
|
||||||
|
case CHANWIDTH_80P80MHZ:
|
||||||
|
case CHANWIDTH_160MHZ:
|
||||||
|
/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
|
||||||
|
tx_pwr_count = 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Below local_pwr_constraint logic is referred from
|
||||||
|
* hostapd_eid_pwr_constraint.
|
||||||
|
*
|
||||||
|
* Check if DFS is required by regulatory.
|
||||||
|
*/
|
||||||
|
dfs = hostapd_is_dfs_required(hapd->iface);
|
||||||
|
if (dfs < 0)
|
||||||
|
dfs = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In order to meet regulations when TPC is not implemented using
|
||||||
|
* a transmit power that is below the legal maximum (including any
|
||||||
|
* mitigation factor) should help. In this case, indicate 3 dB below
|
||||||
|
* maximum allowed transmit power.
|
||||||
|
*/
|
||||||
|
if (hapd->iconf->local_pwr_constraint == -1)
|
||||||
|
local_pwr_constraint = (dfs == 0) ? 0 : 3;
|
||||||
|
else
|
||||||
|
local_pwr_constraint = hapd->iconf->local_pwr_constraint;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A STA that is not an AP shall use a transmit power less than or
|
||||||
|
* equal to the local maximum transmit power level for the channel.
|
||||||
|
* The local maximum transmit power can be calculated from the formula:
|
||||||
|
* local max TX pwr = max TX pwr - local pwr constraint
|
||||||
|
* Where max TX pwr is maximum transmit power level specified for
|
||||||
|
* channel in Country element and local pwr constraint is specified
|
||||||
|
* for channel in this Power Constraint element.
|
||||||
|
*/
|
||||||
|
chan = &mode->channels[i];
|
||||||
|
max_tx_power = chan->max_tx_power - local_pwr_constraint;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local Maximum Transmit power is encoded as two's complement
|
||||||
|
* with a 0.5 dB step.
|
||||||
|
*/
|
||||||
|
max_tx_power *= 2; /* in 0.5 dB steps */
|
||||||
|
if (max_tx_power > 127) {
|
||||||
|
/* 63.5 has special meaning of 63.5 dBm or higher */
|
||||||
|
max_tx_power = 127;
|
||||||
|
}
|
||||||
|
if (max_tx_power < -128)
|
||||||
|
max_tx_power = -128;
|
||||||
|
if (max_tx_power < 0)
|
||||||
|
tx_pwr = 0x80 + max_tx_power + 128;
|
||||||
|
else
|
||||||
|
tx_pwr = max_tx_power;
|
||||||
|
|
||||||
|
*eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE;
|
||||||
|
*eid++ = 2 + tx_pwr_count;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Max Transmit Power count and
|
||||||
|
* Max Transmit Power units = 0 (EIRP)
|
||||||
|
*/
|
||||||
|
*eid++ = tx_pwr_count;
|
||||||
|
|
||||||
|
for (i = 0; i <= tx_pwr_count; i++)
|
||||||
|
*eid++ = tx_pwr;
|
||||||
|
|
||||||
|
return eid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *vht_capab)
|
||||||
|
{
|
||||||
|
/* Disable VHT caps for STAs associated to no-VHT BSSes. */
|
||||||
|
if (!vht_capab ||
|
||||||
|
!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
|
||||||
|
!check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
|
||||||
|
sta->flags &= ~WLAN_STA_VHT;
|
||||||
|
os_free(sta->vht_capabilities);
|
||||||
|
sta->vht_capabilities = NULL;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta->vht_capabilities == NULL) {
|
||||||
|
sta->vht_capabilities =
|
||||||
|
os_zalloc(sizeof(struct ieee80211_vht_capabilities));
|
||||||
|
if (sta->vht_capabilities == NULL)
|
||||||
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->flags |= WLAN_STA_VHT;
|
||||||
|
os_memcpy(sta->vht_capabilities, vht_capab,
|
||||||
|
sizeof(struct ieee80211_vht_capabilities));
|
||||||
|
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *vht_oper)
|
||||||
|
{
|
||||||
|
if (!vht_oper) {
|
||||||
|
os_free(sta->vht_operation);
|
||||||
|
sta->vht_operation = NULL;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sta->vht_operation) {
|
||||||
|
sta->vht_operation =
|
||||||
|
os_zalloc(sizeof(struct ieee80211_vht_operation));
|
||||||
|
if (!sta->vht_operation)
|
||||||
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memcpy(sta->vht_operation, vht_oper,
|
||||||
|
sizeof(struct ieee80211_vht_operation));
|
||||||
|
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *ie, size_t len)
|
||||||
|
{
|
||||||
|
const u8 *vht_capab;
|
||||||
|
unsigned int vht_capab_len;
|
||||||
|
|
||||||
|
if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
|
||||||
|
hapd->conf->disable_11ac)
|
||||||
|
goto no_capab;
|
||||||
|
|
||||||
|
/* The VHT Capabilities element embedded in vendor VHT */
|
||||||
|
vht_capab = ie + 5;
|
||||||
|
if (vht_capab[0] != WLAN_EID_VHT_CAP)
|
||||||
|
goto no_capab;
|
||||||
|
vht_capab_len = vht_capab[1];
|
||||||
|
if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
|
||||||
|
(int) vht_capab_len > ie + len - vht_capab - 2)
|
||||||
|
goto no_capab;
|
||||||
|
vht_capab += 2;
|
||||||
|
|
||||||
|
if (sta->vht_capabilities == NULL) {
|
||||||
|
sta->vht_capabilities =
|
||||||
|
os_zalloc(sizeof(struct ieee80211_vht_capabilities));
|
||||||
|
if (sta->vht_capabilities == NULL)
|
||||||
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
|
||||||
|
os_memcpy(sta->vht_capabilities, vht_capab,
|
||||||
|
sizeof(struct ieee80211_vht_capabilities));
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
no_capab:
|
||||||
|
sta->flags &= ~WLAN_STA_VENDOR_VHT;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
{
|
||||||
|
u8 *pos = eid;
|
||||||
|
|
||||||
|
if (!hapd->iface->current_mode)
|
||||||
|
return eid;
|
||||||
|
|
||||||
|
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||||
|
*pos++ = (5 + /* The Vendor OUI, type and subtype */
|
||||||
|
2 + sizeof(struct ieee80211_vht_capabilities) +
|
||||||
|
2 + sizeof(struct ieee80211_vht_operation));
|
||||||
|
|
||||||
|
WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
|
||||||
|
pos += 4;
|
||||||
|
*pos++ = VENDOR_VHT_SUBTYPE;
|
||||||
|
pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
|
||||||
|
pos = hostapd_eid_vht_operation(hapd, pos);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *vht_oper_notif)
|
||||||
|
{
|
||||||
|
if (!vht_oper_notif) {
|
||||||
|
sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
|
||||||
|
sta->vht_opmode = *vht_oper_notif;
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hostapd_get_vht_capab(struct hostapd_data *hapd,
|
||||||
|
struct ieee80211_vht_capabilities *vht_cap,
|
||||||
|
struct ieee80211_vht_capabilities *neg_vht_cap)
|
||||||
|
{
|
||||||
|
u32 cap, own_cap, sym_caps;
|
||||||
|
|
||||||
|
if (vht_cap == NULL)
|
||||||
|
return;
|
||||||
|
os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
|
||||||
|
|
||||||
|
cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
|
||||||
|
own_cap = hapd->iconf->vht_capab;
|
||||||
|
|
||||||
|
/* mask out symmetric VHT capabilities we don't support */
|
||||||
|
sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
|
||||||
|
cap &= ~sym_caps | (own_cap & sym_caps);
|
||||||
|
|
||||||
|
/* mask out beamformer/beamformee caps if not supported */
|
||||||
|
if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
|
||||||
|
cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||||
|
VHT_CAP_BEAMFORMEE_STS_MAX);
|
||||||
|
|
||||||
|
if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
|
||||||
|
cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
|
||||||
|
VHT_CAP_SOUNDING_DIMENSION_MAX);
|
||||||
|
|
||||||
|
if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
|
||||||
|
cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
||||||
|
|
||||||
|
if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
|
||||||
|
cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
||||||
|
|
||||||
|
/* mask channel widths we don't support */
|
||||||
|
switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
|
||||||
|
case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
|
||||||
|
break;
|
||||||
|
case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
|
||||||
|
if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
|
||||||
|
cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
|
||||||
|
cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
|
||||||
|
cap &= ~VHT_CAP_SHORT_GI_160;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we don't support RX STBC, mask out TX STBC in the STA's HT caps
|
||||||
|
* if we don't support TX STBC, mask out RX STBC in the STA's HT caps
|
||||||
|
*/
|
||||||
|
if (!(own_cap & VHT_CAP_RXSTBC_MASK))
|
||||||
|
cap &= ~VHT_CAP_TXSTBC;
|
||||||
|
if (!(own_cap & VHT_CAP_TXSTBC))
|
||||||
|
cap &= ~VHT_CAP_RXSTBC_MASK;
|
||||||
|
|
||||||
|
neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
|
||||||
|
}
|
2997
hostapd-2.9/src/ap/ieee802_1x.c
Normal file
2997
hostapd-2.9/src/ap/ieee802_1x.c
Normal file
File diff suppressed because it is too large
Load diff
70
hostapd-2.9/src/ap/ieee802_1x.h
Normal file
70
hostapd-2.9/src/ap/ieee802_1x.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* hostapd / IEEE 802.1X-2004 Authenticator
|
||||||
|
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IEEE802_1X_H
|
||||||
|
#define IEEE802_1X_H
|
||||||
|
|
||||||
|
struct hostapd_data;
|
||||||
|
struct sta_info;
|
||||||
|
struct eapol_state_machine;
|
||||||
|
struct hostapd_config;
|
||||||
|
struct hostapd_bss_config;
|
||||||
|
struct hostapd_radius_attr;
|
||||||
|
struct radius_msg;
|
||||||
|
|
||||||
|
|
||||||
|
void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
|
||||||
|
size_t len);
|
||||||
|
void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
|
||||||
|
void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int authorized);
|
||||||
|
void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
|
||||||
|
int ieee802_1x_init(struct hostapd_data *hapd);
|
||||||
|
void ieee802_1x_erp_flush(struct hostapd_data *hapd);
|
||||||
|
void ieee802_1x_deinit(struct hostapd_data *hapd);
|
||||||
|
int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *buf, size_t len, int ack);
|
||||||
|
int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
const u8 *data, int len, int ack);
|
||||||
|
u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
|
||||||
|
u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
|
||||||
|
int idx);
|
||||||
|
struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
|
||||||
|
const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
|
||||||
|
const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
|
||||||
|
size_t *len);
|
||||||
|
void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
|
||||||
|
int enabled);
|
||||||
|
void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
|
||||||
|
int valid);
|
||||||
|
void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
|
||||||
|
int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
|
||||||
|
int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
void hostapd_get_ntp_timestamp(u8 *buf);
|
||||||
|
char *eap_type_text(u8 type);
|
||||||
|
|
||||||
|
const char *radius_mode_txt(struct hostapd_data *hapd);
|
||||||
|
int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
|
||||||
|
int add_common_radius_attr(struct hostapd_data *hapd,
|
||||||
|
struct hostapd_radius_attr *req_attr,
|
||||||
|
struct sta_info *sta,
|
||||||
|
struct radius_msg *msg);
|
||||||
|
int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
struct radius_msg *msg, int acct);
|
||||||
|
void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
const u8 *eap, size_t len);
|
||||||
|
struct eapol_state_machine *
|
||||||
|
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
|
|
||||||
|
#endif /* IEEE802_1X_H */
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue