Initializing repository
This commit is contained in:
174
CONTRIBUTIONS
Normal file
174
CONTRIBUTIONS
Normal file
@@ -0,0 +1,174 @@
|
||||
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 main 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.
|
||||
|
||||
A list of pending patches waiting for review is available in
|
||||
Patchwork: https://patchwork.ozlabs.org/project/hostap/list/
|
||||
|
||||
|
||||
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-2022, 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
COPYING
Normal file
22
COPYING
Normal file
@@ -0,0 +1,22 @@
|
||||
wpa_supplicant and hostapd
|
||||
--------------------------
|
||||
|
||||
Copyright (c) 2002-2022, 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
README
Normal file
56
README
Normal file
@@ -0,0 +1,56 @@
|
||||
wpa_supplicant and hostapd
|
||||
--------------------------
|
||||
|
||||
Copyright (c) 2002-2024, 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.
|
||||
4
hs20/client/.gitignore
vendored
Normal file
4
hs20/client/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
hs20-osu-client
|
||||
SP
|
||||
osu-ca.pem
|
||||
spp.xsd
|
||||
91
hs20/client/Android.mk
Normal file
91
hs20/client/Android.mk
Normal file
@@ -0,0 +1,91 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
INCLUDES = $(LOCAL_PATH)
|
||||
INCLUDES += $(LOCAL_PATH)/../../src/utils
|
||||
INCLUDES += $(LOCAL_PATH)/../../src/common
|
||||
INCLUDES += $(LOCAL_PATH)/../../src
|
||||
INCLUDES += external/libxml2/include
|
||||
INCLUDES += external/curl/include
|
||||
INCLUDES += external/webkit/Source/WebKit/gtk
|
||||
|
||||
# We try to keep this compiling against older platform versions.
|
||||
# The new icu location (external/icu) exports its own headers, but
|
||||
# the older versions in external/icu4c don't, and we need to add those
|
||||
# headers to the include path by hand.
|
||||
ifeq ($(wildcard external/icu),)
|
||||
INCLUDES += external/icu4c/common
|
||||
else
|
||||
# The LOCAL_EXPORT_C_INCLUDE_DIRS from ICU did not seem to fully resolve the
|
||||
# build (e.g., "mm -B" failed to build, but following that with "mm" allowed
|
||||
# the build to complete). For now, add the include directory manually here for
|
||||
# Android 5.0.
|
||||
ver = $(filter 5.0%,$(PLATFORM_VERSION))
|
||||
ifneq (,$(strip $(ver)))
|
||||
INCLUDES += external/icu/icu4c/source/common
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
L_CFLAGS += -DCONFIG_CTRL_IFACE
|
||||
L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
|
||||
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
|
||||
|
||||
OBJS = spp_client.c
|
||||
OBJS += oma_dm_client.c
|
||||
OBJS += osu_client.c
|
||||
OBJS += est.c
|
||||
OBJS += ../../src/common/wpa_ctrl.c
|
||||
OBJS += ../../src/common/wpa_helpers.c
|
||||
OBJS += ../../src/utils/xml-utils.c
|
||||
#OBJS += ../../src/utils/browser-android.c
|
||||
OBJS += ../../src/utils/browser-wpadebug.c
|
||||
OBJS += ../../src/utils/wpabuf.c
|
||||
OBJS += ../../src/utils/eloop.c
|
||||
OBJS += ../../src/wps/httpread.c
|
||||
OBJS += ../../src/wps/http_server.c
|
||||
OBJS += ../../src/utils/xml_libxml2.c
|
||||
OBJS += ../../src/utils/http_curl.c
|
||||
OBJS += ../../src/utils/base64.c
|
||||
OBJS += ../../src/utils/os_unix.c
|
||||
L_CFLAGS += -DCONFIG_DEBUG_FILE
|
||||
OBJS += ../../src/utils/wpa_debug.c
|
||||
OBJS += ../../src/utils/common.c
|
||||
OBJS += ../../src/crypto/crypto_internal.c
|
||||
OBJS += ../../src/crypto/md5-internal.c
|
||||
OBJS += ../../src/crypto/sha1-internal.c
|
||||
OBJS += ../../src/crypto/sha256-internal.c
|
||||
OBJS += ../../src/crypto/tls_openssl_ocsp.c
|
||||
|
||||
L_CFLAGS += -DEAP_TLS_OPENSSL
|
||||
|
||||
L_CFLAGS += -Wno-unused-parameter
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_VERSION_LAST_STABLE) -ge 8 ; echo $$?), 0)
|
||||
L_CFLAGS += -DCONFIG_ANDROID_LOG
|
||||
L_CFLAGS += -DANDROID_LOG_NAME='"hs20-osu-client"'
|
||||
endif
|
||||
|
||||
########################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := hs20-osu-client
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libc libcutils
|
||||
LOCAL_SHARED_LIBRARIES += libcrypto libssl
|
||||
ifeq ($(shell test $(PLATFORM_VERSION_LAST_STABLE) -ge 8 ; echo $$?), 0)
|
||||
LOCAL_VENDOR_MODULE := true
|
||||
LOCAL_SHARED_LIBRARIES += libxml2
|
||||
LOCAL_SHARED_LIBRARIES += liblog
|
||||
else
|
||||
#LOCAL_SHARED_LIBRARIES += libxml2
|
||||
LOCAL_STATIC_LIBRARIES += libxml2
|
||||
LOCAL_SHARED_LIBRARIES += libicuuc
|
||||
endif # End of check for platform version
|
||||
LOCAL_SHARED_LIBRARIES += libcurl
|
||||
|
||||
LOCAL_CFLAGS := $(L_CFLAGS)
|
||||
LOCAL_SRC_FILES := $(OBJS)
|
||||
LOCAL_C_INCLUDES := $(INCLUDES)
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
########################
|
||||
81
hs20/client/Makefile
Normal file
81
hs20/client/Makefile
Normal file
@@ -0,0 +1,81 @@
|
||||
ALL=hs20-osu-client
|
||||
|
||||
include ../../src/build.rules
|
||||
|
||||
CFLAGS += -I../../src/utils
|
||||
CFLAGS += -I../../src/common
|
||||
CFLAGS += -I../../src
|
||||
|
||||
ifndef CONFIG_NO_BROWSER
|
||||
ifndef CONFIG_BROWSER_SYSTEM
|
||||
TEST_WK := $(shell pkg-config --silence-errors --cflags webkitgtk-3.0)
|
||||
ifeq ($(TEST_WK),)
|
||||
# Try webkit2
|
||||
GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkit2gtk-4.0)
|
||||
GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkit2gtk-4.0)
|
||||
CFLAGS += -DUSE_WEBKIT2
|
||||
else
|
||||
GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
|
||||
GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
|
||||
endif
|
||||
|
||||
CFLAGS += $(GTKCFLAGS)
|
||||
LIBS += $(GTKLIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJS=spp_client.o
|
||||
OBJS += oma_dm_client.o
|
||||
OBJS += osu_client.o
|
||||
OBJS += est.o
|
||||
OBJS += ../../src/utils/xml-utils.o
|
||||
CFLAGS += -DCONFIG_CTRL_IFACE
|
||||
CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
|
||||
OBJS += ../../src/common/wpa_ctrl.o ../../src/common/wpa_helpers.o
|
||||
ifdef CONFIG_NO_BROWSER
|
||||
CFLAGS += -DCONFIG_NO_BROWSER
|
||||
else
|
||||
ifdef CONFIG_BROWSER_SYSTEM
|
||||
OBJS += ../../src/utils/eloop.o
|
||||
OBJS += ../../src/utils/wpabuf.o
|
||||
OBJS += ../../src/wps/httpread.o
|
||||
OBJS += ../../src/wps/http_server.o
|
||||
OBJS += ../../src/utils/browser-system.o
|
||||
else
|
||||
OBJS += ../../src/utils/browser.o
|
||||
endif
|
||||
endif
|
||||
OBJS += ../../src/utils/xml_libxml2.o
|
||||
OBJS += ../../src/utils/http_curl.o
|
||||
OBJS += ../../src/utils/base64.o
|
||||
OBJS += ../../src/utils/os_unix.o
|
||||
CFLAGS += -DCONFIG_DEBUG_FILE
|
||||
OBJS += ../../src/utils/wpa_debug.o
|
||||
OBJS += ../../src/utils/common.o
|
||||
OBJS += ../../src/crypto/crypto_internal.o
|
||||
OBJS += ../../src/crypto/md5-internal.o
|
||||
OBJS += ../../src/crypto/sha1-internal.o
|
||||
OBJS += ../../src/crypto/sha256-internal.o
|
||||
|
||||
CFLAGS += $(shell xml2-config --cflags)
|
||||
LIBS += $(shell xml2-config --libs)
|
||||
|
||||
# Allow static/custom linking of libcurl.
|
||||
ifdef CUST_CURL_LINKAGE
|
||||
LIBS += ${CUST_CURL_LINKAGE}
|
||||
else
|
||||
LIBS += -lcurl
|
||||
endif
|
||||
|
||||
CFLAGS += -DEAP_TLS_OPENSSL
|
||||
OBJS += ../../src/crypto/tls_openssl_ocsp.o
|
||||
LIBS += -lssl -lcrypto
|
||||
|
||||
_OBJS_VAR := OBJS
|
||||
include ../../src/objs.mk
|
||||
hs20-osu-client: $(OBJS)
|
||||
$(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
|
||||
@$(E) " LD " $@
|
||||
|
||||
clean: common-clean
|
||||
rm -f core *~
|
||||
47
hs20/client/devdetail.xml
Normal file
47
hs20/client/devdetail.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
|
||||
<Ext>
|
||||
<org.wi-fi>
|
||||
<Wi-Fi>
|
||||
<EAPMethodList>
|
||||
<EAPMethod1>
|
||||
<EAPType>13</EAPType>
|
||||
</EAPMethod1>
|
||||
<EAPMethod2>
|
||||
<EAPType>21</EAPType>
|
||||
<InnerMethod>MS-CHAP-V2</InnerMethod>
|
||||
</EAPMethod2>
|
||||
<EAPMethod3>
|
||||
<EAPType>18</EAPType>
|
||||
</EAPMethod3>
|
||||
<EAPMethod4>
|
||||
<EAPType>23</EAPType>
|
||||
</EAPMethod4>
|
||||
<EAPMethod5>
|
||||
<EAPType>50</EAPType>
|
||||
</EAPMethod5>
|
||||
</EAPMethodList>
|
||||
<ManufacturingCertificate>false</ManufacturingCertificate>
|
||||
<Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
|
||||
<IMSI>310026000000000</IMSI>
|
||||
<IMEI_MEID>imei:490123456789012</IMEI_MEID>
|
||||
<ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
|
||||
<Ops>
|
||||
<launchBrowserToURI></launchBrowserToURI>
|
||||
<negotiateClientCertTLS></negotiateClientCertTLS>
|
||||
<getCertificate></getCertificate>
|
||||
</Ops>
|
||||
</Wi-Fi>
|
||||
</org.wi-fi>
|
||||
</Ext>
|
||||
<URI>
|
||||
<MaxDepth>0</MaxDepth>
|
||||
<MaxTotLen>0</MaxTotLen>
|
||||
<MaxSegLen>0</MaxSegLen>
|
||||
</URI>
|
||||
<DevType>MobilePhone</DevType>
|
||||
<OEM>Manufacturer</OEM>
|
||||
<FwV>1.0</FwV>
|
||||
<SwV>1.0</SwV>
|
||||
<HwV>1.0</HwV>
|
||||
<LrgObj>false</LrgObj>
|
||||
</DevDetail>
|
||||
7
hs20/client/devinfo.xml
Normal file
7
hs20/client/devinfo.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
|
||||
<DevId>urn:Example:HS20-station:123456</DevId>
|
||||
<Man>Manufacturer</Man>
|
||||
<Mod>HS20-station</Mod>
|
||||
<DmV>1.2</DmV>
|
||||
<Lang>en</Lang>
|
||||
</DevInfo>
|
||||
742
hs20/client/est.c
Normal file
742
hs20/client/est.c
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* Hotspot 2.0 OSU client - EST client
|
||||
* Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/pkcs7.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/asn1t.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/buffer.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "utils/base64.h"
|
||||
#include "utils/xml-utils.h"
|
||||
#include "utils/http-utils.h"
|
||||
#include "osu_client.h"
|
||||
|
||||
|
||||
static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
|
||||
size_t len, char *pem_file, char *der_file)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
CBS pkcs7_cbs;
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
PKCS7 *p7 = NULL;
|
||||
const unsigned char *p = pkcs7;
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
STACK_OF(X509) *certs;
|
||||
int i, num, ret = -1;
|
||||
BIO *out = NULL;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
certs = sk_X509_new_null();
|
||||
if (!certs)
|
||||
goto fail;
|
||||
CBS_init(&pkcs7_cbs, pkcs7, len);
|
||||
if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
|
||||
wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
write_result(ctx, "Could not parse PKCS#7 object from EST");
|
||||
goto fail;
|
||||
}
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
p7 = d2i_PKCS7(NULL, &p, len);
|
||||
if (p7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
write_result(ctx, "Could not parse PKCS#7 object from EST");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (OBJ_obj2nid(p7->type)) {
|
||||
case NID_pkcs7_signed:
|
||||
certs = p7->d.sign->cert;
|
||||
break;
|
||||
case NID_pkcs7_signedAndEnveloped:
|
||||
certs = p7->d.signed_and_enveloped->cert;
|
||||
break;
|
||||
default:
|
||||
certs = NULL;
|
||||
break;
|
||||
}
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
|
||||
if (!certs || ((num = sk_X509_num(certs)) == 0)) {
|
||||
wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
|
||||
write_result(ctx, "No certificates found in PKCS#7 object");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (der_file) {
|
||||
FILE *f = fopen(der_file, "wb");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
i2d_X509_fp(f, sk_X509_value(certs, 0));
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (pem_file) {
|
||||
out = BIO_new(BIO_s_file());
|
||||
if (out == NULL ||
|
||||
BIO_write_filename(out, pem_file) <= 0)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
X509 *cert = sk_X509_value(certs, i);
|
||||
X509_print(out, cert);
|
||||
PEM_write_bio_X509(out, cert);
|
||||
BIO_puts(out, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
if (certs)
|
||||
sk_X509_pop_free(certs, X509_free);
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
PKCS7_free(p7);
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
if (out)
|
||||
BIO_free_all(out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
|
||||
{
|
||||
char *buf, *resp;
|
||||
size_t buflen;
|
||||
unsigned char *pkcs7;
|
||||
size_t pkcs7_len, resp_len;
|
||||
int res;
|
||||
|
||||
buflen = os_strlen(url) + 100;
|
||||
buf = os_malloc(buflen);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
os_snprintf(buf, buflen, "%s/cacerts", url);
|
||||
wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
|
||||
write_summary(ctx, "Download EST cacerts from %s", buf);
|
||||
ctx->no_osu_cert_validation = 1;
|
||||
http_ocsp_set(ctx->http, 1);
|
||||
res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
|
||||
ctx->ca_fname);
|
||||
http_ocsp_set(ctx->http,
|
||||
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
|
||||
ctx->no_osu_cert_validation = 0;
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
|
||||
buf);
|
||||
write_result(ctx, "Failed to download EST cacerts from %s",
|
||||
buf);
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
os_free(buf);
|
||||
|
||||
resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
|
||||
write_result(ctx, "Could not read EST cacerts");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
|
||||
if (pkcs7 && pkcs7_len < resp_len / 2) {
|
||||
wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
|
||||
(unsigned int) pkcs7_len, (unsigned int) resp_len);
|
||||
os_free(pkcs7);
|
||||
pkcs7 = NULL;
|
||||
}
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
|
||||
pkcs7 = os_malloc(resp_len);
|
||||
if (pkcs7) {
|
||||
os_memcpy(pkcs7, resp, resp_len);
|
||||
pkcs7_len = resp_len;
|
||||
}
|
||||
}
|
||||
os_free(resp);
|
||||
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
|
||||
write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
|
||||
NULL);
|
||||
os_free(pkcs7);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
|
||||
write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
|
||||
return -1;
|
||||
}
|
||||
unlink("Cert/est-cacerts.txt");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
|
||||
*
|
||||
* AttrOrOID ::= CHOICE {
|
||||
* oid OBJECT IDENTIFIER,
|
||||
* attribute Attribute }
|
||||
*
|
||||
* Attribute ::= SEQUENCE {
|
||||
* type OBJECT IDENTIFIER,
|
||||
* values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
ASN1_OBJECT *type;
|
||||
STACK_OF(ASN1_OBJECT) *values;
|
||||
} Attribute;
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
union {
|
||||
ASN1_OBJECT *oid;
|
||||
Attribute *attribute;
|
||||
} d;
|
||||
} AttrOrOID;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
DEFINE_STACK_OF(AttrOrOID)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
STACK_OF(AttrOrOID) *attrs;
|
||||
} CsrAttrs;
|
||||
|
||||
ASN1_SEQUENCE(Attribute) = {
|
||||
ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
|
||||
ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
|
||||
} ASN1_SEQUENCE_END(Attribute);
|
||||
|
||||
ASN1_CHOICE(AttrOrOID) = {
|
||||
ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
|
||||
ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
|
||||
} ASN1_CHOICE_END(AttrOrOID);
|
||||
|
||||
ASN1_CHOICE(CsrAttrs) = {
|
||||
ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
|
||||
} ASN1_CHOICE_END(CsrAttrs);
|
||||
|
||||
IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
|
||||
|
||||
|
||||
static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
char txt[100];
|
||||
int res;
|
||||
|
||||
if (!oid)
|
||||
return;
|
||||
|
||||
res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt))
|
||||
return;
|
||||
|
||||
if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
|
||||
} else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
|
||||
wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
|
||||
STACK_OF(ASN1_OBJECT) *values,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
char txt[100];
|
||||
int i, num, res;
|
||||
|
||||
num = sk_ASN1_OBJECT_num(values);
|
||||
for (i = 0; i < num; i++) {
|
||||
ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
|
||||
|
||||
res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt))
|
||||
continue;
|
||||
|
||||
if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq macAddress");
|
||||
} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq imei");
|
||||
} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq meid");
|
||||
} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq DevId");
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
|
||||
txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
char txt[100], txt2[100];
|
||||
int i, num, res;
|
||||
|
||||
if (!attr || !attr->type || !attr->values)
|
||||
return;
|
||||
|
||||
res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt))
|
||||
return;
|
||||
|
||||
if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
|
||||
add_csrattrs_ext_req(ctx, attr->values, exts);
|
||||
return;
|
||||
}
|
||||
|
||||
num = sk_ASN1_OBJECT_num(attr->values);
|
||||
for (i = 0; i < num; i++) {
|
||||
ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
|
||||
|
||||
res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt2))
|
||||
continue;
|
||||
|
||||
wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
|
||||
txt, txt2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
int i, num;
|
||||
|
||||
if (!csrattrs || ! csrattrs->attrs)
|
||||
return;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
num = sk_AttrOrOID_num(csrattrs->attrs);
|
||||
#else
|
||||
num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
|
||||
#endif
|
||||
for (i = 0; i < num; i++) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i);
|
||||
#else
|
||||
AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
|
||||
#endif
|
||||
switch (ao->type) {
|
||||
case 0:
|
||||
add_csrattrs_oid(ctx, ao->d.oid, exts);
|
||||
break;
|
||||
case 1:
|
||||
add_csrattrs_attr(ctx, ao->d.attribute, exts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
|
||||
char *csr_pem, char *est_req, char *old_cert,
|
||||
CsrAttrs *csrattrs)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
X509_REQ *req = NULL;
|
||||
int ret = -1;
|
||||
unsigned int val;
|
||||
X509_NAME *subj = NULL;
|
||||
char name[100];
|
||||
STACK_OF(X509_EXTENSION) *exts = NULL;
|
||||
X509_EXTENSION *ex;
|
||||
BIO *out;
|
||||
CONF *ctmp = NULL;
|
||||
|
||||
wpa_printf(MSG_INFO, "Generate RSA private key");
|
||||
write_summary(ctx, "Generate RSA private key");
|
||||
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
if (!pctx)
|
||||
return -1;
|
||||
|
||||
if (EVP_PKEY_keygen_init(pctx) <= 0)
|
||||
goto fail;
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
|
||||
goto fail;
|
||||
|
||||
if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
|
||||
goto fail;
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
pctx = NULL;
|
||||
|
||||
if (key_pem) {
|
||||
FILE *f = fopen(key_pem, "wb");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
|
||||
wpa_printf(MSG_INFO, "Could not write private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
fclose(f);
|
||||
goto fail;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
wpa_printf(MSG_INFO, "Generate CSR");
|
||||
write_summary(ctx, "Generate CSR");
|
||||
req = X509_REQ_new();
|
||||
if (req == NULL)
|
||||
goto fail;
|
||||
|
||||
if (old_cert) {
|
||||
FILE *f;
|
||||
X509 *cert;
|
||||
int res;
|
||||
|
||||
f = fopen(old_cert, "r");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
cert = PEM_read_X509(f, NULL, NULL, NULL);
|
||||
fclose(f);
|
||||
|
||||
if (cert == NULL)
|
||||
goto fail;
|
||||
res = X509_REQ_set_subject_name(req,
|
||||
X509_get_subject_name(cert));
|
||||
X509_free(cert);
|
||||
if (!res)
|
||||
goto fail;
|
||||
} else {
|
||||
os_get_random((u8 *) &val, sizeof(val));
|
||||
os_snprintf(name, sizeof(name), "cert-user-%u", val);
|
||||
subj = X509_NAME_new();
|
||||
if (subj == NULL ||
|
||||
!X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
|
||||
(unsigned char *) name,
|
||||
-1, -1, 0) ||
|
||||
!X509_REQ_set_subject_name(req, subj))
|
||||
goto fail;
|
||||
X509_NAME_free(subj);
|
||||
subj = NULL;
|
||||
}
|
||||
|
||||
if (!X509_REQ_set_pubkey(req, pkey))
|
||||
goto fail;
|
||||
|
||||
exts = sk_X509_EXTENSION_new_null();
|
||||
if (!exts)
|
||||
goto fail;
|
||||
|
||||
ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
|
||||
"CA:FALSE");
|
||||
if (ex == NULL ||
|
||||
!sk_X509_EXTENSION_push(exts, ex))
|
||||
goto fail;
|
||||
|
||||
ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
|
||||
"nonRepudiation,digitalSignature,keyEncipherment");
|
||||
if (ex == NULL ||
|
||||
!sk_X509_EXTENSION_push(exts, ex))
|
||||
goto fail;
|
||||
|
||||
ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
|
||||
"1.3.6.1.4.1.40808.1.1.2");
|
||||
if (ex == NULL ||
|
||||
!sk_X509_EXTENSION_push(exts, ex))
|
||||
goto fail;
|
||||
|
||||
add_csrattrs(ctx, csrattrs, exts);
|
||||
|
||||
if (!X509_REQ_add_extensions(req, exts))
|
||||
goto fail;
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
exts = NULL;
|
||||
|
||||
if (!X509_REQ_sign(req, pkey, EVP_sha256()))
|
||||
goto fail;
|
||||
|
||||
out = BIO_new(BIO_s_mem());
|
||||
if (out) {
|
||||
char *txt;
|
||||
size_t rlen;
|
||||
|
||||
#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
|
||||
X509_REQ_print(out, req);
|
||||
#endif
|
||||
rlen = BIO_ctrl_pending(out);
|
||||
txt = os_malloc(rlen + 1);
|
||||
if (txt) {
|
||||
int res = BIO_read(out, txt, rlen);
|
||||
if (res > 0) {
|
||||
txt[res] = '\0';
|
||||
wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
|
||||
txt);
|
||||
}
|
||||
os_free(txt);
|
||||
}
|
||||
BIO_free(out);
|
||||
}
|
||||
|
||||
if (csr_pem) {
|
||||
FILE *f = fopen(csr_pem, "w");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
|
||||
X509_REQ_print_fp(f, req);
|
||||
#endif
|
||||
if (!PEM_write_X509_REQ(f, req)) {
|
||||
fclose(f);
|
||||
goto fail;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (est_req) {
|
||||
BIO *mem = BIO_new(BIO_s_mem());
|
||||
BUF_MEM *ptr;
|
||||
char *pos, *end, *buf_end;
|
||||
FILE *f;
|
||||
|
||||
if (mem == NULL)
|
||||
goto fail;
|
||||
if (!PEM_write_bio_X509_REQ(mem, req)) {
|
||||
BIO_free(mem);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BIO_get_mem_ptr(mem, &ptr);
|
||||
pos = ptr->data;
|
||||
buf_end = pos + ptr->length;
|
||||
|
||||
/* Remove START/END lines */
|
||||
while (pos < buf_end && *pos != '\n')
|
||||
pos++;
|
||||
if (pos == buf_end) {
|
||||
BIO_free(mem);
|
||||
goto fail;
|
||||
}
|
||||
pos++;
|
||||
|
||||
end = pos;
|
||||
while (end < buf_end && *end != '-')
|
||||
end++;
|
||||
|
||||
f = fopen(est_req, "w");
|
||||
if (f == NULL) {
|
||||
BIO_free(mem);
|
||||
goto fail;
|
||||
}
|
||||
fwrite(pos, end - pos, 1, f);
|
||||
fclose(f);
|
||||
|
||||
BIO_free(mem);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
if (exts)
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
if (subj)
|
||||
X509_NAME_free(subj);
|
||||
if (req)
|
||||
X509_REQ_free(req);
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (pctx)
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int est_build_csr(struct hs20_osu_client *ctx, const char *url)
|
||||
{
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
int res;
|
||||
char old_cert_buf[200];
|
||||
char *old_cert = NULL;
|
||||
CsrAttrs *csrattrs = NULL;
|
||||
|
||||
buflen = os_strlen(url) + 100;
|
||||
buf = os_malloc(buflen);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
os_snprintf(buf, buflen, "%s/csrattrs", url);
|
||||
wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
|
||||
write_summary(ctx, "Download EST csrattrs from %s", buf);
|
||||
ctx->no_osu_cert_validation = 1;
|
||||
http_ocsp_set(ctx->http, 1);
|
||||
res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
|
||||
ctx->ca_fname);
|
||||
http_ocsp_set(ctx->http,
|
||||
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
|
||||
ctx->no_osu_cert_validation = 0;
|
||||
os_free(buf);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
|
||||
} else {
|
||||
size_t resp_len;
|
||||
char *resp;
|
||||
unsigned char *attrs;
|
||||
const unsigned char *pos;
|
||||
size_t attrs_len;
|
||||
|
||||
resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not read csrattrs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
attrs = base64_decode(resp, resp_len, &attrs_len);
|
||||
os_free(resp);
|
||||
|
||||
if (attrs == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
|
||||
return -1;
|
||||
}
|
||||
unlink("Cert/est-csrattrs.txt");
|
||||
|
||||
pos = attrs;
|
||||
csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
|
||||
os_free(attrs);
|
||||
if (csrattrs == NULL) {
|
||||
wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
|
||||
/* Continue assuming no additional requirements */
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->client_cert_present) {
|
||||
os_snprintf(old_cert_buf, sizeof(old_cert_buf),
|
||||
"SP/%s/client-cert.pem", ctx->fqdn);
|
||||
old_cert = old_cert_buf;
|
||||
}
|
||||
|
||||
res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
|
||||
"Cert/est-req.b64", old_cert, csrattrs);
|
||||
if (csrattrs)
|
||||
CsrAttrs_free(csrattrs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
|
||||
const char *user, const char *pw)
|
||||
{
|
||||
char *buf, *resp, *req, *req2;
|
||||
size_t buflen, resp_len, len, pkcs7_len;
|
||||
unsigned char *pkcs7;
|
||||
char client_cert_buf[200];
|
||||
char client_key_buf[200];
|
||||
const char *client_cert = NULL, *client_key = NULL;
|
||||
int res;
|
||||
|
||||
req = os_readfile("Cert/est-req.b64", &len);
|
||||
if (req == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
|
||||
return -1;
|
||||
}
|
||||
req2 = os_realloc(req, len + 1);
|
||||
if (req2 == NULL) {
|
||||
os_free(req);
|
||||
return -1;
|
||||
}
|
||||
req2[len] = '\0';
|
||||
req = req2;
|
||||
wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
|
||||
|
||||
buflen = os_strlen(url) + 100;
|
||||
buf = os_malloc(buflen);
|
||||
if (buf == NULL) {
|
||||
os_free(req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->client_cert_present) {
|
||||
os_snprintf(buf, buflen, "%s/simplereenroll", url);
|
||||
os_snprintf(client_cert_buf, sizeof(client_cert_buf),
|
||||
"SP/%s/client-cert.pem", ctx->fqdn);
|
||||
client_cert = client_cert_buf;
|
||||
os_snprintf(client_key_buf, sizeof(client_key_buf),
|
||||
"SP/%s/client-key.pem", ctx->fqdn);
|
||||
client_key = client_key_buf;
|
||||
} else
|
||||
os_snprintf(buf, buflen, "%s/simpleenroll", url);
|
||||
wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
|
||||
write_summary(ctx, "EST simpleenroll URL: %s", buf);
|
||||
ctx->no_osu_cert_validation = 1;
|
||||
http_ocsp_set(ctx->http, 1);
|
||||
resp = http_post(ctx->http, buf, req, "application/pkcs10",
|
||||
"Content-Transfer-Encoding: base64",
|
||||
ctx->ca_fname, user, pw, client_cert, client_key,
|
||||
&resp_len);
|
||||
http_ocsp_set(ctx->http,
|
||||
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
|
||||
ctx->no_osu_cert_validation = 0;
|
||||
os_free(buf);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_INFO, "EST certificate enrollment failed");
|
||||
write_result(ctx, "EST certificate enrollment failed");
|
||||
return -1;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
|
||||
|
||||
pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
|
||||
pkcs7 = os_malloc(resp_len);
|
||||
if (pkcs7) {
|
||||
os_memcpy(pkcs7, resp, resp_len);
|
||||
pkcs7_len = resp_len;
|
||||
}
|
||||
}
|
||||
os_free(resp);
|
||||
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
|
||||
write_result(ctx, "Failed to parse EST simpleenroll base64 response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
|
||||
"Cert/est_cert.der");
|
||||
os_free(pkcs7);
|
||||
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
|
||||
write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
|
||||
ctx->client_cert_present ? "re" : "");
|
||||
write_summary(ctx, "EST simple%senroll completed successfully",
|
||||
ctx->client_cert_present ? "re" : "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
1398
hs20/client/oma_dm_client.c
Normal file
1398
hs20/client/oma_dm_client.c
Normal file
File diff suppressed because it is too large
Load Diff
3474
hs20/client/osu_client.c
Normal file
3474
hs20/client/osu_client.c
Normal file
File diff suppressed because it is too large
Load Diff
121
hs20/client/osu_client.h
Normal file
121
hs20/client/osu_client.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Hotspot 2.0 - OSU client
|
||||
* Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef OSU_CLIENT_H
|
||||
#define OSU_CLIENT_H
|
||||
|
||||
#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
|
||||
|
||||
#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
|
||||
#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
|
||||
#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
|
||||
#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
|
||||
|
||||
|
||||
#define MAX_OSU_VALS 10
|
||||
|
||||
struct osu_lang_text {
|
||||
char lang[4];
|
||||
char text[253];
|
||||
};
|
||||
|
||||
struct hs20_osu_client {
|
||||
struct xml_node_ctx *xml;
|
||||
struct http_ctx *http;
|
||||
int no_reconnect;
|
||||
char pps_fname[300];
|
||||
char *devid;
|
||||
const char *result_file;
|
||||
const char *summary_file;
|
||||
const char *ifname;
|
||||
const char *ca_fname;
|
||||
int no_osu_cert_validation; /* for EST operations */
|
||||
char *fqdn;
|
||||
char *server_url;
|
||||
struct osu_lang_text friendly_name[MAX_OSU_VALS];
|
||||
size_t friendly_name_count;
|
||||
size_t icon_count;
|
||||
char icon_filename[MAX_OSU_VALS][256];
|
||||
u8 icon_hash[MAX_OSU_VALS][32];
|
||||
int pps_cred_set;
|
||||
int pps_updated;
|
||||
int client_cert_present;
|
||||
char **server_dnsname;
|
||||
size_t server_dnsname_count;
|
||||
const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
|
||||
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
|
||||
unsigned long int workarounds;
|
||||
int ignore_tls; /* whether to ignore TLS validation issues with HTTPS
|
||||
* server certificate */
|
||||
};
|
||||
|
||||
|
||||
/* osu_client.c */
|
||||
|
||||
void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
|
||||
xml_node_t *node);
|
||||
int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
|
||||
int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
|
||||
xml_node_t *add_mo, char *fname, size_t fname_len);
|
||||
void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
|
||||
const char *alt_loc, char **user, char **pw);
|
||||
int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
|
||||
xml_node_t *pps);
|
||||
void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
|
||||
|
||||
|
||||
/* spp_client.c */
|
||||
|
||||
void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
int cmd_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
|
||||
|
||||
/* oma_dm_client.c */
|
||||
|
||||
int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname);
|
||||
void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
|
||||
const char *add_fname);
|
||||
void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
|
||||
const char *replace_fname);
|
||||
|
||||
/* est.c */
|
||||
|
||||
int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
|
||||
int est_build_csr(struct hs20_osu_client *ctx, const char *url);
|
||||
int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
|
||||
const char *user, const char *pw);
|
||||
|
||||
#endif /* OSU_CLIENT_H */
|
||||
1003
hs20/client/spp_client.c
Normal file
1003
hs20/client/spp_client.c
Normal file
File diff suppressed because it is too large
Load Diff
12
src/Makefile
Normal file
12
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 pasn radius rsn_supp tls utils wps
|
||||
SUBDIRS += fst
|
||||
|
||||
all:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
|
||||
|
||||
clean:
|
||||
$(Q)for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
|
||||
$(Q)rm -f *~
|
||||
|
||||
install:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
|
||||
60
src/ap/Makefile
Normal file
60
src/ap/Makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
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_WPS
|
||||
CFLAGS += -DCONFIG_PROXYARP
|
||||
CFLAGS += -DCONFIG_IPV6
|
||||
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 \
|
||||
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
|
||||
|
||||
include ../lib.rules
|
||||
547
src/ap/accounting.c
Normal file
547
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
src/ap/accounting.h
Normal file
45
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 */
|
||||
1518
src/ap/acs.c
Normal file
1518
src/ap/acs.c
Normal file
File diff suppressed because it is too large
Load Diff
35
src/ap/acs.h
Normal file
35
src/ap/acs.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
#define ACS_SCAN_RETRY_MAX_COUNT 15
|
||||
#define ACS_SCAN_RETRY_INTERVAL 5
|
||||
|
||||
#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 */
|
||||
273
src/ap/airtime_policy.c
Normal file
273
src/ap/airtime_policy.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
if (hapd->force_backlog_bytes)
|
||||
data.backlog_bytes = 1;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
|
||||
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;
|
||||
bool all_div_min = true;
|
||||
bool 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 && !ether_addr_equal(wt->addr, sta))
|
||||
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
src/ap/airtime_policy.h
Normal file
48
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 */
|
||||
1789
src/ap/ap_config.c
Normal file
1789
src/ap/ap_config.c
Normal file
File diff suppressed because it is too large
Load Diff
1401
src/ap/ap_config.h
Normal file
1401
src/ap/ap_config.h
Normal file
File diff suppressed because it is too large
Load Diff
1252
src/ap/ap_drv_ops.c
Normal file
1252
src/ap/ap_drv_ops.c
Normal file
File diff suppressed because it is too large
Load Diff
481
src/ap/ap_drv_ops.h
Normal file
481
src/ap/ap_drv_ops.h
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* 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);
|
||||
bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
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,
|
||||
const struct ieee80211_eht_capabilities *eht_capab,
|
||||
size_t eht_capab_len,
|
||||
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
|
||||
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||
int set, const u8 *link_addr, bool mld_link_sta);
|
||||
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_if_link_remove(struct hostapd_data *hapd,
|
||||
enum wpa_driver_if_type type,
|
||||
const char *ifname, u8 link_id);
|
||||
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, int link_id, 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 edmg, u8 edmg_channel,
|
||||
int ht_enabled, int vht_enabled, int he_enabled,
|
||||
bool eht_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);
|
||||
bool hostapd_drv_nl80211(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 vlan_id, int set_tx,
|
||||
const u8 *seq, size_t seq_len,
|
||||
const u8 *key, size_t key_len, enum key_flag key_flag);
|
||||
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack,
|
||||
const u16 *csa_offs, size_t csa_offs_len,
|
||||
int no_encrypt);
|
||||
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, bool eht_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1,
|
||||
bool radar_background);
|
||||
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);
|
||||
int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
|
||||
int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
|
||||
const u8 *own_addr, const u8 *addr,
|
||||
u32 cipher, u8 key_len, const u8 *key,
|
||||
u8 ltf_keyseed_len,
|
||||
const u8 *ltf_keyseed, u32 action);
|
||||
|
||||
|
||||
#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);
|
||||
void hostapd_get_mld_capa(struct hostapd_iface *iface);
|
||||
|
||||
void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
|
||||
struct hostapd_hw_modes *mode,
|
||||
int acs_ch_list_all, bool allow_disabled,
|
||||
int **freq_list);
|
||||
|
||||
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,
|
||||
int link_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, link_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, int link_id)
|
||||
{
|
||||
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, link_id);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211AX
|
||||
static inline int hostapd_drv_switch_color(struct hostapd_data *hapd,
|
||||
struct cca_settings *settings)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->switch_color || !hapd->drv_priv)
|
||||
return -1;
|
||||
|
||||
return hapd->driver->switch_color(hapd->drv_priv, settings);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211AX */
|
||||
|
||||
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,
|
||||
enum nested_attr nested_attr_flag,
|
||||
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, nested_attr_flag, buf);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
|
||||
{
|
||||
int link_id = -1;
|
||||
|
||||
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
|
||||
return 0;
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (hapd->conf->mld_ap)
|
||||
link_id = hapd->mld_link_id;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
return hapd->driver->stop_ap(hapd->drv_priv, link_id);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static inline int
|
||||
hostapd_drv_set_band(struct hostapd_data *hapd, u32 band_mask)
|
||||
{
|
||||
if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band)
|
||||
return -1;
|
||||
return hapd->driver->set_band(hapd->drv_priv, band_mask);
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
static inline int hostapd_drv_driver_cmd(struct hostapd_data *hapd,
|
||||
char *cmd, char *buf, size_t buf_len)
|
||||
{
|
||||
if (!hapd->driver->driver_cmd)
|
||||
return -1;
|
||||
return hapd->driver->driver_cmd(hapd->drv_priv, cmd, buf, buf_len);
|
||||
}
|
||||
#endif /* ANDROID */
|
||||
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
static inline int
|
||||
hostapd_drv_register_frame(struct hostapd_data *hapd, u16 type,
|
||||
const u8 *match, size_t match_len,
|
||||
bool multicast)
|
||||
{
|
||||
if (!hapd->driver || !hapd->drv_priv || !hapd->driver->register_frame)
|
||||
return -1;
|
||||
return hapd->driver->register_frame(hapd->drv_priv, type, match,
|
||||
match_len, multicast);
|
||||
}
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
|
||||
static inline int hostapd_drv_link_add(struct hostapd_data *hapd,
|
||||
u8 link_id, const u8 *addr)
|
||||
{
|
||||
if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_add)
|
||||
return -1;
|
||||
|
||||
return hapd->driver->link_add(hapd->drv_priv, link_id, addr, hapd);
|
||||
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (!hapd->conf->mld_ap || !hapd->driver || !hapd->drv_priv ||
|
||||
!hapd->driver->link_sta_remove)
|
||||
return -1;
|
||||
|
||||
return hapd->driver->link_sta_remove(hapd->drv_priv, hapd->mld_link_id,
|
||||
addr);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
#endif /* AP_DRV_OPS */
|
||||
308
src/ap/ap_list.c
Normal file
308
src/ap/ap_list.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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 && !ether_addr_equal(s->addr, ap))
|
||||
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 (ether_addr_equal(s->addr, ap->addr)) {
|
||||
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
|
||||
return;
|
||||
}
|
||||
|
||||
while (s->hnext != NULL &&
|
||||
!ether_addr_equal(s->hnext->addr, ap->addr))
|
||||
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++;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
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
src/ap/ap_list.h
Normal file
58
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
src/ap/ap_mlme.c
Normal file
191
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";
|
||||
default:
|
||||
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
src/ap/ap_mlme.h
Normal file
34
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 */
|
||||
445
src/ap/authsrv.c
Normal file
445
src/ap/authsrv.c
Normal file
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* 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/crypto.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;
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (!hostapd_mld_is_first_bss(hapd)) {
|
||||
struct hostapd_data *first;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MLD: Using RADIUS server of the first BSS");
|
||||
|
||||
first = hostapd_mld_get_first_bss(hapd);
|
||||
if (!first)
|
||||
return -1;
|
||||
hapd->radius_srv = first->radius_srv;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
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.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.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_domain = conf->erp_domain;
|
||||
srv.eap_cfg = hapd->eap_cfg;
|
||||
|
||||
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;
|
||||
case TLS_UNSAFE_RENEGOTIATION_DISABLED:
|
||||
/* Not applicable to TLS server */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* EAP_TLS_FUNCS */
|
||||
|
||||
|
||||
static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd)
|
||||
{
|
||||
struct eap_config *cfg;
|
||||
|
||||
cfg = os_zalloc(sizeof(*cfg));
|
||||
if (!cfg)
|
||||
return NULL;
|
||||
|
||||
cfg->eap_server = hapd->conf->eap_server;
|
||||
cfg->ssl_ctx = hapd->ssl_ctx;
|
||||
cfg->msg_ctx = hapd->msg_ctx;
|
||||
cfg->eap_sim_db_priv = hapd->eap_sim_db_priv;
|
||||
cfg->tls_session_lifetime = hapd->conf->tls_session_lifetime;
|
||||
cfg->tls_flags = hapd->conf->tls_flags;
|
||||
cfg->max_auth_rounds = hapd->conf->max_auth_rounds;
|
||||
cfg->max_auth_rounds_short = hapd->conf->max_auth_rounds_short;
|
||||
if (hapd->conf->pac_opaque_encr_key)
|
||||
cfg->pac_opaque_encr_key =
|
||||
os_memdup(hapd->conf->pac_opaque_encr_key, 16);
|
||||
if (hapd->conf->eap_fast_a_id) {
|
||||
cfg->eap_fast_a_id = os_memdup(hapd->conf->eap_fast_a_id,
|
||||
hapd->conf->eap_fast_a_id_len);
|
||||
cfg->eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
|
||||
}
|
||||
if (hapd->conf->eap_fast_a_id_info)
|
||||
cfg->eap_fast_a_id_info =
|
||||
os_strdup(hapd->conf->eap_fast_a_id_info);
|
||||
cfg->eap_fast_prov = hapd->conf->eap_fast_prov;
|
||||
cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime;
|
||||
cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
|
||||
cfg->eap_teap_auth = hapd->conf->eap_teap_auth;
|
||||
cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
|
||||
cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
|
||||
cfg->eap_teap_id = hapd->conf->eap_teap_id;
|
||||
cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
|
||||
cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
|
||||
cfg->eap_sim_id = hapd->conf->eap_sim_id;
|
||||
cfg->imsi_privacy_key = hapd->imsi_privacy_key;
|
||||
cfg->eap_sim_aka_fast_reauth_limit =
|
||||
hapd->conf->eap_sim_aka_fast_reauth_limit;
|
||||
cfg->tnc = hapd->conf->tnc;
|
||||
cfg->wps = hapd->wps;
|
||||
cfg->fragment_size = hapd->conf->fragment_size;
|
||||
cfg->pwd_group = hapd->conf->pwd_group;
|
||||
cfg->pbc_in_m1 = hapd->conf->pbc_in_m1;
|
||||
if (hapd->conf->server_id) {
|
||||
cfg->server_id = (u8 *) os_strdup(hapd->conf->server_id);
|
||||
cfg->server_id_len = os_strlen(hapd->conf->server_id);
|
||||
} else {
|
||||
cfg->server_id = (u8 *) os_strdup("hostapd");
|
||||
cfg->server_id_len = 7;
|
||||
}
|
||||
cfg->erp = hapd->conf->eap_server_erp;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
cfg->skip_prot_success = hapd->conf->eap_skip_prot_success;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
||||
int authsrv_init(struct hostapd_data *hapd)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (!hostapd_mld_is_first_bss(hapd)) {
|
||||
struct hostapd_data *first;
|
||||
|
||||
first = hostapd_mld_get_first_bss(hapd);
|
||||
if (!first)
|
||||
return -1;
|
||||
|
||||
if (!first->eap_cfg) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MLD: First BSS auth_serv does not exist. Init on its behalf");
|
||||
|
||||
if (authsrv_init(first))
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
|
||||
|
||||
#ifdef EAP_TLS_FUNCS
|
||||
hapd->ssl_ctx = first->ssl_ctx;
|
||||
#endif /* EAP_TLS_FUNCS */
|
||||
hapd->eap_cfg = first->eap_cfg;
|
||||
#ifdef EAP_SIM_DB
|
||||
hapd->eap_sim_db_priv = first->eap_sim_db_priv;
|
||||
#endif /* EAP_SIM_DB */
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
#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 CRYPTO_RSA_OAEP_SHA256
|
||||
crypto_rsa_key_free(hapd->imsi_privacy_key);
|
||||
hapd->imsi_privacy_key = NULL;
|
||||
if (hapd->conf->imsi_privacy_key) {
|
||||
hapd->imsi_privacy_key = crypto_rsa_key_read(
|
||||
hapd->conf->imsi_privacy_key, true);
|
||||
if (!hapd->imsi_privacy_key) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Failed to read/parse IMSI privacy key %s",
|
||||
hapd->conf->imsi_privacy_key);
|
||||
authsrv_deinit(hapd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
|
||||
#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 */
|
||||
|
||||
hapd->eap_cfg = authsrv_eap_config(hapd);
|
||||
if (!hapd->eap_cfg) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Failed to build EAP server configuration");
|
||||
authsrv_deinit(hapd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#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 CONFIG_IEEE80211BE
|
||||
if (!hostapd_mld_is_first_bss(hapd)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MLD: Deinit auth_serv of a non-first BSS");
|
||||
|
||||
hapd->radius_srv = NULL;
|
||||
hapd->eap_cfg = NULL;
|
||||
#ifdef EAP_SIM_DB
|
||||
hapd->eap_sim_db_priv = NULL;
|
||||
#endif /* EAP_SIM_DB */
|
||||
#ifdef EAP_TLS_FUNCS
|
||||
hapd->ssl_ctx = NULL;
|
||||
#endif /* EAP_TLS_FUNCS */
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
#ifdef RADIUS_SERVER
|
||||
radius_server_deinit(hapd->radius_srv);
|
||||
hapd->radius_srv = NULL;
|
||||
#endif /* RADIUS_SERVER */
|
||||
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
crypto_rsa_key_free(hapd->imsi_privacy_key);
|
||||
hapd->imsi_privacy_key = NULL;
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
|
||||
#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 */
|
||||
|
||||
eap_server_config_free(hapd->eap_cfg);
|
||||
hapd->eap_cfg = NULL;
|
||||
}
|
||||
15
src/ap/authsrv.h
Normal file
15
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 */
|
||||
2762
src/ap/beacon.c
Normal file
2762
src/ap/beacon.c
Normal file
File diff suppressed because it is too large
Load Diff
39
src/ap/beacon.h
Normal file
39
src/ap/beacon.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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);
|
||||
void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
|
||||
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);
|
||||
|
||||
const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid);
|
||||
|
||||
u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
|
||||
struct unsol_bcast_probe_resp *ubpr);
|
||||
|
||||
#endif /* BEACON_H */
|
||||
99
src/ap/bss_load.c
Normal file
99
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_per_bss_only(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
src/ap/bss_load.h
Normal file
17
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 */
|
||||
139
src/ap/comeback_token.c
Normal file
139
src/ap/comeback_token.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* hostapd / Comeback token mechanism for SAE
|
||||
* Copyright (c) 2002-2017, 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 "hostapd.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "crypto/random.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "comeback_token.h"
|
||||
|
||||
|
||||
#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
|
||||
|
||||
static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
|
||||
{
|
||||
u8 hash[SHA256_MAC_LEN];
|
||||
|
||||
if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
|
||||
addr, ETH_ALEN, hash) < 0)
|
||||
return -1;
|
||||
*idx = hash[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int check_comeback_token(const u8 *comeback_key,
|
||||
u16 *comeback_pending_idx, const u8 *addr,
|
||||
const u8 *token, size_t token_len)
|
||||
{
|
||||
u8 mac[SHA256_MAC_LEN];
|
||||
const u8 *addrs[2];
|
||||
size_t len[2];
|
||||
u16 token_idx;
|
||||
u8 idx;
|
||||
|
||||
if (token_len != SHA256_MAC_LEN ||
|
||||
comeback_token_hash(comeback_key, addr, &idx) < 0)
|
||||
return -1;
|
||||
token_idx = comeback_pending_idx[idx];
|
||||
if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Comeback: Invalid anti-clogging token from "
|
||||
MACSTR " - token_idx 0x%04x, expected 0x%04x",
|
||||
MAC2STR(addr), WPA_GET_BE16(token), token_idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addrs[0] = addr;
|
||||
len[0] = ETH_ALEN;
|
||||
addrs[1] = token;
|
||||
len[1] = 2;
|
||||
if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
|
||||
2, addrs, len, mac) < 0 ||
|
||||
os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
|
||||
return -1;
|
||||
|
||||
comeback_pending_idx[idx] = 0; /* invalidate used token */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf *
|
||||
auth_build_token_req(struct os_reltime *last_comeback_key_update,
|
||||
u8 *comeback_key, u16 comeback_idx,
|
||||
u16 *comeback_pending_idx, size_t idx_len,
|
||||
int group, const u8 *addr, int h2e)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
u8 *token;
|
||||
struct os_reltime now;
|
||||
u8 idx[2];
|
||||
const u8 *addrs[2];
|
||||
size_t len[2];
|
||||
u8 p_idx;
|
||||
u16 token_idx;
|
||||
|
||||
os_get_reltime(&now);
|
||||
if (!os_reltime_initialized(last_comeback_key_update) ||
|
||||
os_reltime_expired(&now, last_comeback_key_update, 60) ||
|
||||
comeback_idx == 0xffff) {
|
||||
if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
|
||||
return NULL;
|
||||
wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
|
||||
comeback_key, COMEBACK_KEY_SIZE);
|
||||
*last_comeback_key_update = now;
|
||||
comeback_idx = 0;
|
||||
os_memset(comeback_pending_idx, 0, idx_len);
|
||||
}
|
||||
|
||||
buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
if (group)
|
||||
wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
|
||||
|
||||
if (h2e) {
|
||||
/* Encapsulate Anti-clogging Token field in a container IE */
|
||||
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
|
||||
wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
|
||||
wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
|
||||
}
|
||||
|
||||
if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
token_idx = comeback_pending_idx[p_idx];
|
||||
if (!token_idx) {
|
||||
comeback_idx++;
|
||||
token_idx = comeback_idx;
|
||||
comeback_pending_idx[p_idx] = token_idx;
|
||||
}
|
||||
WPA_PUT_BE16(idx, token_idx);
|
||||
token = wpabuf_put(buf, SHA256_MAC_LEN);
|
||||
addrs[0] = addr;
|
||||
len[0] = ETH_ALEN;
|
||||
addrs[1] = idx;
|
||||
len[1] = sizeof(idx);
|
||||
if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
|
||||
2, addrs, len, token) < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
WPA_PUT_BE16(token, token_idx);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
|
||||
21
src/ap/comeback_token.h
Normal file
21
src/ap/comeback_token.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* hostapd / Comeback token mechanism for SAE
|
||||
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef COMEBACK_TOKEN_H
|
||||
#define COMEBACK_TOKEN_H
|
||||
|
||||
int check_comeback_token(const u8 *comeback_key,
|
||||
u16 *comeback_pending_idx, const u8 *addr,
|
||||
const u8 *token, size_t token_len);
|
||||
struct wpabuf *
|
||||
auth_build_token_req(struct os_reltime *last_comeback_key_update,
|
||||
u8 *comeback_key, u16 comeback_idx,
|
||||
u16 *comeback_pending_idx, size_t idx_len,
|
||||
int group, const u8 *addr, int h2e);
|
||||
|
||||
#endif /* COMEBACK_TOKEN_H */
|
||||
1622
src/ap/ctrl_iface_ap.c
Normal file
1622
src/ap/ctrl_iface_ap.c
Normal file
File diff suppressed because it is too large
Load Diff
57
src/ap/ctrl_iface_ap.h
Normal file
57
src/ap/ctrl_iface_ap.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
|
||||
const char *cmd);
|
||||
int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
|
||||
const char *cmd);
|
||||
int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
|
||||
const char *cmd);
|
||||
int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
|
||||
const char *cmd);
|
||||
int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
|
||||
const char *txtaddr);
|
||||
void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
|
||||
int *num);
|
||||
int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_disassoc_accept_mac(struct hostapd_data *hapd);
|
||||
int hostapd_disassoc_deny_mac(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* CTRL_IFACE_AP_H */
|
||||
1669
src/ap/dfs.c
Normal file
1669
src/ap/dfs.c
Normal file
File diff suppressed because it is too large
Load Diff
36
src/ap/dfs.h
Normal file
36
src/ap/dfs.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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_is_dfs_chan_available(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);
|
||||
int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
|
||||
int center_freq);
|
||||
|
||||
#endif /* DFS_H */
|
||||
160
src/ap/dhcp_snoop.c
Normal file
160
src/ap/dhcp_snoop.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 ip_len;
|
||||
|
||||
exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
|
||||
if (exten_len < 4)
|
||||
return;
|
||||
|
||||
b = (const struct bootp_pkt *) &buf[ETH_HLEN];
|
||||
ip_len = ntohs(b->iph.ip_len);
|
||||
if (ip_len > (unsigned int) (len - ETH_HLEN))
|
||||
return;
|
||||
|
||||
if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
|
||||
return;
|
||||
|
||||
/* Parse DHCP options */
|
||||
end = (const u8 *) b + ip_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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HS20
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
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
src/ap/dhcp_snoop.h
Normal file
30
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 */
|
||||
4007
src/ap/dpp_hostapd.c
Normal file
4007
src/ap/dpp_hostapd.c
Normal file
File diff suppressed because it is too large
Load Diff
54
src/ap/dpp_hostapd.h
Normal file
54
src/ap/dpp_hostapd.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* hostapd / DPP integration
|
||||
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||
* Copyright (c) 2018-2020, The Linux Foundation
|
||||
*
|
||||
* 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
|
||||
|
||||
struct dpp_bootstrap_info;
|
||||
|
||||
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_nfc_handover_sel(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);
|
||||
|
||||
int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
|
||||
void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
|
||||
void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
|
||||
int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd);
|
||||
void hostapd_dpp_push_button_stop(struct hostapd_data *hapd);
|
||||
bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd);
|
||||
int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd);
|
||||
void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd);
|
||||
|
||||
#endif /* DPP_HOSTAPD_H */
|
||||
2798
src/ap/drv_callbacks.c
Normal file
2798
src/ap/drv_callbacks.c
Normal file
File diff suppressed because it is too large
Load Diff
290
src/ap/eap_user_db.c
Normal file
290
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
src/ap/eth_p_oui.c
Normal file
191
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
src/ap/eth_p_oui.h
Normal file
28
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
src/ap/fils_hlp.c
Normal file
654
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 ip *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->ip_v = 4;
|
||||
iph->ip_hl = sizeof(*iph) / 4;
|
||||
iph->ip_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
|
||||
iph->ip_ttl = 1;
|
||||
iph->ip_p = 17; /* UDP */
|
||||
iph->ip_src.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||
iph->ip_dst.s_addr = dhcp->client_ip;
|
||||
iph->ip_sum = 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 ip *iph;
|
||||
const struct udphdr *udph;
|
||||
u16 sport, dport, ulen;
|
||||
|
||||
if (len < sizeof(*iph) + sizeof(*udph))
|
||||
return 0;
|
||||
iph = (const struct ip *) 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 ip *iph;
|
||||
uint16_t ip_len;
|
||||
|
||||
if (len < sizeof(*iph))
|
||||
return 0;
|
||||
iph = (const struct ip *) pos;
|
||||
if (ip_checksum(iph, sizeof(*iph)) != 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP request IPv4 packet had invalid header checksum - dropped");
|
||||
return 0;
|
||||
}
|
||||
ip_len = ntohs(iph->ip_len);
|
||||
if (ip_len > len)
|
||||
return 0;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
|
||||
iph->ip_src.s_addr, iph->ip_dst.s_addr, iph->ip_p);
|
||||
switch (iph->ip_p) {
|
||||
case 17:
|
||||
return fils_process_hlp_udp(hapd, sta, dst, pos, len);
|
||||
default:
|
||||
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 (!ether_addr_equal(sta->addr, pos + ETH_ALEN)) {
|
||||
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);
|
||||
default:
|
||||
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
src/ap/fils_hlp.h
Normal file
27
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 */
|
||||
718
src/ap/gas_query_ap.c
Normal file
718
src/ap/gas_query_ap.c
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
|
||||
|
||||
/**
|
||||
* 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 (ether_addr_equal(q->addr, addr) &&
|
||||
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 (!ether_addr_equal(dst, query->addr)) {
|
||||
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);
|
||||
if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
|
||||
comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
|
||||
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 (ether_addr_equal(dst, q->addr) &&
|
||||
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
src/ap/gas_query_ap.h
Normal file
43
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 */
|
||||
1895
src/ap/gas_serv.c
Normal file
1895
src/ap/gas_serv.c
Normal file
File diff suppressed because it is too large
Load Diff
95
src/ap/gas_serv.h
Normal file
95
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, int freq);
|
||||
|
||||
#endif /* GAS_SERV_H */
|
||||
4951
src/ap/hostapd.c
Normal file
4951
src/ap/hostapd.c
Normal file
File diff suppressed because it is too large
Load Diff
852
src/ap/hostapd.h
Normal file
852
src/ap/hostapd.h
Normal file
@@ -0,0 +1,852 @@
|
||||
/*
|
||||
* 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 "common/dpp.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 */
|
||||
|
||||
#ifdef CONFIG_CTRL_IFACE_UDP
|
||||
#define CTRL_IFACE_COOKIE_LEN 8
|
||||
#endif /* CONFIG_CTRL_IFACE_UDP */
|
||||
|
||||
struct hostapd_iface;
|
||||
struct hostapd_mld;
|
||||
|
||||
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;
|
||||
#ifdef CONFIG_DPP3
|
||||
struct os_reltime dpp_pb_time;
|
||||
struct os_reltime dpp_pb_announce_time;
|
||||
struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
|
||||
struct dpp_bootstrap_info *dpp_pb_bi;
|
||||
u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
|
||||
u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
|
||||
struct os_reltime dpp_pb_last_resp;
|
||||
bool dpp_pb_result_indicated;
|
||||
char *dpp_pb_cmd;
|
||||
#endif /* CONFIG_DPP3 */
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
#ifdef CONFIG_CTRL_IFACE_UDP
|
||||
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
|
||||
#endif /* CONFIG_CTRL_IFACE_UDP */
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
struct hostapd_mld **mld;
|
||||
size_t mld_count;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
};
|
||||
|
||||
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 */
|
||||
HOSTAPD_CHAN_INVALID_NO_IR = 3, /* channel invalid due to AFC NO IR */
|
||||
};
|
||||
|
||||
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;
|
||||
u32 short_ssid;
|
||||
u8 bss_parameters;
|
||||
};
|
||||
|
||||
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 hostapd_cached_radius_acl *acl_cache;
|
||||
struct hostapd_acl_query_data *acl_queries;
|
||||
|
||||
struct wpa_authenticator *wpa_auth;
|
||||
struct eapol_authenticator *eapol_auth;
|
||||
struct eap_config *eap_cfg;
|
||||
|
||||
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 crypto_rsa_key *imsi_privacy_key;
|
||||
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,
|
||||
const u8 *ip);
|
||||
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_IEEE80211AX
|
||||
bool cca_in_progress;
|
||||
u8 cca_count;
|
||||
u8 cca_color;
|
||||
unsigned int cca_c_off_beacon;
|
||||
unsigned int cca_c_off_proberesp;
|
||||
struct os_reltime first_color_collision;
|
||||
struct os_reltime last_color_collision;
|
||||
u64 color_collision_bitmap;
|
||||
#endif /* CONFIG_IEEE80211AX */
|
||||
|
||||
#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;
|
||||
bool x_snoop_initialized;
|
||||
#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
|
||||
|
||||
#define COMEBACK_KEY_SIZE 8
|
||||
#define COMEBACK_PENDING_IDX_SIZE 256
|
||||
|
||||
/** Key used for generating SAE anti-clogging tokens */
|
||||
u8 comeback_key[COMEBACK_KEY_SIZE];
|
||||
struct os_reltime last_comeback_key_update;
|
||||
u16 comeback_idx;
|
||||
u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
|
||||
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;
|
||||
|
||||
enum wpa_alg last_igtk_alg;
|
||||
int last_igtk_key_idx;
|
||||
u8 last_igtk[WPA_IGTK_MAX_LEN];
|
||||
size_t last_igtk_len;
|
||||
|
||||
enum wpa_alg last_bigtk_alg;
|
||||
int last_bigtk_key_idx;
|
||||
u8 last_bigtk[WPA_BIGTK_MAX_LEN];
|
||||
size_t last_bigtk_len;
|
||||
|
||||
bool force_backlog_bytes;
|
||||
#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;
|
||||
u8 link_measurement_req_token;
|
||||
unsigned int lci_req_active:1;
|
||||
unsigned int range_req_active:1;
|
||||
unsigned int link_mesr_req_active:1;
|
||||
|
||||
int dhcp_sock; /* UDP socket used with the DHCP server */
|
||||
|
||||
struct ptksa_cache *ptksa;
|
||||
|
||||
#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;
|
||||
size_t dpp_pkex_code_len;
|
||||
char *dpp_pkex_identifier;
|
||||
enum dpp_pkex_ver dpp_pkex_ver;
|
||||
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_DPP2
|
||||
struct wpabuf *dpp_presence_announcement;
|
||||
struct dpp_bootstrap_info *dpp_chirp_bi;
|
||||
int dpp_chirp_freq;
|
||||
int *dpp_chirp_freqs;
|
||||
int dpp_chirp_iter;
|
||||
int dpp_chirp_round;
|
||||
int dpp_chirp_scan_done;
|
||||
int dpp_chirp_listen;
|
||||
struct os_reltime dpp_relay_last_needs_ctrl;
|
||||
#endif /* CONFIG_DPP2 */
|
||||
#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 */
|
||||
|
||||
#ifdef CONFIG_CTRL_IFACE_UDP
|
||||
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
|
||||
#endif /* CONFIG_CTRL_IFACE_UDP */
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
u8 eht_mld_bss_param_change;
|
||||
struct hostapd_mld *mld;
|
||||
struct dl_list link;
|
||||
u8 mld_link_id;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
u8 eht_mld_link_removal_count;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
#ifdef CONFIG_NAN_USD
|
||||
struct nan_de *nan_de;
|
||||
#endif /* CONFIG_NAN_USD */
|
||||
|
||||
u64 scan_cookie; /* Scan instance identifier for the ongoing HT40 scan
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
/**
|
||||
* struct hostapd_mld - hostapd per-mld data structure
|
||||
*/
|
||||
struct hostapd_mld {
|
||||
char name[IFNAMSIZ + 1];
|
||||
u8 mld_addr[ETH_ALEN];
|
||||
u8 next_link_id;
|
||||
u8 num_links;
|
||||
/* Number of hostapd_data (hapd) referencing this. num_links cannot be
|
||||
* used since num_links can go to 0 even when a BSS is disabled and
|
||||
* when it is re-enabled, the MLD should exist and hence it cannot be
|
||||
* freed when num_links is 0.
|
||||
*/
|
||||
u8 refcount;
|
||||
|
||||
struct hostapd_data *fbss;
|
||||
struct dl_list links; /* List head of all affiliated links */
|
||||
};
|
||||
|
||||
#define HOSTAPD_MLD_MAX_REF_COUNT 0xFF
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
/**
|
||||
* 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_NO_IR,
|
||||
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;
|
||||
u64 drv_flags2;
|
||||
unsigned int drv_rrm_flags;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
u16 mld_eml_capa, mld_mld_capa;
|
||||
|
||||
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;
|
||||
|
||||
bool radar_detected;
|
||||
|
||||
/* Background radar configuration */
|
||||
struct {
|
||||
int channel;
|
||||
int secondary_channel;
|
||||
int freq;
|
||||
int centr_freq_seg0_idx;
|
||||
int centr_freq_seg1_idx;
|
||||
/* Main chain is on temporary channel during
|
||||
* CAC detection on radar offchain.
|
||||
*/
|
||||
unsigned int temp_ch:1;
|
||||
/* CAC started on radar offchain */
|
||||
unsigned int cac_started:1;
|
||||
} radar_background;
|
||||
|
||||
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;
|
||||
unsigned int acs_num_retries;
|
||||
#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];
|
||||
|
||||
/* Maximum number of interfaces supported for MBSSID advertisement */
|
||||
unsigned int mbssid_max_interfaces;
|
||||
/* Maximum profile periodicity for enhanced MBSSID advertisement */
|
||||
unsigned int ema_max_periodicity;
|
||||
|
||||
int (*enable_iface_cb)(struct hostapd_iface *iface);
|
||||
int (*disable_iface_cb)(struct hostapd_iface *iface);
|
||||
|
||||
/* Configured freq of interface is NO_IR */
|
||||
bool is_no_ir;
|
||||
|
||||
bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
|
||||
};
|
||||
|
||||
/* 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_reload_bss_only(struct hostapd_data *bss);
|
||||
int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
|
||||
void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
|
||||
void hostapd_free_hapd_data(struct hostapd_data *hapd);
|
||||
void hostapd_cleanup_iface_partial(struct hostapd_iface *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_config(struct hostapd_data *hapd,
|
||||
struct hostapd_freq_params *freq_params);
|
||||
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);
|
||||
void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
|
||||
|
||||
void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
|
||||
void hostapd_cleanup_cca_params(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,
|
||||
int mld_assoc_link_id);
|
||||
|
||||
/* 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 *req_ie, size_t req_ielen, const u8 *resp_ie,
|
||||
size_t resp_ielen, const u8 *link_addr, 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,
|
||||
u16 punct_bitmap, 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 */
|
||||
|
||||
int hostapd_set_acl(struct hostapd_data *hapd);
|
||||
struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
|
||||
int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
|
||||
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
|
||||
u8 link_id);
|
||||
int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
|
||||
bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
|
||||
struct hostapd_data *hapd2);
|
||||
u8 hostapd_get_mld_id(struct hostapd_data *hapd);
|
||||
int hostapd_mld_add_link(struct hostapd_data *hapd);
|
||||
int hostapd_mld_remove_link(struct hostapd_data *hapd);
|
||||
struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
|
||||
|
||||
void free_beacon_data(struct beacon_data *beacon);
|
||||
int hostapd_fill_cca_settings(struct hostapd_data *hapd,
|
||||
struct cca_settings *settings);
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
|
||||
bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
|
||||
|
||||
#define for_each_mld_link(partner, self) \
|
||||
dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
|
||||
|
||||
#else /* CONFIG_IEEE80211BE */
|
||||
|
||||
static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#define for_each_mld_link(partner, self) \
|
||||
if (false)
|
||||
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* HOSTAPD_H */
|
||||
255
src/ap/hs20.c
Normal file
255
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);
|
||||
}
|
||||
if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
|
||||
capab |= WPA_CAPABILITY_MFPC;
|
||||
if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
|
||||
capab |= WPA_CAPABILITY_MFPR;
|
||||
}
|
||||
#ifdef CONFIG_OCV
|
||||
if (hapd->conf->ocv &&
|
||||
(hapd->iface->drv_flags2 &
|
||||
(WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_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
src/ap/hs20.h
Normal file
26
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 */
|
||||
1393
src/ap/hw_features.c
Normal file
1393
src/ap/hw_features.c
Normal file
File diff suppressed because it is too large
Load Diff
108
src/ap/hw_features.h
Normal file
108
src/ap/hw_features.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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_csa_update_hwmode(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_check_edmg_capab(struct hostapd_iface *iface);
|
||||
int hostapd_check_he_6ghz_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);
|
||||
int hostapd_hw_skip_mode(struct hostapd_iface *iface,
|
||||
struct hostapd_hw_modes *mode);
|
||||
int hostapd_determine_mode(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_csa_update_hwmode(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 "UNKNOWN";
|
||||
}
|
||||
|
||||
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_check_edmg_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)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int hostapd_hw_skip_mode(struct hostapd_iface *iface,
|
||||
struct hostapd_hw_modes *mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hostapd_determine_mode(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* NEED_AP_MLME */
|
||||
|
||||
#endif /* HW_FEATURES_H */
|
||||
8278
src/ap/ieee802_11.c
Normal file
8278
src/ap/ieee802_11.c
Normal file
File diff suppressed because it is too large
Load Diff
267
src/ap/ieee802_11.h
Normal file
267
src/ap/ieee802_11.h
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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 radius_sta;
|
||||
enum ieee80211_op_mode;
|
||||
enum oper_chan_width;
|
||||
struct ieee802_11_elems;
|
||||
struct sae_pk;
|
||||
struct sae_pt;
|
||||
struct sae_password_entry;
|
||||
struct mld_info;
|
||||
|
||||
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,
|
||||
bool mbssid_complete);
|
||||
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_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
|
||||
size_t len);
|
||||
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_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);
|
||||
u8 * hostapd_eid_he_6ghz_band_cap(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);
|
||||
void hostapd_get_eht_capab(struct hostapd_data *hapd,
|
||||
const struct ieee80211_eht_capabilities *src,
|
||||
struct ieee80211_eht_capabilities *dest,
|
||||
size_t len);
|
||||
u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
|
||||
struct mld_info *mld_info,
|
||||
u8 *eid, bool include_mld_id);
|
||||
u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
|
||||
u8 *eid);
|
||||
size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
|
||||
struct mld_info *info,
|
||||
bool include_mld_id);
|
||||
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
|
||||
const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len);
|
||||
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
|
||||
struct ieee802_11_elems *elems,
|
||||
struct sta_info *sta);
|
||||
int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
|
||||
const u8 *basic_mle, size_t basic_mle_len,
|
||||
u8 *mld_addr);
|
||||
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);
|
||||
|
||||
int 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);
|
||||
u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *he_6ghz_capab);
|
||||
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
|
||||
enum ieee80211_op_mode mode);
|
||||
bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd);
|
||||
u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
|
||||
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *buf, 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,
|
||||
u16 value);
|
||||
|
||||
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 *status);
|
||||
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,
|
||||
const u8 *link_addr);
|
||||
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);
|
||||
|
||||
size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd);
|
||||
u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len);
|
||||
|
||||
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);
|
||||
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
|
||||
u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *ext_capab_ie, size_t ext_capab_ie_len);
|
||||
size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
|
||||
bool include_mld_params);
|
||||
u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
|
||||
bool include_mld_params);
|
||||
int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int res, struct radius_sta *info);
|
||||
size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
|
||||
enum ieee80211_op_mode opmode);
|
||||
u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
|
||||
enum ieee80211_op_mode opmode);
|
||||
u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
|
||||
u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
enum ieee80211_op_mode opmode,
|
||||
const u8 *he_capab, size_t he_capab_len,
|
||||
const u8 *eht_capab, size_t eht_capab_len);
|
||||
size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
|
||||
u8 *elem_count, const u8 *known_bss,
|
||||
size_t known_bss_len, size_t *rnr_len);
|
||||
u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
|
||||
unsigned int frame_stype, u8 elem_count,
|
||||
u8 **elem_offset,
|
||||
const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
|
||||
u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
|
||||
bool hostapd_is_mld_ap(struct hostapd_data *hapd);
|
||||
const char * sae_get_password(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, const char *rx_id,
|
||||
struct sae_password_entry **pw_entry,
|
||||
struct sae_pt **s_pt, const struct sae_pk **s_pk);
|
||||
struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct hostapd_data **assoc_hapd);
|
||||
int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ies, size_t ies_len,
|
||||
bool reassoc, int tx_link_status,
|
||||
bool offload);
|
||||
|
||||
#endif /* IEEE802_11_H */
|
||||
751
src/ap/ieee802_11_auth.c
Normal file
751
src/ap/ieee802_11_auth.c
Normal file
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
* hostapd / IEEE 802.11 authentication (ACL)
|
||||
* Copyright (c) 2003-2022, 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 "sta_info.h"
|
||||
#include "wpa_auth.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;
|
||||
struct radius_sta info;
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
bool radius_psk;
|
||||
int akm;
|
||||
u8 *anonce;
|
||||
u8 *eapol;
|
||||
size_t eapol_len;
|
||||
};
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
|
||||
{
|
||||
os_free(e->info.identity);
|
||||
os_free(e->info.radius_cui);
|
||||
hostapd_free_psk_list(e->info.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 int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
|
||||
struct radius_sta *out)
|
||||
{
|
||||
struct hostapd_cached_radius_acl *entry;
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
for (entry = hapd->acl_cache; entry; entry = entry->next) {
|
||||
if (!ether_addr_equal(entry->addr, addr))
|
||||
continue;
|
||||
|
||||
if (os_reltime_expired(&now, &entry->timestamp,
|
||||
RADIUS_ACL_TIMEOUT))
|
||||
return -1; /* entry has expired */
|
||||
*out = entry->info;
|
||||
|
||||
return entry->accepted;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
|
||||
static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
|
||||
{
|
||||
if (!query)
|
||||
return;
|
||||
os_free(query->auth_msg);
|
||||
os_free(query->anonce);
|
||||
os_free(query->eapol);
|
||||
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)
|
||||
return -1;
|
||||
|
||||
if (radius_msg_make_authenticator(msg) < 0) {
|
||||
wpa_printf(MSG_INFO, "Could not make Request Authenticator");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!radius_msg_add_msg_auth(msg))
|
||||
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 (query->akm &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
|
||||
wpa_akm_to_suite(query->akm))) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (query->anonce &&
|
||||
!radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
|
||||
RADIUS_VENDOR_ID_FREERADIUS,
|
||||
RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE,
|
||||
query->anonce, WPA_NONCE_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-Anonce");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (query->eapol &&
|
||||
!radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
|
||||
RADIUS_VENDOR_ID_FREERADIUS,
|
||||
RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG,
|
||||
query->eapol, query->eapol_len)) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-EAPoL-Key-Msg");
|
||||
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
|
||||
* @out.session_timeout: Buffer for returning session timeout (from RADIUS)
|
||||
* @out.acct_interim_interval: Buffer for returning account interval (from
|
||||
* RADIUS)
|
||||
* @out.vlan_id: Buffer for returning VLAN ID
|
||||
* @out.psk: Linked list buffer for returning WPA PSK
|
||||
* @out.identity: Buffer for returning identity (from RADIUS)
|
||||
* @out.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 properly cloning the returned out->identity and
|
||||
* out->radius_cui and out->psk values.
|
||||
*/
|
||||
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *msg, size_t len, struct radius_sta *out,
|
||||
int is_probe_req)
|
||||
{
|
||||
int res;
|
||||
|
||||
os_memset(out, 0, sizeof(*out));
|
||||
|
||||
res = hostapd_check_acl(hapd, addr, &out->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)
|
||||
os_memset(&out->vlan_id, 0, sizeof(out->vlan_id));
|
||||
|
||||
/* Check whether ACL cache has an entry for this station */
|
||||
res = hostapd_acl_cache_get(hapd, addr, out);
|
||||
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 (ether_addr_equal(query->addr, addr)) {
|
||||
/* pending query in RADIUS retransmit queue;
|
||||
* do not generate a new one */
|
||||
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) {
|
||||
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) {
|
||||
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)
|
||||
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->info.psk;
|
||||
cache->info.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_sta *info;
|
||||
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)
|
||||
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,
|
||||
hapd->conf->radius_require_message_authenticator)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Incoming RADIUS packet did not have correct authenticator - dropped");
|
||||
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) {
|
||||
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));
|
||||
info = &cache->info;
|
||||
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
|
||||
u8 *buf;
|
||||
size_t len;
|
||||
|
||||
if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
|
||||
&info->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,
|
||||
&info->acct_interim_interval) == 0 &&
|
||||
info->acct_interim_interval < 60) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Ignored too small Acct-Interim-Interval %d for STA "
|
||||
MACSTR,
|
||||
info->acct_interim_interval,
|
||||
MAC2STR(query->addr));
|
||||
info->acct_interim_interval = 0;
|
||||
}
|
||||
|
||||
if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
|
||||
info->vlan_id.notempty = !!radius_msg_get_vlanid(
|
||||
msg, &info->vlan_id.untagged,
|
||||
MAX_NUM_TAGGED_VLAN, info->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) {
|
||||
info->identity = os_zalloc(len + 1);
|
||||
if (info->identity)
|
||||
os_memcpy(info->identity, buf, len);
|
||||
}
|
||||
if (radius_msg_get_attr_ptr(
|
||||
msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||
&buf, &len, NULL) == 0) {
|
||||
info->radius_cui = os_zalloc(len + 1);
|
||||
if (info->radius_cui)
|
||||
os_memcpy(info->radius_cui, buf, len);
|
||||
}
|
||||
|
||||
if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
|
||||
!info->psk)
|
||||
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||
|
||||
if (info->vlan_id.notempty &&
|
||||
!hostapd_vlan_valid(hapd->conf->vlan, &info->vlan_id)) {
|
||||
hostapd_logger(hapd, query->addr,
|
||||
HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Invalid VLAN %d%s received from RADIUS server",
|
||||
info->vlan_id.untagged,
|
||||
info->vlan_id.tagged[0] ? "+" : "");
|
||||
os_memset(&info->vlan_id, 0, sizeof(info->vlan_id));
|
||||
}
|
||||
if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
|
||||
!info->vlan_id.notempty)
|
||||
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||
} else
|
||||
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||
cache->next = hapd->acl_cache;
|
||||
hapd->acl_cache = cache;
|
||||
|
||||
if (query->radius_psk) {
|
||||
struct sta_info *sta;
|
||||
bool success = cache->accepted == HOSTAPD_ACL_ACCEPT ||
|
||||
cache->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT;
|
||||
|
||||
sta = ap_get_sta(hapd, query->addr);
|
||||
if (!sta || !sta->wpa_sm) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"No STA/SM entry found for the RADIUS PSK response");
|
||||
goto done;
|
||||
}
|
||||
#ifdef NEED_AP_MLME
|
||||
if (success &&
|
||||
(ieee802_11_set_radius_info(hapd, sta, cache->accepted,
|
||||
info) < 0 ||
|
||||
ap_sta_bind_vlan(hapd, sta) < 0))
|
||||
success = false;
|
||||
#endif /* NEED_AP_MLME */
|
||||
wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success);
|
||||
} else {
|
||||
#ifdef CONFIG_DRIVER_RADIUS_ACL
|
||||
hostapd_drv_set_radius_acl_auth(hapd, query->addr,
|
||||
cache->accepted,
|
||||
info->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)
|
||||
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_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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
bin_clear_free(prev, sizeof(*prev));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
|
||||
int key_mgmt, const u8 *anonce,
|
||||
const u8 *eapol, size_t eapol_len)
|
||||
{
|
||||
struct hostapd_acl_query_data *query;
|
||||
|
||||
query = os_zalloc(sizeof(*query));
|
||||
if (!query)
|
||||
return;
|
||||
|
||||
query->radius_psk = true;
|
||||
query->akm = key_mgmt;
|
||||
os_get_reltime(&query->timestamp);
|
||||
os_memcpy(query->addr, addr, ETH_ALEN);
|
||||
if (anonce)
|
||||
query->anonce = os_memdup(anonce, WPA_NONCE_LEN);
|
||||
if (eapol) {
|
||||
query->eapol = os_memdup(eapol, eapol_len);
|
||||
query->eapol_len = eapol_len;
|
||||
}
|
||||
if (hostapd_radius_acl_query(hapd, addr, query)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Failed to send Access-Request for RADIUS PSK/ACL query");
|
||||
hostapd_acl_query_free(query);
|
||||
return;
|
||||
}
|
||||
|
||||
query->next = hapd->acl_queries;
|
||||
hapd->acl_queries = query;
|
||||
}
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
43
src/ap/ieee802_11_auth.h
Normal file
43
src/ap/ieee802_11_auth.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* hostapd / IEEE 802.11 authentication (ACL)
|
||||
* Copyright (c) 2003-2022, 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
|
||||
};
|
||||
|
||||
struct radius_sta {
|
||||
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 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, struct radius_sta *out,
|
||||
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);
|
||||
void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
|
||||
struct hostapd_sta_wpa_psk_short *src);
|
||||
void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
|
||||
int key_mgmt, const u8 *anonce,
|
||||
const u8 *eapol, size_t eapol_len);
|
||||
|
||||
#endif /* IEEE802_11_AUTH_H */
|
||||
1405
src/ap/ieee802_11_eht.c
Normal file
1405
src/ap/ieee802_11_eht.c
Normal file
File diff suppressed because it is too large
Load Diff
571
src/ap/ieee802_11_he.c
Normal file
571
src/ap/ieee802_11_he.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* 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 "common/ieee802_11_common.h"
|
||||
#include "common/hw_features_common.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;
|
||||
/* Count the number of 1 bits in RU Index Bitmask */
|
||||
while (ru) {
|
||||
if (ru & 0x1)
|
||||
sz++;
|
||||
ru >>= 1;
|
||||
}
|
||||
|
||||
/* fixed header of 3 (NSTS) + 4 (RU Index Bitmask) = 7 bits */
|
||||
/* 6 * (NSTS + 1) bits for bit 1 in RU Index Bitmask */
|
||||
sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
|
||||
sz = (sz * 6) + 7;
|
||||
/* PPE Pad to count the number of needed full octets */
|
||||
sz = (sz + 7) / 8;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
static u8 ieee80211_he_mcs_set_size(const u8 *phy_cap_info)
|
||||
{
|
||||
u8 sz = 4;
|
||||
|
||||
if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
|
||||
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
|
||||
sz += 4;
|
||||
if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
|
||||
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
|
||||
sz += 4;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
|
||||
{
|
||||
struct ieee80211_he_capabilities *cap;
|
||||
size_t cap_len;
|
||||
u8 ppe_thres_hdr;
|
||||
|
||||
cap = (struct ieee80211_he_capabilities *) buf;
|
||||
cap_len = sizeof(*cap) - sizeof(cap->optional);
|
||||
if (len < cap_len)
|
||||
return 1;
|
||||
|
||||
cap_len += ieee80211_he_mcs_set_size(cap->he_phy_capab_info);
|
||||
if (len < cap_len)
|
||||
return 1;
|
||||
|
||||
ppe_thres_hdr = len > cap_len ? buf[cap_len] : 0xff;
|
||||
cap_len += ieee80211_he_ppet_size(ppe_thres_hdr,
|
||||
cap->he_phy_capab_info);
|
||||
|
||||
return len < cap_len;
|
||||
}
|
||||
|
||||
|
||||
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 = 4, ppet_size = 0;
|
||||
|
||||
if (!mode)
|
||||
return eid;
|
||||
|
||||
ie_size = sizeof(*cap) - sizeof(cap->optional);
|
||||
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 CONF_OPER_CHWIDTH_80P80MHZ:
|
||||
he_oper_chwidth |=
|
||||
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
|
||||
mcs_nss_size += 4;
|
||||
/* fall through */
|
||||
case CONF_OPER_CHWIDTH_160MHZ:
|
||||
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||
mcs_nss_size += 4;
|
||||
/* fall through */
|
||||
case CONF_OPER_CHWIDTH_80MHZ:
|
||||
case CONF_OPER_CHWIDTH_USE_HT:
|
||||
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
|
||||
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
|
||||
if (is_6ghz_op_class(hapd->iconf->op_class))
|
||||
oper_size += 5;
|
||||
|
||||
*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_er_su_disable)
|
||||
params |= HE_OPERATION_ER_SU_DISABLE;
|
||||
|
||||
if (hapd->iface->conf->he_op.he_bss_color_disabled ||
|
||||
hapd->cca_in_progress)
|
||||
params |= HE_OPERATION_BSS_COLOR_DISABLED;
|
||||
if (hapd->iface->conf->he_op.he_bss_color_partial)
|
||||
params |= HE_OPERATION_BSS_COLOR_PARTIAL;
|
||||
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 */
|
||||
|
||||
pos += 6; /* skip the fixed part */
|
||||
|
||||
if (is_6ghz_op_class(hapd->iconf->op_class)) {
|
||||
enum oper_chan_width oper_chwidth =
|
||||
hostapd_get_oper_chwidth(hapd->iconf);
|
||||
u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
|
||||
u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
|
||||
u8 control;
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
|
||||
|
||||
if (punct_bitmap) {
|
||||
punct_update_legacy_bw(punct_bitmap,
|
||||
hapd->iconf->channel,
|
||||
&oper_chwidth, &seg0, &seg1);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
if (!seg0)
|
||||
seg0 = hapd->iconf->channel;
|
||||
|
||||
params |= HE_OPERATION_6GHZ_OPER_INFO;
|
||||
|
||||
/* 6 GHz Operation Information field
|
||||
* IEEE Std 802.11ax-2021, 9.4.2.249 HE Operation element,
|
||||
* Figure 9-788k
|
||||
*/
|
||||
*pos++ = hapd->iconf->channel; /* Primary Channel */
|
||||
|
||||
/* Control:
|
||||
* bits 0-1: Channel Width
|
||||
* bit 2: Duplicate Beacon
|
||||
* bits 3-5: Regulatory Info
|
||||
*/
|
||||
/* Channel Width */
|
||||
if (seg1)
|
||||
control = 3;
|
||||
else
|
||||
control = center_idx_to_bw_6ghz(seg0);
|
||||
|
||||
control |= hapd->iconf->he_6ghz_reg_pwr_type <<
|
||||
HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
|
||||
|
||||
*pos++ = control;
|
||||
|
||||
/* Channel Center Freq Seg0/Seg1 */
|
||||
if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
|
||||
oper_chwidth == CONF_OPER_CHWIDTH_320MHZ) {
|
||||
/*
|
||||
* Seg 0 indicates the channel center frequency index of
|
||||
* the 160 MHz channel.
|
||||
*/
|
||||
seg1 = seg0;
|
||||
if (hapd->iconf->channel < seg0)
|
||||
seg0 -= 8;
|
||||
else
|
||||
seg0 += 8;
|
||||
}
|
||||
|
||||
*pos++ = seg0;
|
||||
*pos++ = seg1;
|
||||
/* Minimum Rate */
|
||||
*pos++ = 6; /* TODO: what should be set here? */
|
||||
}
|
||||
|
||||
oper->he_oper_params = host_to_le32(params);
|
||||
|
||||
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;
|
||||
os_memcpy(spr_param,
|
||||
hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
|
||||
spr_param += 8;
|
||||
os_memcpy(spr_param,
|
||||
hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
|
||||
pos += 18;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct hostapd_config *conf = hapd->iface->conf;
|
||||
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
|
||||
struct he_capabilities *he_cap;
|
||||
struct ieee80211_he_6ghz_band_cap *cap;
|
||||
u16 capab;
|
||||
u8 *pos;
|
||||
|
||||
if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
|
||||
!is_6ghz_freq(hapd->iface->freq))
|
||||
return eid;
|
||||
|
||||
he_cap = &mode->he_capab[IEEE80211_MODE_AP];
|
||||
capab = he_cap->he_6ghz_capa & HE_6GHZ_BAND_CAP_MIN_MPDU_START;
|
||||
capab |= (conf->he_6ghz_max_ampdu_len_exp <<
|
||||
HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT) &
|
||||
HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK;
|
||||
capab |= (conf->he_6ghz_max_mpdu <<
|
||||
HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT) &
|
||||
HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK;
|
||||
capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
|
||||
if (conf->he_6ghz_rx_ant_pat)
|
||||
capab |= HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS;
|
||||
if (conf->he_6ghz_tx_ant_pat)
|
||||
capab |= HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS;
|
||||
|
||||
pos = eid;
|
||||
*pos++ = WLAN_EID_EXTENSION;
|
||||
*pos++ = 1 + sizeof(*cap);
|
||||
*pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
|
||||
|
||||
cap = (struct ieee80211_he_6ghz_band_cap *) pos;
|
||||
cap->capab = host_to_le16(capab);
|
||||
pos += sizeof(*cap);
|
||||
|
||||
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 CONF_OPER_CHWIDTH_80P80MHZ:
|
||||
mcs_count = 3;
|
||||
break;
|
||||
case CONF_OPER_CHWIDTH_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 || !(sta->flags & WLAN_STA_WMM) ||
|
||||
!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax ||
|
||||
!check_valid_he_mcs(hapd, he_capab, opmode) ||
|
||||
ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *he_6ghz_capab)
|
||||
{
|
||||
if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
|
||||
hapd->conf->disable_11ax ||
|
||||
!is_6ghz_op_class(hapd->iconf->op_class)) {
|
||||
sta->flags &= ~WLAN_STA_6GHZ;
|
||||
os_free(sta->he_6ghz_capab);
|
||||
sta->he_6ghz_capab = NULL;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!sta->he_6ghz_capab) {
|
||||
sta->he_6ghz_capab =
|
||||
os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
|
||||
if (!sta->he_6ghz_capab)
|
||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
sta->flags |= WLAN_STA_6GHZ;
|
||||
os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
|
||||
sizeof(struct ieee80211_he_6ghz_band_cap));
|
||||
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
|
||||
enum ieee80211_op_mode mode)
|
||||
{
|
||||
u8 *mac_cap;
|
||||
|
||||
if (!hapd->iface->current_mode ||
|
||||
!hapd->iface->current_mode->he_capab[mode].he_supported ||
|
||||
!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
|
||||
return 0;
|
||||
|
||||
mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
|
||||
|
||||
return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
|
||||
hapd->iface->conf->he_op.he_twt_responder;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
if (!hapd->cca_in_progress)
|
||||
return eid;
|
||||
|
||||
/* BSS Color Change Announcement element */
|
||||
*eid++ = WLAN_EID_EXTENSION;
|
||||
*eid++ = 3;
|
||||
*eid++ = WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT;
|
||||
*eid++ = hapd->cca_count; /* Color Switch Countdown */
|
||||
*eid++ = hapd->cca_color; /* New BSS Color Information */
|
||||
|
||||
return eid;
|
||||
}
|
||||
534
src/ap/ieee802_11_ht.c
Normal file
534
src/ap/ieee802_11_ht.c
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* 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 || is_6ghz_op_class(hapd->iconf->op_class))
|
||||
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 ||
|
||||
is_6ghz_op_class(hapd->iconf->op_class))
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
op_mode
|
||||
Set to 0 (HT pure) under the following 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int 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);
|
||||
|
||||
return hostapd_ht_operation_update(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);
|
||||
}
|
||||
1228
src/ap/ieee802_11_shared.c
Normal file
1228
src/ap/ieee802_11_shared.c
Normal file
File diff suppressed because it is too large
Load Diff
371
src/ap/ieee802_11_vht.c
Normal file
371
src/ap/ieee802_11_vht.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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 "common/hw_features_common.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 || is_6ghz_op_class(hapd->iconf->op_class))
|
||||
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;
|
||||
enum oper_chan_width oper_chwidth =
|
||||
hostapd_get_oper_chwidth(hapd->iconf);
|
||||
u8 seg0 = hapd->iconf->vht_oper_centr_freq_seg0_idx;
|
||||
u8 seg1 = hapd->iconf->vht_oper_centr_freq_seg1_idx;
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
if (is_6ghz_op_class(hapd->iconf->op_class))
|
||||
return eid;
|
||||
|
||||
*pos++ = WLAN_EID_VHT_OPERATION;
|
||||
*pos++ = sizeof(*oper);
|
||||
|
||||
oper = (struct ieee80211_vht_operation *) pos;
|
||||
os_memset(oper, 0, sizeof(*oper));
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (punct_bitmap) {
|
||||
punct_update_legacy_bw(punct_bitmap,
|
||||
hapd->iconf->channel,
|
||||
&oper_chwidth, &seg0, &seg1);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
/*
|
||||
* 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 = seg0;
|
||||
oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
|
||||
|
||||
oper->vht_op_info_chwidth = oper_chwidth;
|
||||
if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
|
||||
/*
|
||||
* Convert 160 MHz channel width to new style as interop
|
||||
* workaround.
|
||||
*/
|
||||
oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
|
||||
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 (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
|
||||
/*
|
||||
* Convert 80+80 MHz channel width to new style as interop
|
||||
* workaround.
|
||||
*/
|
||||
oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
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 || !(sta->flags & WLAN_STA_WMM) ||
|
||||
!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;
|
||||
|
||||
/* Vendor VHT is applicable only to 2.4 GHz */
|
||||
if (!hapd->iface->current_mode ||
|
||||
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
||||
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);
|
||||
}
|
||||
3144
src/ap/ieee802_1x.c
Normal file
3144
src/ap/ieee802_1x.c
Normal file
File diff suppressed because it is too large
Load Diff
69
src/ap/ieee802_1x.h
Normal file
69
src/ap/ieee802_1x.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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, enum frame_encryption encrypted);
|
||||
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,
|
||||
bool enabled);
|
||||
void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, bool valid);
|
||||
void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, bool 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 */
|
||||
244
src/ap/mbo_ap.c
Normal file
244
src/ap/mbo_ap.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* hostapd - MBO
|
||||
* Copyright (c) 2016, 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/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "mbo_ap.h"
|
||||
|
||||
|
||||
void mbo_ap_sta_free(struct sta_info *sta)
|
||||
{
|
||||
struct mbo_non_pref_chan_info *info, *prev;
|
||||
|
||||
info = sta->non_pref_chan;
|
||||
sta->non_pref_chan = NULL;
|
||||
while (info) {
|
||||
prev = info;
|
||||
info = info->next;
|
||||
os_free(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct mbo_non_pref_chan_info *info, *tmp;
|
||||
char channels[200], *pos, *end;
|
||||
size_t num_chan, i;
|
||||
int ret;
|
||||
|
||||
if (len <= 3)
|
||||
return; /* Not enough room for any channels */
|
||||
|
||||
num_chan = len - 3;
|
||||
info = os_zalloc(sizeof(*info) + num_chan);
|
||||
if (!info)
|
||||
return;
|
||||
info->op_class = buf[0];
|
||||
info->pref = buf[len - 2];
|
||||
info->reason_code = buf[len - 1];
|
||||
info->num_channels = num_chan;
|
||||
buf++;
|
||||
os_memcpy(info->channels, buf, num_chan);
|
||||
if (!sta->non_pref_chan) {
|
||||
sta->non_pref_chan = info;
|
||||
} else {
|
||||
tmp = sta->non_pref_chan;
|
||||
while (tmp->next)
|
||||
tmp = tmp->next;
|
||||
tmp->next = info;
|
||||
}
|
||||
|
||||
pos = channels;
|
||||
end = pos + sizeof(channels);
|
||||
*pos = '\0';
|
||||
for (i = 0; i < num_chan; i++) {
|
||||
ret = os_snprintf(pos, end - pos, "%s%u",
|
||||
i == 0 ? "" : " ", buf[i]);
|
||||
if (os_snprintf_error(end - pos, ret)) {
|
||||
*pos = '\0';
|
||||
break;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
|
||||
" non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
|
||||
MAC2STR(sta->addr), info->op_class, info->pref,
|
||||
info->reason_code, channels);
|
||||
}
|
||||
|
||||
|
||||
void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
const u8 *pos, *attr, *end;
|
||||
size_t len;
|
||||
|
||||
if (!hapd->conf->mbo_enabled || !elems->mbo)
|
||||
return;
|
||||
|
||||
pos = elems->mbo + 4;
|
||||
len = elems->mbo_len - 4;
|
||||
wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
|
||||
|
||||
attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
|
||||
if (attr && attr[1] >= 1)
|
||||
sta->cell_capa = attr[2];
|
||||
|
||||
mbo_ap_sta_free(sta);
|
||||
end = pos + len;
|
||||
while (end - pos > 1) {
|
||||
u8 ie_len = pos[1];
|
||||
|
||||
if (2 + ie_len > end - pos)
|
||||
break;
|
||||
|
||||
if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
|
||||
mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
|
||||
{
|
||||
char *pos = buf, *end = buf + buflen;
|
||||
int ret;
|
||||
struct mbo_non_pref_chan_info *info;
|
||||
u8 i;
|
||||
unsigned int count = 0;
|
||||
|
||||
if (!sta->cell_capa)
|
||||
return 0;
|
||||
|
||||
ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return pos - buf;
|
||||
pos += ret;
|
||||
|
||||
for (info = sta->non_pref_chan; info; info = info->next) {
|
||||
char *pos2 = pos;
|
||||
|
||||
ret = os_snprintf(pos2, end - pos2,
|
||||
"non_pref_chan[%u]=%u:%u:%u:",
|
||||
count, info->op_class, info->pref,
|
||||
info->reason_code);
|
||||
count++;
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
|
||||
for (i = 0; i < info->num_channels; i++) {
|
||||
ret = os_snprintf(pos2, end - pos2, "%u%s",
|
||||
info->channels[i],
|
||||
i + 1 < info->num_channels ?
|
||||
"," : "");
|
||||
if (os_snprintf_error(end - pos2, ret)) {
|
||||
pos2 = NULL;
|
||||
break;
|
||||
}
|
||||
pos2 += ret;
|
||||
}
|
||||
|
||||
if (!pos2)
|
||||
break;
|
||||
ret = os_snprintf(pos2, end - pos2, "\n");
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
pos = pos2;
|
||||
}
|
||||
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
if (len < 1)
|
||||
return;
|
||||
wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
|
||||
" updated cellular data capability: %u",
|
||||
MAC2STR(sta->addr), buf[0]);
|
||||
sta->cell_capa = buf[0];
|
||||
}
|
||||
|
||||
|
||||
static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
|
||||
const u8 *buf, size_t len,
|
||||
int *first_non_pref_chan)
|
||||
{
|
||||
switch (type) {
|
||||
case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
|
||||
if (*first_non_pref_chan) {
|
||||
/*
|
||||
* Need to free the previously stored entries now to
|
||||
* allow the update to replace all entries.
|
||||
*/
|
||||
*first_non_pref_chan = 0;
|
||||
mbo_ap_sta_free(sta);
|
||||
}
|
||||
mbo_ap_parse_non_pref_chan(sta, buf, len);
|
||||
break;
|
||||
case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
|
||||
mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MBO: Ignore unknown WNM Notification WFA subelement %u",
|
||||
type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u8 ie_len;
|
||||
struct sta_info *sta;
|
||||
int first_non_pref_chan = 1;
|
||||
|
||||
if (!hapd->conf->mbo_enabled)
|
||||
return;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
pos = buf;
|
||||
end = buf + len;
|
||||
|
||||
while (end - pos > 1) {
|
||||
ie_len = pos[1];
|
||||
|
||||
if (2 + ie_len > end - pos)
|
||||
break;
|
||||
|
||||
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
|
||||
ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
|
||||
mbo_ap_wnm_notif_req_elem(sta, pos[5],
|
||||
pos + 6, ie_len - 4,
|
||||
&first_non_pref_chan);
|
||||
else
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MBO: Ignore unknown WNM Notification element %u (len=%u)",
|
||||
pos[0], pos[1]);
|
||||
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
}
|
||||
51
src/ap/mbo_ap.h
Normal file
51
src/ap/mbo_ap.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* MBO related functions and structures
|
||||
* Copyright (c) 2016, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef MBO_AP_H
|
||||
#define MBO_AP_H
|
||||
|
||||
struct hostapd_data;
|
||||
struct sta_info;
|
||||
struct ieee802_11_elems;
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
|
||||
void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems);
|
||||
int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
|
||||
void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *buf, size_t len);
|
||||
void mbo_ap_sta_free(struct sta_info *sta);
|
||||
|
||||
#else /* CONFIG_MBO */
|
||||
|
||||
static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
|
||||
const u8 *addr,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mbo_ap_sta_free(struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
#endif /* MBO_AP_H */
|
||||
267
src/ap/nan_usd_ap.c
Normal file
267
src/ap/nan_usd_ap.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* NAN unsynchronized service discovery (USD)
|
||||
* Copyright (c) 2024, Qualcomm Innovation Center, 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/wpa_ctrl.h"
|
||||
#include "common/nan_de.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "nan_usd_ap.h"
|
||||
|
||||
|
||||
static int hostapd_nan_de_tx(void *ctx, unsigned int freq,
|
||||
unsigned int wait_time,
|
||||
const u8 *dst, const u8 *src, const u8 *bssid,
|
||||
const struct wpabuf *buf)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
|
||||
" A3=" MACSTR " len=%zu",
|
||||
MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
|
||||
wpabuf_len(buf));
|
||||
|
||||
/* TODO: Force use of OFDM */
|
||||
return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
|
||||
unsigned int duration)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
|
||||
enum nan_service_protocol_type srv_proto_type,
|
||||
const u8 *ssi, size_t ssi_len,
|
||||
int peer_publish_id, const u8 *peer_addr,
|
||||
bool fsd, bool fsd_gas)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
char *ssi_hex;
|
||||
|
||||
ssi_hex = os_zalloc(2 * ssi_len + 1);
|
||||
if (!ssi_hex)
|
||||
return;
|
||||
if (ssi)
|
||||
wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
|
||||
"subscribe_id=%d publish_id=%d address=" MACSTR
|
||||
" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
|
||||
subscribe_id, peer_publish_id, MAC2STR(peer_addr),
|
||||
fsd, fsd_gas, srv_proto_type, ssi_hex);
|
||||
os_free(ssi_hex);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hostapd_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr,
|
||||
int peer_subscribe_id,
|
||||
enum nan_service_protocol_type srv_proto_type,
|
||||
const u8 *ssi, size_t ssi_len)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
char *ssi_hex;
|
||||
|
||||
ssi_hex = os_zalloc(2 * ssi_len + 1);
|
||||
if (!ssi_hex)
|
||||
return;
|
||||
if (ssi)
|
||||
wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_REPLIED
|
||||
"publish_id=%d address=" MACSTR
|
||||
" subscribe_id=%d srv_proto_type=%u ssi=%s",
|
||||
publish_id, MAC2STR(peer_addr), peer_subscribe_id,
|
||||
srv_proto_type, ssi_hex);
|
||||
os_free(ssi_hex);
|
||||
}
|
||||
|
||||
|
||||
static const char * nan_reason_txt(enum nan_de_reason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case NAN_DE_REASON_TIMEOUT:
|
||||
return "timeout";
|
||||
case NAN_DE_REASON_USER_REQUEST:
|
||||
return "user-request";
|
||||
case NAN_DE_REASON_FAILURE:
|
||||
return "failure";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_nan_de_publish_terminated(void *ctx, int publish_id,
|
||||
enum nan_de_reason reason)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_PUBLISH_TERMINATED
|
||||
"publish_id=%d reason=%s",
|
||||
publish_id, nan_reason_txt(reason));
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
|
||||
enum nan_de_reason reason)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
|
||||
"subscribe_id=%d reason=%s",
|
||||
subscribe_id, nan_reason_txt(reason));
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id,
|
||||
const u8 *ssi, size_t ssi_len,
|
||||
const u8 *peer_addr)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
char *ssi_hex;
|
||||
|
||||
ssi_hex = os_zalloc(2 * ssi_len + 1);
|
||||
if (!ssi_hex)
|
||||
return;
|
||||
if (ssi)
|
||||
wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_RECEIVE
|
||||
"id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
|
||||
id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
|
||||
os_free(ssi_hex);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_nan_usd_init(struct hostapd_data *hapd)
|
||||
{
|
||||
struct nan_callbacks cb;
|
||||
|
||||
os_memset(&cb, 0, sizeof(cb));
|
||||
cb.ctx = hapd;
|
||||
cb.tx = hostapd_nan_de_tx;
|
||||
cb.listen = hostapd_nan_de_listen;
|
||||
cb.discovery_result = hostapd_nan_de_discovery_result;
|
||||
cb.replied = hostapd_nan_de_replied;
|
||||
cb.publish_terminated = hostapd_nan_de_publish_terminated;
|
||||
cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
|
||||
cb.receive = hostapd_nan_de_receive;
|
||||
|
||||
hapd->nan_de = nan_de_init(hapd->own_addr, true, &cb);
|
||||
if (!hapd->nan_de)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
nan_de_deinit(hapd->nan_de);
|
||||
hapd->nan_de = NULL;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
|
||||
unsigned int freq, const u8 *buf, size_t len)
|
||||
{
|
||||
if (!hapd->nan_de)
|
||||
return;
|
||||
nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
|
||||
}
|
||||
|
||||
|
||||
void hostapd_nan_usd_flush(struct hostapd_data *hapd)
|
||||
{
|
||||
if (!hapd->nan_de)
|
||||
return;
|
||||
nan_de_flush(hapd->nan_de);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
|
||||
enum nan_service_protocol_type srv_proto_type,
|
||||
const struct wpabuf *ssi,
|
||||
struct nan_publish_params *params)
|
||||
{
|
||||
int publish_id;
|
||||
struct wpabuf *elems = NULL;
|
||||
|
||||
if (!hapd->nan_de)
|
||||
return -1;
|
||||
|
||||
publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
|
||||
ssi, elems, params);
|
||||
wpabuf_free(elems);
|
||||
return publish_id;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id)
|
||||
{
|
||||
if (!hapd->nan_de)
|
||||
return;
|
||||
nan_de_cancel_publish(hapd->nan_de, publish_id);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
|
||||
const struct wpabuf *ssi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!hapd->nan_de)
|
||||
return -1;
|
||||
ret = nan_de_update_publish(hapd->nan_de, publish_id, ssi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
|
||||
const char *service_name,
|
||||
enum nan_service_protocol_type srv_proto_type,
|
||||
const struct wpabuf *ssi,
|
||||
struct nan_subscribe_params *params)
|
||||
{
|
||||
int subscribe_id;
|
||||
struct wpabuf *elems = NULL;
|
||||
|
||||
if (!hapd->nan_de)
|
||||
return -1;
|
||||
|
||||
subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
|
||||
srv_proto_type, ssi, elems, params);
|
||||
wpabuf_free(elems);
|
||||
return subscribe_id;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
|
||||
int subscribe_id)
|
||||
{
|
||||
if (!hapd->nan_de)
|
||||
return;
|
||||
nan_de_cancel_subscribe(hapd->nan_de, subscribe_id);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
|
||||
const struct wpabuf *ssi,
|
||||
const struct wpabuf *elems,
|
||||
const u8 *peer_addr, u8 req_instance_id)
|
||||
{
|
||||
if (!hapd->nan_de)
|
||||
return -1;
|
||||
return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
|
||||
req_instance_id);
|
||||
}
|
||||
46
src/ap/nan_usd_ap.h
Normal file
46
src/ap/nan_usd_ap.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* NAN unsynchronized service discovery (USD)
|
||||
* Copyright (c) 2024, Qualcomm Innovation Center, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef NAN_USD_AP_H
|
||||
#define NAN_USD_AP_H
|
||||
|
||||
struct nan_subscribe_params;
|
||||
struct nan_publish_params;
|
||||
enum nan_service_protocol_type;
|
||||
|
||||
int hostapd_nan_usd_init(struct hostapd_data *hapd);
|
||||
void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
|
||||
void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
|
||||
unsigned int freq, const u8 *buf, size_t len);
|
||||
void hostapd_nan_usd_flush(struct hostapd_data *hapd);
|
||||
int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
|
||||
enum nan_service_protocol_type srv_proto_type,
|
||||
const struct wpabuf *ssi,
|
||||
struct nan_publish_params *params);
|
||||
void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id);
|
||||
int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
|
||||
const struct wpabuf *ssi);
|
||||
int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
|
||||
const char *service_name,
|
||||
enum nan_service_protocol_type srv_proto_type,
|
||||
const struct wpabuf *ssi,
|
||||
struct nan_subscribe_params *params);
|
||||
void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
|
||||
int subscribe_id);
|
||||
int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
|
||||
const struct wpabuf *ssi,
|
||||
const struct wpabuf *elems,
|
||||
const u8 *peer_addr, u8 req_instance_id);
|
||||
void hostapd_nan_usd_remain_on_channel_cb(struct hostapd_data *hapd,
|
||||
unsigned int freq,
|
||||
unsigned int duration);
|
||||
void hostapd_nan_usd_cancel_remain_on_channel_cb(struct hostapd_data *hapd,
|
||||
unsigned int freq);
|
||||
void hostapd_nan_usd_tx_wait_expire(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* NAN_USD_AP_H */
|
||||
189
src/ap/ndisc_snoop.c
Normal file
189
src/ap/ndisc_snoop.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Neighbor Discovery 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 <netinet/ip6.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "l2_packet/l2_packet.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "list.h"
|
||||
#include "x_snoop.h"
|
||||
#include "ndisc_snoop.h"
|
||||
|
||||
struct ip6addr {
|
||||
struct in6_addr addr;
|
||||
struct dl_list list;
|
||||
};
|
||||
|
||||
struct icmpv6_ndmsg {
|
||||
struct ip6_hdr ipv6h;
|
||||
struct icmp6_hdr icmp6h;
|
||||
struct in6_addr target_addr;
|
||||
u8 opt_type;
|
||||
u8 len;
|
||||
u8 opt_lladdr[0];
|
||||
} STRUCT_PACKED;
|
||||
|
||||
#define ROUTER_ADVERTISEMENT 134
|
||||
#define NEIGHBOR_SOLICITATION 135
|
||||
#define NEIGHBOR_ADVERTISEMENT 136
|
||||
#define SOURCE_LL_ADDR 1
|
||||
|
||||
static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
|
||||
{
|
||||
struct ip6addr *ip6addr;
|
||||
|
||||
ip6addr = os_zalloc(sizeof(*ip6addr));
|
||||
if (!ip6addr)
|
||||
return -1;
|
||||
|
||||
os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
|
||||
|
||||
dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
struct ip6addr *ip6addr, *prev;
|
||||
|
||||
dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
|
||||
list) {
|
||||
hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
|
||||
dl_list_del(&ip6addr->list);
|
||||
os_free(ip6addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
|
||||
{
|
||||
struct ip6addr *ip6addr;
|
||||
|
||||
dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
|
||||
if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
|
||||
ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
|
||||
ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
|
||||
ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
struct icmpv6_ndmsg *msg;
|
||||
struct in6_addr saddr;
|
||||
struct sta_info *sta;
|
||||
int res;
|
||||
char addrtxt[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
|
||||
return;
|
||||
msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
|
||||
switch (msg->icmp6h.icmp6_type) {
|
||||
case NEIGHBOR_SOLICITATION:
|
||||
if (len < ETH_HLEN + sizeof(*msg))
|
||||
return;
|
||||
if (msg->opt_type != SOURCE_LL_ADDR)
|
||||
return;
|
||||
|
||||
/*
|
||||
* IPv6 header may not be 32-bit aligned in the buffer, so use
|
||||
* a local copy to avoid unaligned reads.
|
||||
*/
|
||||
os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
|
||||
if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
|
||||
saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
|
||||
if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
|
||||
return;
|
||||
sta = ap_get_sta(hapd, msg->opt_lladdr);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
if (sta_has_ip6addr(sta, &saddr))
|
||||
return;
|
||||
|
||||
if (inet_ntop(AF_INET6, &saddr, addrtxt,
|
||||
sizeof(addrtxt)) == NULL)
|
||||
addrtxt[0] = '\0';
|
||||
wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
|
||||
MACSTR, addrtxt, MAC2STR(sta->addr));
|
||||
hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
|
||||
res = hostapd_drv_br_add_ip_neigh(hapd, 6,
|
||||
(u8 *) &saddr,
|
||||
128, sta->addr);
|
||||
if (res) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"ndisc_snoop: Adding ip neigh failed: %d",
|
||||
res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sta_ip6addr_add(sta, &saddr))
|
||||
return;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_HS20
|
||||
case ROUTER_ADVERTISEMENT:
|
||||
if (hapd->conf->disable_dgaf)
|
||||
ucast_to_stas(hapd, buf, len);
|
||||
break;
|
||||
#endif /* CONFIG_HS20 */
|
||||
case NEIGHBOR_ADVERTISEMENT:
|
||||
if (hapd->conf->na_mcast_to_ucast)
|
||||
ucast_to_stas(hapd, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ndisc_snoop_init(struct hostapd_data *hapd)
|
||||
{
|
||||
hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
|
||||
L2_PACKET_FILTER_NDISC);
|
||||
if (hapd->sock_ndisc == NULL) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ndisc_snoop_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
l2_packet_deinit(hapd->sock_ndisc);
|
||||
hapd->sock_ndisc = NULL;
|
||||
}
|
||||
36
src/ap/ndisc_snoop.h
Normal file
36
src/ap/ndisc_snoop.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Neighbor Discovery 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 NDISC_SNOOP_H
|
||||
#define NDISC_SNOOP_H
|
||||
|
||||
#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
|
||||
|
||||
int ndisc_snoop_init(struct hostapd_data *hapd);
|
||||
void ndisc_snoop_deinit(struct hostapd_data *hapd);
|
||||
void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
|
||||
|
||||
static inline int ndisc_snoop_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void sta_ip6addr_del(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
|
||||
|
||||
#endif /* NDISC_SNOOP_H */
|
||||
366
src/ap/neighbor_db.c
Normal file
366
src/ap/neighbor_db.c
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* hostapd / Neighboring APs DB
|
||||
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
|
||||
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* 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/crc32.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "neighbor_db.h"
|
||||
|
||||
|
||||
struct hostapd_neighbor_entry *
|
||||
hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
|
||||
const struct wpa_ssid_value *ssid)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
|
||||
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
|
||||
list) {
|
||||
if (ether_addr_equal(bssid, nr->bssid) &&
|
||||
(!ssid ||
|
||||
(ssid->ssid_len == nr->ssid.ssid_len &&
|
||||
os_memcmp(ssid->ssid, nr->ssid.ssid,
|
||||
ssid->ssid_len) == 0)))
|
||||
return nr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
char *pos, *end;
|
||||
|
||||
pos = buf;
|
||||
end = buf + buflen;
|
||||
|
||||
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
|
||||
list) {
|
||||
int ret;
|
||||
char nrie[2 * 255 + 1];
|
||||
char lci[2 * 255 + 1];
|
||||
char civic[2 * 255 + 1];
|
||||
char ssid[SSID_MAX_LEN * 2 + 1];
|
||||
|
||||
ssid[0] = '\0';
|
||||
wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
|
||||
nr->ssid.ssid_len);
|
||||
|
||||
nrie[0] = '\0';
|
||||
if (nr->nr)
|
||||
wpa_snprintf_hex(nrie, sizeof(nrie),
|
||||
wpabuf_head(nr->nr),
|
||||
wpabuf_len(nr->nr));
|
||||
|
||||
lci[0] = '\0';
|
||||
if (nr->lci)
|
||||
wpa_snprintf_hex(lci, sizeof(lci),
|
||||
wpabuf_head(nr->lci),
|
||||
wpabuf_len(nr->lci));
|
||||
|
||||
civic[0] = '\0';
|
||||
if (nr->civic)
|
||||
wpa_snprintf_hex(civic, sizeof(civic),
|
||||
wpabuf_head(nr->civic),
|
||||
wpabuf_len(nr->civic));
|
||||
|
||||
ret = os_snprintf(pos, end - pos, MACSTR
|
||||
" ssid=%s%s%s%s%s%s%s%s\n",
|
||||
MAC2STR(nr->bssid), ssid,
|
||||
nr->nr ? " nr=" : "", nrie,
|
||||
nr->lci ? " lci=" : "", lci,
|
||||
nr->civic ? " civic=" : "", civic,
|
||||
nr->stationary ? " stat" : "");
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
break;
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
|
||||
{
|
||||
wpabuf_free(nr->nr);
|
||||
nr->nr = NULL;
|
||||
wpabuf_free(nr->lci);
|
||||
nr->lci = NULL;
|
||||
wpabuf_free(nr->civic);
|
||||
nr->civic = NULL;
|
||||
os_memset(nr->bssid, 0, sizeof(nr->bssid));
|
||||
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
|
||||
os_memset(&nr->lci_date, 0, sizeof(nr->lci_date));
|
||||
nr->stationary = 0;
|
||||
nr->short_ssid = 0;
|
||||
nr->bss_parameters = 0;
|
||||
}
|
||||
|
||||
|
||||
static struct hostapd_neighbor_entry *
|
||||
hostapd_neighbor_add(struct hostapd_data *hapd)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
|
||||
nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
|
||||
if (!nr)
|
||||
return NULL;
|
||||
|
||||
dl_list_add(&hapd->nr_db, &nr->list);
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
|
||||
const struct wpa_ssid_value *ssid,
|
||||
const struct wpabuf *nr, const struct wpabuf *lci,
|
||||
const struct wpabuf *civic, int stationary,
|
||||
u8 bss_parameters)
|
||||
{
|
||||
struct hostapd_neighbor_entry *entry;
|
||||
|
||||
entry = hostapd_neighbor_get(hapd, bssid, ssid);
|
||||
if (!entry)
|
||||
entry = hostapd_neighbor_add(hapd);
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
hostapd_neighbor_clear_entry(entry);
|
||||
|
||||
os_memcpy(entry->bssid, bssid, ETH_ALEN);
|
||||
os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
|
||||
entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
|
||||
|
||||
entry->nr = wpabuf_dup(nr);
|
||||
if (!entry->nr)
|
||||
goto fail;
|
||||
|
||||
if (lci && wpabuf_len(lci)) {
|
||||
entry->lci = wpabuf_dup(lci);
|
||||
if (!entry->lci || os_get_time(&entry->lci_date))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (civic && wpabuf_len(civic)) {
|
||||
entry->civic = wpabuf_dup(civic);
|
||||
if (!entry->civic)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
entry->stationary = stationary;
|
||||
entry->bss_parameters = bss_parameters;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
hostapd_neighbor_remove(hapd, bssid, ssid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_neighbor_free(struct hostapd_neighbor_entry *nr)
|
||||
{
|
||||
hostapd_neighbor_clear_entry(nr);
|
||||
dl_list_del(&nr->list);
|
||||
os_free(nr);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
|
||||
const struct wpa_ssid_value *ssid)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
|
||||
nr = hostapd_neighbor_get(hapd, bssid, ssid);
|
||||
if (!nr)
|
||||
return -1;
|
||||
|
||||
hostapd_neighbor_free(nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_free_neighbor_db(struct hostapd_data *hapd)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr, *prev;
|
||||
|
||||
dl_list_for_each_safe(nr, prev, &hapd->nr_db,
|
||||
struct hostapd_neighbor_entry, list) {
|
||||
hostapd_neighbor_free(nr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef NEED_AP_MLME
|
||||
static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
|
||||
int ht, int vht, int he)
|
||||
{
|
||||
enum oper_chan_width oper_chwidth;
|
||||
|
||||
oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
|
||||
|
||||
if (!ht && !vht && !he)
|
||||
return NR_CHAN_WIDTH_20;
|
||||
if (!hapd->iconf->secondary_channel)
|
||||
return NR_CHAN_WIDTH_20;
|
||||
if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
|
||||
return NR_CHAN_WIDTH_40;
|
||||
if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
|
||||
return NR_CHAN_WIDTH_80;
|
||||
if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
|
||||
return NR_CHAN_WIDTH_160;
|
||||
if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
|
||||
return NR_CHAN_WIDTH_80P80;
|
||||
return NR_CHAN_WIDTH_20;
|
||||
}
|
||||
#endif /* NEED_AP_MLME */
|
||||
|
||||
|
||||
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
|
||||
{
|
||||
#ifdef NEED_AP_MLME
|
||||
u16 capab = hostapd_own_capab_info(hapd);
|
||||
int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
|
||||
int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
|
||||
int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
|
||||
bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
|
||||
struct wpa_ssid_value ssid;
|
||||
u8 channel, op_class;
|
||||
u8 center_freq1_idx = 0, center_freq2_idx = 0;
|
||||
enum nr_chan_width width;
|
||||
u32 bssid_info;
|
||||
struct wpabuf *nr;
|
||||
|
||||
if (!(hapd->conf->radio_measurements[0] &
|
||||
WLAN_RRM_CAPS_NEIGHBOR_REPORT))
|
||||
return;
|
||||
|
||||
bssid_info = 3; /* AP is reachable */
|
||||
bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
|
||||
bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
|
||||
|
||||
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
|
||||
bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
|
||||
|
||||
bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
|
||||
|
||||
if (hapd->conf->wmm_enabled) {
|
||||
bssid_info |= NEI_REP_BSSID_INFO_QOS;
|
||||
|
||||
if (hapd->conf->wmm_uapsd &&
|
||||
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
|
||||
bssid_info |= NEI_REP_BSSID_INFO_APSD;
|
||||
}
|
||||
|
||||
if (ht) {
|
||||
bssid_info |= NEI_REP_BSSID_INFO_HT |
|
||||
NEI_REP_BSSID_INFO_DELAYED_BA;
|
||||
|
||||
/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
|
||||
if (vht)
|
||||
bssid_info |= NEI_REP_BSSID_INFO_VHT;
|
||||
}
|
||||
|
||||
if (he)
|
||||
bssid_info |= NEI_REP_BSSID_INFO_HE;
|
||||
if (eht)
|
||||
bssid_info |= NEI_REP_BSSID_INFO_EHT;
|
||||
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
|
||||
|
||||
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
|
||||
hapd->iconf->secondary_channel,
|
||||
hostapd_get_oper_chwidth(hapd->iconf),
|
||||
&op_class, &channel) ==
|
||||
NUM_HOSTAPD_MODES)
|
||||
return;
|
||||
width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
|
||||
if (vht) {
|
||||
center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
|
||||
hapd->iconf);
|
||||
if (width == NR_CHAN_WIDTH_80P80)
|
||||
center_freq2_idx =
|
||||
hostapd_get_oper_centr_freq_seg1_idx(
|
||||
hapd->iconf);
|
||||
} else if (ht) {
|
||||
ieee80211_freq_to_chan(hapd->iface->freq +
|
||||
10 * hapd->iconf->secondary_channel,
|
||||
¢er_freq1_idx);
|
||||
}
|
||||
|
||||
ssid.ssid_len = hapd->conf->ssid.ssid_len;
|
||||
os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
|
||||
|
||||
/*
|
||||
* Neighbor Report element size = BSSID + BSSID info + op_class + chan +
|
||||
* phy type + wide bandwidth channel subelement.
|
||||
*/
|
||||
nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
|
||||
if (!nr)
|
||||
return;
|
||||
|
||||
wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
|
||||
wpabuf_put_le32(nr, bssid_info);
|
||||
wpabuf_put_u8(nr, op_class);
|
||||
wpabuf_put_u8(nr, channel);
|
||||
wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
|
||||
|
||||
/*
|
||||
* Wide Bandwidth Channel subelement may be needed to allow the
|
||||
* receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
|
||||
* Figure 9-301.
|
||||
*/
|
||||
wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
|
||||
wpabuf_put_u8(nr, 3);
|
||||
wpabuf_put_u8(nr, width);
|
||||
wpabuf_put_u8(nr, center_freq1_idx);
|
||||
wpabuf_put_u8(nr, center_freq2_idx);
|
||||
|
||||
hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
|
||||
hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
|
||||
|
||||
wpabuf_free(nr);
|
||||
#endif /* NEED_AP_MLME */
|
||||
}
|
||||
|
||||
|
||||
static struct hostapd_neighbor_entry *
|
||||
hostapd_neighbor_get_diff_short_ssid(struct hostapd_data *hapd, const u8 *bssid)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
|
||||
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
|
||||
list) {
|
||||
if (ether_addr_equal(bssid, nr->bssid) &&
|
||||
nr->short_ssid != hapd->conf->ssid.short_ssid)
|
||||
return nr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
|
||||
nr = hostapd_neighbor_get_diff_short_ssid(hapd, hapd->own_addr);
|
||||
if (!nr)
|
||||
return -1;
|
||||
|
||||
/* Clear old entry due to SSID change */
|
||||
hostapd_neighbor_free(nr);
|
||||
|
||||
hostapd_neighbor_set_own_report(hapd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
src/ap/neighbor_db.h
Normal file
28
src/ap/neighbor_db.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* hostapd / Neighboring APs DB
|
||||
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
|
||||
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef NEIGHBOR_DB_H
|
||||
#define NEIGHBOR_DB_H
|
||||
|
||||
struct hostapd_neighbor_entry *
|
||||
hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
|
||||
const struct wpa_ssid_value *ssid);
|
||||
int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen);
|
||||
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
|
||||
const struct wpa_ssid_value *ssid,
|
||||
const struct wpabuf *nr, const struct wpabuf *lci,
|
||||
const struct wpabuf *civic, int stationary,
|
||||
u8 bss_parameters);
|
||||
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
|
||||
int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd);
|
||||
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
|
||||
const struct wpa_ssid_value *ssid);
|
||||
void hostapd_free_neighbor_db(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* NEIGHBOR_DB_H */
|
||||
113
src/ap/p2p_hostapd.c
Normal file
113
src/ap/p2p_hostapd.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* hostapd / P2P integration
|
||||
* Copyright (c) 2009-2010, Atheros Communications
|
||||
*
|
||||
* 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 "p2p/p2p.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "sta_info.h"
|
||||
#include "p2p_hostapd.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
|
||||
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
if (sta->p2p_ie == NULL)
|
||||
return 0;
|
||||
|
||||
return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
|
||||
"duration=%d", count, start, duration);
|
||||
|
||||
if (count == 0) {
|
||||
hapd->noa_enabled = 0;
|
||||
hapd->noa_start = 0;
|
||||
hapd->noa_duration = 0;
|
||||
}
|
||||
|
||||
if (count != 255) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
|
||||
"NoA parameters");
|
||||
return hostapd_driver_set_noa(hapd, count, start, duration);
|
||||
}
|
||||
|
||||
hapd->noa_enabled = 1;
|
||||
hapd->noa_start = start;
|
||||
hapd->noa_duration = duration;
|
||||
|
||||
if (hapd->num_sta_no_p2p == 0) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
|
||||
"periodic NoA parameters");
|
||||
return hostapd_driver_set_noa(hapd, count, start, duration);
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
|
||||
"periodic NoA");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
|
||||
|
||||
if (hapd->noa_enabled) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
|
||||
hostapd_driver_set_noa(hapd, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
|
||||
|
||||
if (hapd->noa_enabled) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
|
||||
hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
|
||||
hapd->noa_duration);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 bitmap;
|
||||
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*eid++ = 4 + 3 + 1;
|
||||
WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
|
||||
eid += 4;
|
||||
|
||||
*eid++ = P2P_ATTR_MANAGEABILITY;
|
||||
WPA_PUT_LE16(eid, 1);
|
||||
eid += 2;
|
||||
bitmap = P2P_MAN_DEVICE_MANAGEMENT;
|
||||
if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
|
||||
bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
|
||||
bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
|
||||
*eid++ = bitmap;
|
||||
|
||||
return eid;
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
35
src/ap/p2p_hostapd.h
Normal file
35
src/ap/p2p_hostapd.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* hostapd / P2P integration
|
||||
* Copyright (c) 2009-2010, Atheros Communications
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef P2P_HOSTAPD_H
|
||||
#define P2P_HOSTAPD_H
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
|
||||
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration);
|
||||
void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
|
||||
void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
|
||||
|
||||
|
||||
#else /* CONFIG_P2P */
|
||||
|
||||
static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
|
||||
|
||||
#endif /* P2P_HOSTAPD_H */
|
||||
751
src/ap/pmksa_cache_auth.c
Normal file
751
src/ap/pmksa_cache_auth.c
Normal file
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
* hostapd - PMKSA cache for IEEE 802.11i RSN
|
||||
* Copyright (c) 2004-2008, 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_das.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_config.h"
|
||||
#include "pmksa_cache_auth.h"
|
||||
|
||||
|
||||
static const int pmksa_cache_max_entries = 1024;
|
||||
static const int dot11RSNAConfigPMKLifetime = 43200;
|
||||
|
||||
struct rsn_pmksa_cache {
|
||||
#define PMKID_HASH_SIZE 128
|
||||
#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
|
||||
struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
|
||||
struct rsn_pmksa_cache_entry *pmksa;
|
||||
int pmksa_count;
|
||||
|
||||
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
|
||||
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
|
||||
|
||||
|
||||
static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
|
||||
{
|
||||
os_free(entry->vlan_desc);
|
||||
os_free(entry->identity);
|
||||
os_free(entry->dpp_pkhash);
|
||||
wpabuf_free(entry->cui);
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
radius_free_class(&entry->radius_class);
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
bin_clear_free(entry, sizeof(*entry));
|
||||
}
|
||||
|
||||
|
||||
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *pos, *prev;
|
||||
unsigned int hash;
|
||||
|
||||
pmksa->pmksa_count--;
|
||||
|
||||
if (pmksa->free_cb)
|
||||
pmksa->free_cb(entry, pmksa->ctx);
|
||||
|
||||
/* unlink from hash list */
|
||||
hash = PMKID_HASH(entry->pmkid);
|
||||
pos = pmksa->pmkid[hash];
|
||||
prev = NULL;
|
||||
while (pos) {
|
||||
if (pos == entry) {
|
||||
if (prev != NULL)
|
||||
prev->hnext = entry->hnext;
|
||||
else
|
||||
pmksa->pmkid[hash] = entry->hnext;
|
||||
break;
|
||||
}
|
||||
prev = pos;
|
||||
pos = pos->hnext;
|
||||
}
|
||||
|
||||
/* unlink from entry list */
|
||||
pos = pmksa->pmksa;
|
||||
prev = NULL;
|
||||
while (pos) {
|
||||
if (pos == entry) {
|
||||
if (prev != NULL)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
pmksa->pmksa = entry->next;
|
||||
break;
|
||||
}
|
||||
prev = pos;
|
||||
pos = pos->next;
|
||||
}
|
||||
|
||||
_pmksa_cache_free_entry(entry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_flush - Flush all PMKSA cache entries
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
*/
|
||||
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
|
||||
{
|
||||
while (pmksa->pmksa) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
|
||||
MACSTR, MAC2STR(pmksa->pmksa->spa));
|
||||
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct rsn_pmksa_cache *pmksa = eloop_ctx;
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
|
||||
MACSTR, MAC2STR(pmksa->pmksa->spa));
|
||||
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
|
||||
}
|
||||
|
||||
pmksa_cache_set_expiration(pmksa);
|
||||
}
|
||||
|
||||
|
||||
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
|
||||
{
|
||||
int sec;
|
||||
struct os_reltime now;
|
||||
|
||||
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
|
||||
if (pmksa->pmksa == NULL)
|
||||
return;
|
||||
os_get_reltime(&now);
|
||||
sec = pmksa->pmksa->expiration - now.sec;
|
||||
if (sec < 0)
|
||||
sec = 0;
|
||||
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
|
||||
struct eapol_state_machine *eapol)
|
||||
{
|
||||
struct vlan_description *vlan_desc;
|
||||
|
||||
if (eapol == NULL)
|
||||
return;
|
||||
|
||||
if (eapol->identity) {
|
||||
entry->identity = os_malloc(eapol->identity_len);
|
||||
if (entry->identity) {
|
||||
entry->identity_len = eapol->identity_len;
|
||||
os_memcpy(entry->identity, eapol->identity,
|
||||
eapol->identity_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (eapol->radius_cui)
|
||||
entry->cui = wpabuf_dup(eapol->radius_cui);
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
radius_copy_class(&entry->radius_class, &eapol->radius_class);
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
entry->eap_type_authsrv = eapol->eap_type_authsrv;
|
||||
|
||||
vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
|
||||
if (vlan_desc && vlan_desc->notempty) {
|
||||
entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
|
||||
if (entry->vlan_desc)
|
||||
*entry->vlan_desc = *vlan_desc;
|
||||
} else {
|
||||
entry->vlan_desc = NULL;
|
||||
}
|
||||
|
||||
entry->acct_multi_session_id = eapol->acct_multi_session_id;
|
||||
}
|
||||
|
||||
|
||||
void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
|
||||
struct rsn_pmksa_cache_entry *entry,
|
||||
struct eapol_state_machine *eapol)
|
||||
{
|
||||
if (entry == NULL || eapol == NULL)
|
||||
return;
|
||||
|
||||
if (entry->identity) {
|
||||
os_free(eapol->identity);
|
||||
eapol->identity = os_malloc(entry->identity_len);
|
||||
if (eapol->identity) {
|
||||
eapol->identity_len = entry->identity_len;
|
||||
os_memcpy(eapol->identity, entry->identity,
|
||||
entry->identity_len);
|
||||
}
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
|
||||
eapol->identity, eapol->identity_len);
|
||||
}
|
||||
|
||||
if (entry->cui) {
|
||||
wpabuf_free(eapol->radius_cui);
|
||||
eapol->radius_cui = wpabuf_dup(entry->cui);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
radius_free_class(&eapol->radius_class);
|
||||
radius_copy_class(&eapol->radius_class, &entry->radius_class);
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
if (eapol->radius_class.attr) {
|
||||
wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
|
||||
"PMKSA", (unsigned long) eapol->radius_class.count);
|
||||
}
|
||||
|
||||
eapol->eap_type_authsrv = entry->eap_type_authsrv;
|
||||
#ifndef CONFIG_NO_VLAN
|
||||
ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
|
||||
#endif /* CONFIG_NO_VLAN */
|
||||
|
||||
eapol->acct_multi_session_id = entry->acct_multi_session_id;
|
||||
}
|
||||
|
||||
|
||||
static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *pos, *prev;
|
||||
int hash;
|
||||
|
||||
/* Add the new entry; order by expiration time */
|
||||
pos = pmksa->pmksa;
|
||||
prev = NULL;
|
||||
while (pos) {
|
||||
if (pos->expiration > entry->expiration)
|
||||
break;
|
||||
prev = pos;
|
||||
pos = pos->next;
|
||||
}
|
||||
if (prev == NULL) {
|
||||
entry->next = pmksa->pmksa;
|
||||
pmksa->pmksa = entry;
|
||||
} else {
|
||||
entry->next = prev->next;
|
||||
prev->next = entry;
|
||||
}
|
||||
|
||||
hash = PMKID_HASH(entry->pmkid);
|
||||
entry->hnext = pmksa->pmkid[hash];
|
||||
pmksa->pmkid[hash] = entry;
|
||||
|
||||
pmksa->pmksa_count++;
|
||||
if (prev == NULL)
|
||||
pmksa_cache_set_expiration(pmksa);
|
||||
wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
|
||||
MAC2STR(entry->spa));
|
||||
wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_add - Add a PMKSA cache entry
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
* @pmk: The new pairwise master key
|
||||
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
|
||||
* @pmkid: Calculated PMKID
|
||||
* @kck: Key confirmation key or %NULL if not yet derived
|
||||
* @kck_len: KCK length in bytes
|
||||
* @aa: Authenticator address
|
||||
* @spa: Supplicant address
|
||||
* @session_timeout: Session timeout
|
||||
* @eapol: Pointer to EAPOL state machine data
|
||||
* @akmp: WPA_KEY_MGMT_* used in key derivation
|
||||
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
|
||||
*
|
||||
* This function create a PMKSA entry for a new PMK and adds it to the PMKSA
|
||||
* cache. If an old entry is already in the cache for the same Supplicant,
|
||||
* this entry will be replaced with the new entry. PMKID will be calculated
|
||||
* based on the PMK.
|
||||
*/
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
|
||||
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
|
||||
const u8 *kck, size_t kck_len,
|
||||
const u8 *aa, const u8 *spa, int session_timeout,
|
||||
struct eapol_state_machine *eapol, int akmp)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
|
||||
entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
|
||||
aa, spa, session_timeout, eapol,
|
||||
akmp);
|
||||
|
||||
if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
|
||||
return NULL;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_create_entry - Create a PMKSA cache entry
|
||||
* @pmk: The new pairwise master key
|
||||
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
|
||||
* @pmkid: Calculated PMKID
|
||||
* @kck: Key confirmation key or %NULL if not yet derived
|
||||
* @kck_len: KCK length in bytes
|
||||
* @aa: Authenticator address
|
||||
* @spa: Supplicant address
|
||||
* @session_timeout: Session timeout
|
||||
* @eapol: Pointer to EAPOL state machine data
|
||||
* @akmp: WPA_KEY_MGMT_* used in key derivation
|
||||
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
|
||||
*
|
||||
* This function creates a PMKSA entry.
|
||||
*/
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
|
||||
const u8 *kck, size_t kck_len, const u8 *aa,
|
||||
const u8 *spa, int session_timeout,
|
||||
struct eapol_state_machine *eapol, int akmp)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct os_reltime now;
|
||||
|
||||
if (pmk_len > PMK_LEN_MAX)
|
||||
return NULL;
|
||||
|
||||
if (wpa_key_mgmt_suite_b(akmp) && !kck)
|
||||
return NULL;
|
||||
|
||||
entry = os_zalloc(sizeof(*entry));
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
os_memcpy(entry->pmk, pmk, pmk_len);
|
||||
entry->pmk_len = pmk_len;
|
||||
if (kck && kck_len && kck_len < WPA_KCK_MAX_LEN) {
|
||||
os_memcpy(entry->kck, kck, kck_len);
|
||||
entry->kck_len = kck_len;
|
||||
}
|
||||
if (pmkid)
|
||||
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
|
||||
else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
|
||||
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
|
||||
else if (wpa_key_mgmt_suite_b(akmp))
|
||||
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
|
||||
else
|
||||
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
|
||||
os_get_reltime(&now);
|
||||
entry->expiration = now.sec;
|
||||
if (session_timeout > 0)
|
||||
entry->expiration += session_timeout;
|
||||
else
|
||||
entry->expiration += dot11RSNAConfigPMKLifetime;
|
||||
entry->akmp = akmp;
|
||||
os_memcpy(entry->spa, spa, ETH_ALEN);
|
||||
pmksa_cache_from_eapol_data(entry, eapol);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_add_entry - Add a PMKSA cache entry
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
* @entry: Pointer to PMKSA cache entry
|
||||
*
|
||||
* This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
|
||||
* already in the cache for the same Supplicant, this entry will be replaced
|
||||
* with the new entry. PMKID will be calculated based on the PMK.
|
||||
*/
|
||||
int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *pos;
|
||||
|
||||
if (entry == NULL)
|
||||
return -1;
|
||||
|
||||
/* Replace an old entry for the same STA (if found) with the new entry
|
||||
*/
|
||||
pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
|
||||
if (pos)
|
||||
pmksa_cache_free_entry(pmksa, pos);
|
||||
|
||||
if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
|
||||
/* Remove the oldest entry to make room for the new entry */
|
||||
wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
|
||||
"entry (for " MACSTR ") to make room for new one",
|
||||
MAC2STR(pmksa->pmksa->spa));
|
||||
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
|
||||
}
|
||||
|
||||
pmksa_cache_link_entry(pmksa, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
|
||||
const struct rsn_pmksa_cache_entry *old_entry,
|
||||
const u8 *aa, const u8 *pmkid)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
|
||||
entry = os_zalloc(sizeof(*entry));
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
|
||||
os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
|
||||
entry->pmk_len = old_entry->pmk_len;
|
||||
entry->expiration = old_entry->expiration;
|
||||
entry->akmp = old_entry->akmp;
|
||||
os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
|
||||
entry->opportunistic = 1;
|
||||
if (old_entry->identity) {
|
||||
entry->identity = os_malloc(old_entry->identity_len);
|
||||
if (entry->identity) {
|
||||
entry->identity_len = old_entry->identity_len;
|
||||
os_memcpy(entry->identity, old_entry->identity,
|
||||
old_entry->identity_len);
|
||||
}
|
||||
}
|
||||
if (old_entry->cui)
|
||||
entry->cui = wpabuf_dup(old_entry->cui);
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
radius_copy_class(&entry->radius_class, &old_entry->radius_class);
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
entry->eap_type_authsrv = old_entry->eap_type_authsrv;
|
||||
if (old_entry->vlan_desc) {
|
||||
entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
|
||||
if (entry->vlan_desc)
|
||||
*entry->vlan_desc = *old_entry->vlan_desc;
|
||||
} else {
|
||||
entry->vlan_desc = NULL;
|
||||
}
|
||||
entry->opportunistic = 1;
|
||||
|
||||
pmksa_cache_link_entry(pmksa, entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_deinit - Free all entries in PMKSA cache
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
*/
|
||||
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry, *prev;
|
||||
int i;
|
||||
|
||||
if (pmksa == NULL)
|
||||
return;
|
||||
|
||||
entry = pmksa->pmksa;
|
||||
while (entry) {
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
_pmksa_cache_free_entry(prev);
|
||||
}
|
||||
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
|
||||
pmksa->pmksa_count = 0;
|
||||
pmksa->pmksa = NULL;
|
||||
for (i = 0; i < PMKID_HASH_SIZE; i++)
|
||||
pmksa->pmkid[i] = NULL;
|
||||
os_free(pmksa);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_get - Fetch a PMKSA cache entry
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
* @spa: Supplicant address or %NULL to match any
|
||||
* @pmkid: PMKID or %NULL to match any
|
||||
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
|
||||
*/
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
|
||||
const u8 *spa, const u8 *pmkid)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
|
||||
if (pmkid) {
|
||||
for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
|
||||
entry = entry->hnext) {
|
||||
if ((spa == NULL ||
|
||||
ether_addr_equal(entry->spa, spa)) &&
|
||||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
|
||||
return entry;
|
||||
}
|
||||
} else {
|
||||
for (entry = pmksa->pmksa; entry; entry = entry->next) {
|
||||
if (spa == NULL ||
|
||||
ether_addr_equal(entry->spa, spa))
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
* @aa: Authenticator address
|
||||
* @spa: Supplicant address
|
||||
* @pmkid: PMKID
|
||||
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
|
||||
*
|
||||
* Use opportunistic key caching (OKC) to find a PMK for a supplicant.
|
||||
*/
|
||||
struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
|
||||
struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
|
||||
const u8 *pmkid)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
u8 new_pmkid[PMKID_LEN];
|
||||
|
||||
for (entry = pmksa->pmksa; entry; entry = entry->next) {
|
||||
if (!ether_addr_equal(entry->spa, spa))
|
||||
continue;
|
||||
if (wpa_key_mgmt_sae(entry->akmp) ||
|
||||
wpa_key_mgmt_fils(entry->akmp)) {
|
||||
if (os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
|
||||
return entry;
|
||||
continue;
|
||||
}
|
||||
if (entry->akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 &&
|
||||
entry->kck_len > 0)
|
||||
rsn_pmkid_suite_b_192(entry->kck, entry->kck_len,
|
||||
aa, spa, new_pmkid);
|
||||
else if (wpa_key_mgmt_suite_b(entry->akmp) &&
|
||||
entry->kck_len > 0)
|
||||
rsn_pmkid_suite_b(entry->kck, entry->kck_len, aa, spa,
|
||||
new_pmkid);
|
||||
else
|
||||
rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa,
|
||||
new_pmkid, entry->akmp);
|
||||
if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_init - Initialize PMKSA cache
|
||||
* @free_cb: Callback function to be called when a PMKSA cache entry is freed
|
||||
* @ctx: Context pointer for free_cb function
|
||||
* Returns: Pointer to PMKSA cache data or %NULL on failure
|
||||
*/
|
||||
struct rsn_pmksa_cache *
|
||||
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
|
||||
void *ctx), void *ctx)
|
||||
{
|
||||
struct rsn_pmksa_cache *pmksa;
|
||||
|
||||
pmksa = os_zalloc(sizeof(*pmksa));
|
||||
if (pmksa) {
|
||||
pmksa->free_cb = free_cb;
|
||||
pmksa->ctx = ctx;
|
||||
}
|
||||
|
||||
return pmksa;
|
||||
}
|
||||
|
||||
|
||||
static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
|
||||
struct radius_das_attrs *attr)
|
||||
{
|
||||
int match = 0;
|
||||
|
||||
if (attr->sta_addr) {
|
||||
if (!ether_addr_equal(attr->sta_addr, entry->spa))
|
||||
return 0;
|
||||
match++;
|
||||
}
|
||||
|
||||
if (attr->acct_multi_session_id) {
|
||||
char buf[20];
|
||||
|
||||
if (attr->acct_multi_session_id_len != 16)
|
||||
return 0;
|
||||
os_snprintf(buf, sizeof(buf), "%016llX",
|
||||
(unsigned long long) entry->acct_multi_session_id);
|
||||
if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0)
|
||||
return 0;
|
||||
match++;
|
||||
}
|
||||
|
||||
if (attr->cui) {
|
||||
if (!entry->cui ||
|
||||
attr->cui_len != wpabuf_len(entry->cui) ||
|
||||
os_memcmp(attr->cui, wpabuf_head(entry->cui),
|
||||
attr->cui_len) != 0)
|
||||
return 0;
|
||||
match++;
|
||||
}
|
||||
|
||||
if (attr->user_name) {
|
||||
if (!entry->identity ||
|
||||
attr->user_name_len != entry->identity_len ||
|
||||
os_memcmp(attr->user_name, entry->identity,
|
||||
attr->user_name_len) != 0)
|
||||
return 0;
|
||||
match++;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
|
||||
int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
|
||||
struct radius_das_attrs *attr)
|
||||
{
|
||||
int found = 0;
|
||||
struct rsn_pmksa_cache_entry *entry, *prev;
|
||||
|
||||
if (attr->acct_session_id)
|
||||
return -1;
|
||||
|
||||
entry = pmksa->pmksa;
|
||||
while (entry) {
|
||||
if (das_attr_match(entry, attr)) {
|
||||
found++;
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
pmksa_cache_free_entry(pmksa, prev);
|
||||
continue;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return found ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
* @buf: Buffer for the list
|
||||
* @len: Length of the buffer
|
||||
* Returns: Number of bytes written to buffer
|
||||
*
|
||||
* This function is used to generate a text format representation of the
|
||||
* current PMKSA cache contents for the ctrl_iface PMKSA command.
|
||||
*/
|
||||
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
|
||||
{
|
||||
int i, ret;
|
||||
char *pos = buf;
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
ret = os_snprintf(pos, buf + len - pos,
|
||||
"Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
|
||||
if (os_snprintf_error(buf + len - pos, ret))
|
||||
return pos - buf;
|
||||
pos += ret;
|
||||
i = 0;
|
||||
entry = pmksa->pmksa;
|
||||
while (entry) {
|
||||
ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
|
||||
i, MAC2STR(entry->spa));
|
||||
if (os_snprintf_error(buf + len - pos, ret))
|
||||
return pos - buf;
|
||||
pos += ret;
|
||||
pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
|
||||
PMKID_LEN);
|
||||
ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
|
||||
(int) (entry->expiration - now.sec),
|
||||
entry->opportunistic);
|
||||
if (os_snprintf_error(buf + len - pos, ret))
|
||||
return pos - buf;
|
||||
pos += ret;
|
||||
entry = entry->next;
|
||||
}
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||
#ifdef CONFIG_MESH
|
||||
|
||||
/**
|
||||
* pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
|
||||
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
|
||||
* @addr: MAC address of the peer (NULL means any)
|
||||
* @buf: Buffer for the list
|
||||
* @len: Length of the buffer
|
||||
* Returns: Number of bytes written to buffer
|
||||
*
|
||||
* This function is used to generate a text format representation of the
|
||||
* current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
|
||||
* in external storage.
|
||||
*/
|
||||
int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
|
||||
char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
char *pos, *end;
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct os_reltime now;
|
||||
|
||||
pos = buf;
|
||||
end = buf + len;
|
||||
os_get_reltime(&now);
|
||||
|
||||
|
||||
/*
|
||||
* Entry format:
|
||||
* <BSSID> <PMKID> <PMK> <expiration in seconds>
|
||||
*/
|
||||
for (entry = pmksa->pmksa; entry; entry = entry->next) {
|
||||
if (addr && !ether_addr_equal(entry->spa, addr))
|
||||
continue;
|
||||
|
||||
ret = os_snprintf(pos, end - pos, MACSTR " ",
|
||||
MAC2STR(entry->spa));
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return 0;
|
||||
pos += ret;
|
||||
|
||||
pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
|
||||
PMKID_LEN);
|
||||
|
||||
ret = os_snprintf(pos, end - pos, " ");
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return 0;
|
||||
pos += ret;
|
||||
|
||||
pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
|
||||
entry->pmk_len);
|
||||
|
||||
ret = os_snprintf(pos, end - pos, " %d\n",
|
||||
(int) (entry->expiration - now.sec));
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return 0;
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MESH */
|
||||
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
|
||||
83
src/ap/pmksa_cache_auth.h
Normal file
83
src/ap/pmksa_cache_auth.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* hostapd - PMKSA cache for IEEE 802.11i RSN
|
||||
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef PMKSA_CACHE_H
|
||||
#define PMKSA_CACHE_H
|
||||
|
||||
#include "radius/radius.h"
|
||||
|
||||
/**
|
||||
* struct rsn_pmksa_cache_entry - PMKSA cache entry
|
||||
*/
|
||||
struct rsn_pmksa_cache_entry {
|
||||
struct rsn_pmksa_cache_entry *next, *hnext;
|
||||
u8 pmkid[PMKID_LEN];
|
||||
u8 pmk[PMK_LEN_MAX];
|
||||
size_t pmk_len;
|
||||
u8 kck[WPA_KCK_MAX_LEN];
|
||||
size_t kck_len;
|
||||
os_time_t expiration;
|
||||
int akmp; /* WPA_KEY_MGMT_* */
|
||||
u8 spa[ETH_ALEN];
|
||||
|
||||
u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
|
||||
* public key */
|
||||
u8 *identity;
|
||||
size_t identity_len;
|
||||
struct wpabuf *cui;
|
||||
struct radius_class_data radius_class;
|
||||
u8 eap_type_authsrv;
|
||||
struct vlan_description *vlan_desc;
|
||||
int opportunistic;
|
||||
|
||||
u64 acct_multi_session_id;
|
||||
};
|
||||
|
||||
struct rsn_pmksa_cache;
|
||||
struct radius_das_attrs;
|
||||
|
||||
struct rsn_pmksa_cache *
|
||||
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
|
||||
void *ctx), void *ctx);
|
||||
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
|
||||
const u8 *spa, const u8 *pmkid);
|
||||
struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
|
||||
struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
|
||||
const u8 *pmkid);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
|
||||
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
|
||||
const u8 *kck, size_t kck_len,
|
||||
const u8 *aa, const u8 *spa, int session_timeout,
|
||||
struct eapol_state_machine *eapol, int akmp);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
|
||||
const u8 *kck, size_t kck_len, const u8 *aa,
|
||||
const u8 *spa, int session_timeout,
|
||||
struct eapol_state_machine *eapol, int akmp);
|
||||
int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
|
||||
const struct rsn_pmksa_cache_entry *old_entry,
|
||||
const u8 *aa, const u8 *pmkid);
|
||||
void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
|
||||
struct rsn_pmksa_cache_entry *entry,
|
||||
struct eapol_state_machine *eapol);
|
||||
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry);
|
||||
int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
|
||||
struct radius_das_attrs *attr);
|
||||
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
|
||||
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
|
||||
int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
|
||||
char *buf, size_t len);
|
||||
|
||||
#endif /* PMKSA_CACHE_H */
|
||||
273
src/ap/preauth_auth.c
Normal file
273
src/ap/preauth_auth.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
|
||||
* Copyright (c) 2004-2007, 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"
|
||||
|
||||
#ifdef CONFIG_RSN_PREAUTH
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "l2_packet/l2_packet.h"
|
||||
#include "common/wpa_common.h"
|
||||
#include "eapol_auth/eapol_auth_sm.h"
|
||||
#include "eapol_auth/eapol_auth_sm_i.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ieee802_1x.h"
|
||||
#include "sta_info.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "preauth_auth.h"
|
||||
|
||||
#ifndef ETH_P_PREAUTH
|
||||
#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
|
||||
#endif /* ETH_P_PREAUTH */
|
||||
|
||||
static const int dot11RSNAConfigPMKLifetime = 43200;
|
||||
|
||||
struct rsn_preauth_interface {
|
||||
struct rsn_preauth_interface *next;
|
||||
struct hostapd_data *hapd;
|
||||
struct l2_packet_data *l2;
|
||||
char *ifname;
|
||||
int ifindex;
|
||||
};
|
||||
|
||||
|
||||
static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct rsn_preauth_interface *piface = ctx;
|
||||
struct hostapd_data *hapd = piface->hapd;
|
||||
struct ieee802_1x_hdr *hdr;
|
||||
struct sta_info *sta;
|
||||
struct l2_ethhdr *ethhdr;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
|
||||
"from interface '%s'", piface->ifname);
|
||||
if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
|
||||
"(len=%lu)", (unsigned long) len);
|
||||
return;
|
||||
}
|
||||
|
||||
ethhdr = (struct l2_ethhdr *) buf;
|
||||
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
|
||||
|
||||
if (!ether_addr_equal(ethhdr->h_dest, hapd->own_addr)) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
|
||||
MACSTR, MAC2STR(ethhdr->h_dest));
|
||||
return;
|
||||
}
|
||||
|
||||
sta = ap_get_sta(hapd, ethhdr->h_source);
|
||||
if (sta && (sta->flags & WLAN_STA_ASSOC)) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
|
||||
"STA " MACSTR, MAC2STR(sta->addr));
|
||||
return;
|
||||
}
|
||||
if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
|
||||
sta = ap_sta_add(hapd, ethhdr->h_source);
|
||||
if (sta == NULL)
|
||||
return;
|
||||
sta->flags = WLAN_STA_PREAUTH;
|
||||
|
||||
ieee802_1x_new_station(hapd, sta);
|
||||
if (sta->eapol_sm == NULL) {
|
||||
ap_free_sta(hapd, sta);
|
||||
sta = NULL;
|
||||
} else {
|
||||
sta->eapol_sm->radius_identifier = -1;
|
||||
sta->eapol_sm->portValid = true;
|
||||
sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
|
||||
}
|
||||
}
|
||||
if (sta == NULL)
|
||||
return;
|
||||
sta->preauth_iface = piface;
|
||||
ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
|
||||
len - sizeof(*ethhdr), FRAME_ENCRYPTION_UNKNOWN);
|
||||
}
|
||||
|
||||
|
||||
static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
struct rsn_preauth_interface *piface;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
|
||||
|
||||
piface = os_zalloc(sizeof(*piface));
|
||||
if (piface == NULL)
|
||||
return -1;
|
||||
piface->hapd = hapd;
|
||||
|
||||
piface->ifname = os_strdup(ifname);
|
||||
if (piface->ifname == NULL) {
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
|
||||
rsn_preauth_receive, piface, 1);
|
||||
if (piface->l2 == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
|
||||
"to ETH_P_PREAUTH");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
piface->next = hapd->preauth_iface;
|
||||
hapd->preauth_iface = piface;
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
os_free(piface->ifname);
|
||||
fail1:
|
||||
os_free(piface);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
struct rsn_preauth_interface *piface, *prev;
|
||||
|
||||
piface = hapd->preauth_iface;
|
||||
hapd->preauth_iface = NULL;
|
||||
while (piface) {
|
||||
prev = piface;
|
||||
piface = piface->next;
|
||||
l2_packet_deinit(prev->l2);
|
||||
os_free(prev->ifname);
|
||||
os_free(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int rsn_preauth_iface_init(struct hostapd_data *hapd)
|
||||
{
|
||||
char *tmp, *start, *end;
|
||||
|
||||
if (hapd->conf->rsn_preauth_interfaces == NULL)
|
||||
return 0;
|
||||
|
||||
tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
start = tmp;
|
||||
for (;;) {
|
||||
while (*start == ' ')
|
||||
start++;
|
||||
if (*start == '\0')
|
||||
break;
|
||||
end = os_strchr(start, ' ');
|
||||
if (end)
|
||||
*end = '\0';
|
||||
|
||||
if (rsn_preauth_iface_add(hapd, start)) {
|
||||
rsn_preauth_iface_deinit(hapd);
|
||||
os_free(tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (end)
|
||||
start = end + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
os_free(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
struct sta_info *sta = timeout_ctx;
|
||||
wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
|
||||
MACSTR, MAC2STR(sta->addr));
|
||||
ap_free_sta(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int success)
|
||||
{
|
||||
const u8 *key;
|
||||
size_t len;
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
|
||||
HOSTAPD_LEVEL_INFO, "pre-authentication %s",
|
||||
success ? "succeeded" : "failed");
|
||||
|
||||
key = ieee802_1x_get_key(sta->eapol_sm, &len);
|
||||
if (len > PMK_LEN)
|
||||
len = PMK_LEN;
|
||||
if (success && key) {
|
||||
if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
|
||||
sta->addr,
|
||||
dot11RSNAConfigPMKLifetime,
|
||||
sta->eapol_sm) == 0) {
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"added PMKSA cache entry (pre-auth)");
|
||||
} else {
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"failed to add PMKSA cache entry "
|
||||
"(pre-auth)");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish STA entry removal from timeout in order to avoid freeing
|
||||
* STA data before the caller has finished processing.
|
||||
*/
|
||||
eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 *buf, size_t len)
|
||||
{
|
||||
struct rsn_preauth_interface *piface;
|
||||
struct l2_ethhdr *ethhdr;
|
||||
|
||||
piface = hapd->preauth_iface;
|
||||
while (piface) {
|
||||
if (piface == sta->preauth_iface)
|
||||
break;
|
||||
piface = piface->next;
|
||||
}
|
||||
|
||||
if (piface == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
|
||||
"interface for " MACSTR, MAC2STR(sta->addr));
|
||||
return;
|
||||
}
|
||||
|
||||
ethhdr = os_malloc(sizeof(*ethhdr) + len);
|
||||
if (ethhdr == NULL)
|
||||
return;
|
||||
|
||||
os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
|
||||
os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
|
||||
ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
|
||||
os_memcpy(ethhdr + 1, buf, len);
|
||||
|
||||
if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
|
||||
sizeof(*ethhdr) + len) < 0) {
|
||||
wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
|
||||
"l2_packet_send\n");
|
||||
}
|
||||
os_free(ethhdr);
|
||||
}
|
||||
|
||||
|
||||
void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_RSN_PREAUTH */
|
||||
52
src/ap/preauth_auth.h
Normal file
52
src/ap/preauth_auth.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
|
||||
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef PREAUTH_H
|
||||
#define PREAUTH_H
|
||||
|
||||
#ifdef CONFIG_RSN_PREAUTH
|
||||
|
||||
int rsn_preauth_iface_init(struct hostapd_data *hapd);
|
||||
void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
|
||||
void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int success);
|
||||
void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 *buf, size_t len);
|
||||
void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
#else /* CONFIG_RSN_PREAUTH */
|
||||
|
||||
static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_finished(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
int success)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_send(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
u8 *buf, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_RSN_PREAUTH */
|
||||
|
||||
#endif /* PREAUTH_H */
|
||||
795
src/ap/rrm.c
Normal file
795
src/ap/rrm.c
Normal file
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
* hostapd / Radio Measurement (RRM)
|
||||
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
|
||||
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
|
||||
* Copyright (c) 2016-2017, 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/wpa_ctrl.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "sta_info.h"
|
||||
#include "eloop.h"
|
||||
#include "neighbor_db.h"
|
||||
#include "rrm.h"
|
||||
|
||||
#define HOSTAPD_RRM_REQUEST_TIMEOUT 5
|
||||
|
||||
|
||||
static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_data;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
|
||||
hapd->lci_req_token);
|
||||
hapd->lci_req_active = 0;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
|
||||
const u8 *pos, size_t len)
|
||||
{
|
||||
if (!hapd->lci_req_active || hapd->lci_req_token != token) {
|
||||
wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
|
||||
return;
|
||||
}
|
||||
|
||||
hapd->lci_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
|
||||
wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_data;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
|
||||
hapd->range_req_token);
|
||||
hapd->range_req_active = 0;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
|
||||
const u8 *pos, size_t len)
|
||||
{
|
||||
if (!hapd->range_req_active || hapd->range_req_token != token) {
|
||||
wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
|
||||
token);
|
||||
return;
|
||||
}
|
||||
|
||||
hapd->range_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
|
||||
wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
|
||||
const u8 *addr, u8 token, u8 rep_mode,
|
||||
const u8 *pos, size_t len)
|
||||
{
|
||||
char report[2 * 255 + 1];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
|
||||
token, len, MAC2STR(addr));
|
||||
/* Skip to the beginning of the Beacon report */
|
||||
if (len < 3)
|
||||
return;
|
||||
pos += 3;
|
||||
len -= 3;
|
||||
report[0] = '\0';
|
||||
if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
|
||||
return;
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
|
||||
MAC2STR(addr), token, rep_mode, report);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
|
||||
const u8 *pos, *ie, *end;
|
||||
u8 token, rep_mode;
|
||||
|
||||
end = buf + len;
|
||||
token = mgmt->u.action.u.rrm.dialog_token;
|
||||
pos = mgmt->u.action.u.rrm.variable;
|
||||
|
||||
while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
|
||||
if (ie[1] < 3) {
|
||||
wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
|
||||
break;
|
||||
}
|
||||
|
||||
rep_mode = ie[3];
|
||||
wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
|
||||
rep_mode, ie[4]);
|
||||
|
||||
switch (ie[4]) {
|
||||
case MEASURE_TYPE_LCI:
|
||||
hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
|
||||
break;
|
||||
case MEASURE_TYPE_FTM_RANGE:
|
||||
hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
|
||||
break;
|
||||
case MEASURE_TYPE_BEACON:
|
||||
hostapd_handle_beacon_report(hapd, mgmt->sa, token,
|
||||
rep_mode, ie + 2, ie[1]);
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Measurement report type %u is not supported",
|
||||
ie[4]);
|
||||
break;
|
||||
}
|
||||
|
||||
pos = ie + ie[1] + 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
|
||||
{
|
||||
const u8 *subelem;
|
||||
|
||||
/* Range Request element + Location Subject + Maximum Age subelement */
|
||||
if (len < 3 + 1 + 4)
|
||||
return 0;
|
||||
|
||||
/* Subelements are arranged as IEs */
|
||||
subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
|
||||
if (subelem && subelem[1] == 2)
|
||||
return WPA_GET_LE16(subelem + 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
|
||||
{
|
||||
struct os_time curr, diff;
|
||||
unsigned long diff_l;
|
||||
|
||||
if (nr->stationary || max_age == 0xffff)
|
||||
return 1;
|
||||
|
||||
if (!max_age)
|
||||
return 0;
|
||||
|
||||
if (os_get_time(&curr))
|
||||
return 0;
|
||||
|
||||
os_time_sub(&curr, &nr->lci_date, &diff);
|
||||
|
||||
/* avoid overflow */
|
||||
if (diff.sec > 0xffff)
|
||||
return 0;
|
||||
|
||||
/* LCI age is calculated in 10th of a second units. */
|
||||
diff_l = diff.sec * 10 + diff.usec / 100000;
|
||||
|
||||
return max_age > diff_l;
|
||||
}
|
||||
|
||||
|
||||
static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
|
||||
struct hostapd_neighbor_entry *nr,
|
||||
int send_lci, int send_civic)
|
||||
{
|
||||
size_t len = 2 + wpabuf_len(nr->nr);
|
||||
|
||||
if (send_lci && nr->lci)
|
||||
len += 2 + wpabuf_len(nr->lci);
|
||||
|
||||
if (send_civic && nr->civic)
|
||||
len += 2 + wpabuf_len(nr->civic);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
|
||||
const u8 *addr, u8 dialog_token,
|
||||
struct wpa_ssid_value *ssid, u8 lci,
|
||||
u8 civic, u16 lci_max_age)
|
||||
{
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
struct wpabuf *buf;
|
||||
u8 *msmt_token;
|
||||
|
||||
/*
|
||||
* The number and length of the Neighbor Report elements in a Neighbor
|
||||
* Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
|
||||
* of RRM header.
|
||||
*/
|
||||
buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
|
||||
wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
|
||||
wpabuf_put_u8(buf, dialog_token);
|
||||
|
||||
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
|
||||
list) {
|
||||
int send_lci;
|
||||
size_t len;
|
||||
|
||||
if (ssid->ssid_len != nr->ssid.ssid_len ||
|
||||
os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
|
||||
continue;
|
||||
|
||||
send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
|
||||
len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
|
||||
|
||||
if (len - 2 > 0xff) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"NR entry for " MACSTR " exceeds 0xFF bytes",
|
||||
MAC2STR(nr->bssid));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len > wpabuf_tailroom(buf))
|
||||
break;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
|
||||
wpabuf_put_u8(buf, len - 2);
|
||||
wpabuf_put_buf(buf, nr->nr);
|
||||
|
||||
if (send_lci && nr->lci) {
|
||||
wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
|
||||
wpabuf_put_u8(buf, wpabuf_len(nr->lci));
|
||||
/*
|
||||
* Override measurement token - the first byte of the
|
||||
* Measurement Report element.
|
||||
*/
|
||||
msmt_token = wpabuf_put(buf, 0);
|
||||
wpabuf_put_buf(buf, nr->lci);
|
||||
*msmt_token = lci;
|
||||
}
|
||||
|
||||
if (civic && nr->civic) {
|
||||
wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
|
||||
wpabuf_put_u8(buf, wpabuf_len(nr->civic));
|
||||
/*
|
||||
* Override measurement token - the first byte of the
|
||||
* Measurement Report element.
|
||||
*/
|
||||
msmt_token = wpabuf_put(buf, 0);
|
||||
wpabuf_put_buf(buf, nr->civic);
|
||||
*msmt_token = civic;
|
||||
}
|
||||
}
|
||||
|
||||
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
wpabuf_free(buf);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
|
||||
const u8 *pos, *ie, *end;
|
||||
struct wpa_ssid_value ssid = {
|
||||
.ssid_len = 0
|
||||
};
|
||||
u8 token;
|
||||
u8 lci = 0, civic = 0; /* Measurement tokens */
|
||||
u16 lci_max_age = 0;
|
||||
|
||||
if (!(hapd->conf->radio_measurements[0] &
|
||||
WLAN_RRM_CAPS_NEIGHBOR_REPORT))
|
||||
return;
|
||||
|
||||
end = buf + len;
|
||||
|
||||
token = mgmt->u.action.u.rrm.dialog_token;
|
||||
pos = mgmt->u.action.u.rrm.variable;
|
||||
len = end - pos;
|
||||
|
||||
ie = get_ie(pos, len, WLAN_EID_SSID);
|
||||
if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
|
||||
ssid.ssid_len = ie[1];
|
||||
os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
|
||||
} else {
|
||||
ssid.ssid_len = hapd->conf->ssid.ssid_len;
|
||||
os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
|
||||
}
|
||||
|
||||
while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
|
||||
if (ie[1] < 3)
|
||||
break;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Neighbor report request, measure type %u",
|
||||
ie[4]);
|
||||
|
||||
switch (ie[4]) { /* Measurement Type */
|
||||
case MEASURE_TYPE_LCI:
|
||||
lci = ie[2]; /* Measurement Token */
|
||||
lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
|
||||
ie[1]);
|
||||
break;
|
||||
case MEASURE_TYPE_LOCATION_CIVIC:
|
||||
civic = ie[2]; /* Measurement token */
|
||||
break;
|
||||
}
|
||||
|
||||
pos = ie + ie[1] + 2;
|
||||
len = end - pos;
|
||||
}
|
||||
|
||||
hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
|
||||
lci_max_age);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_link_mesr_rep_timeout_handler(void *eloop_data,
|
||||
void *user_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_data;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RRM: Link measurement request (token %u) timed out",
|
||||
hapd->link_measurement_req_token);
|
||||
hapd->link_mesr_req_active = 0;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_handle_link_mesr_report(struct hostapd_data *hapd,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
|
||||
const struct rrm_link_measurement_report *report;
|
||||
const u8 *pos, *end;
|
||||
char report_msg[2 * 8 + 1];
|
||||
|
||||
end = buf + len;
|
||||
pos = mgmt->u.action.u.rrm.variable;
|
||||
report = (const struct rrm_link_measurement_report *) (pos - 1);
|
||||
if (end - (const u8 *) report < (int) sizeof(*report))
|
||||
return;
|
||||
|
||||
if (!hapd->link_mesr_req_active ||
|
||||
(hapd->link_measurement_req_token != report->dialog_token)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Unexpected Link measurement report, token %u",
|
||||
report->dialog_token);
|
||||
return;
|
||||
}
|
||||
|
||||
hapd->link_mesr_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
|
||||
|
||||
report_msg[0] = '\0';
|
||||
if (wpa_snprintf_hex(report_msg, sizeof(report_msg),
|
||||
pos, end - pos) < 0)
|
||||
return;
|
||||
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, LINK_MSR_RESP_RX MACSTR " %u %s",
|
||||
MAC2STR(mgmt->sa), report->dialog_token, report_msg);
|
||||
}
|
||||
|
||||
|
||||
void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
|
||||
|
||||
/*
|
||||
* Check for enough bytes: header + (1B)Category + (1B)Action +
|
||||
* (1B)Dialog Token.
|
||||
*/
|
||||
if (len < IEEE80211_HDRLEN + 3)
|
||||
return;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
|
||||
mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
|
||||
|
||||
switch (mgmt->u.action.u.rrm.action) {
|
||||
case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
|
||||
hostapd_handle_radio_msmt_report(hapd, buf, len);
|
||||
break;
|
||||
case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
|
||||
hostapd_handle_nei_report_req(hapd, buf, len);
|
||||
break;
|
||||
case WLAN_RRM_LINK_MEASUREMENT_REPORT:
|
||||
hostapd_handle_link_mesr_report(hapd, buf, len);
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
|
||||
mgmt->u.action.u.rrm.action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct sta_info *sta = ap_get_sta(hapd, addr);
|
||||
int ret;
|
||||
|
||||
if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Request LCI: Destination address is not connected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Request LCI: Station does not support LCI in RRM");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hapd->lci_req_active) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Request LCI: LCI request is already in process, overriding");
|
||||
hapd->lci_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Measurement request (5) + Measurement element with LCI (10) */
|
||||
buf = wpabuf_alloc(5 + 10);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
hapd->lci_req_token++;
|
||||
/* For wraparounds - the token must be nonzero */
|
||||
if (!hapd->lci_req_token)
|
||||
hapd->lci_req_token++;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
|
||||
wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
|
||||
wpabuf_put_u8(buf, hapd->lci_req_token);
|
||||
wpabuf_put_le16(buf, 0); /* Number of repetitions */
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
|
||||
wpabuf_put_u8(buf, 3 + 1 + 4);
|
||||
|
||||
wpabuf_put_u8(buf, 1); /* Measurement Token */
|
||||
/*
|
||||
* Parallel and Enable bits are 0, Duration, Request, and Report are
|
||||
* reserved.
|
||||
*/
|
||||
wpabuf_put_u8(buf, 0);
|
||||
wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
|
||||
|
||||
wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
|
||||
|
||||
wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
|
||||
wpabuf_put_u8(buf, 2);
|
||||
wpabuf_put_le16(buf, 0xffff);
|
||||
|
||||
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
wpabuf_free(buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hapd->lci_req_active = 1;
|
||||
|
||||
eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
|
||||
hostapd_lci_rep_timeout_handler, hapd, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 random_interval, u8 min_ap,
|
||||
const u8 *responders, unsigned int n_responders)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct sta_info *sta;
|
||||
u8 *len;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
|
||||
" rand interval %u min AP %u n_responders %u", MAC2STR(addr),
|
||||
random_interval, min_ap, n_responders);
|
||||
|
||||
if (min_ap == 0 || min_ap > n_responders) {
|
||||
wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Request range: Destination address is not connected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Request range: Destination station does not support FTM range report in RRM");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hapd->range_req_active) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Request range: Range request is already in process; overriding");
|
||||
hapd->range_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Action + measurement type + token + reps + EID + len = 7 */
|
||||
buf = wpabuf_alloc(7 + 255);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
hapd->range_req_token++;
|
||||
if (!hapd->range_req_token) /* For wraparounds */
|
||||
hapd->range_req_token++;
|
||||
|
||||
/* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
|
||||
wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
|
||||
wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
|
||||
wpabuf_put_le16(buf, 0); /* Number of Repetitions */
|
||||
|
||||
/* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
|
||||
wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
|
||||
len = wpabuf_put(buf, 1); /* Length will be set later */
|
||||
|
||||
wpabuf_put_u8(buf, 1); /* Measurement Token */
|
||||
/*
|
||||
* Parallel and Enable bits are 0; Duration, Request, and Report are
|
||||
* reserved.
|
||||
*/
|
||||
wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
|
||||
wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
|
||||
|
||||
/* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
|
||||
wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
|
||||
wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
|
||||
|
||||
/* FTM Range Subelements */
|
||||
|
||||
/*
|
||||
* Taking the neighbor report part of the range request from neighbor
|
||||
* database instead of requesting the separate bits of data from the
|
||||
* user.
|
||||
*/
|
||||
for (i = 0; i < n_responders; i++) {
|
||||
struct hostapd_neighbor_entry *nr;
|
||||
|
||||
nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
|
||||
NULL);
|
||||
if (!nr) {
|
||||
wpa_printf(MSG_INFO, "Missing neighbor report for "
|
||||
MACSTR, MAC2STR(responders + ETH_ALEN * i));
|
||||
wpabuf_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
|
||||
wpa_printf(MSG_ERROR, "Too long range request");
|
||||
wpabuf_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
|
||||
wpabuf_put_u8(buf, wpabuf_len(nr->nr));
|
||||
wpabuf_put_buf(buf, nr->nr);
|
||||
}
|
||||
|
||||
/* Action + measurement type + token + reps + EID + len = 7 */
|
||||
*len = wpabuf_len(buf) - 7;
|
||||
|
||||
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
wpabuf_free(buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hapd->range_req_active = 1;
|
||||
|
||||
eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
|
||||
hostapd_range_rep_timeout_handler, hapd, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_clean_rrm(struct hostapd_data *hapd)
|
||||
{
|
||||
hostapd_free_neighbor_db(hapd);
|
||||
eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
|
||||
hapd->lci_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
|
||||
hapd->range_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 req_mode, const struct wpabuf *req)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct sta_info *sta = ap_get_sta(hapd, addr);
|
||||
int ret;
|
||||
enum beacon_report_mode mode;
|
||||
const u8 *pos;
|
||||
|
||||
/* Request data:
|
||||
* Operating Class (1), Channel Number (1), Randomization Interval (2),
|
||||
* Measurement Duration (2), Measurement Mode (1), BSSID (6),
|
||||
* Optional Subelements (variable)
|
||||
*/
|
||||
if (wpabuf_len(req) < 13) {
|
||||
wpa_printf(MSG_INFO, "Beacon request: Too short request data");
|
||||
return -1;
|
||||
}
|
||||
pos = wpabuf_head(req);
|
||||
mode = pos[6];
|
||||
|
||||
if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Beacon request: " MACSTR " is not connected",
|
||||
MAC2STR(addr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case BEACON_REPORT_MODE_PASSIVE:
|
||||
if (!(sta->rrm_enabled_capa[0] &
|
||||
WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Beacon request: " MACSTR
|
||||
" does not support passive beacon report",
|
||||
MAC2STR(addr));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case BEACON_REPORT_MODE_ACTIVE:
|
||||
if (!(sta->rrm_enabled_capa[0] &
|
||||
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Beacon request: " MACSTR
|
||||
" does not support active beacon report",
|
||||
MAC2STR(addr));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case BEACON_REPORT_MODE_TABLE:
|
||||
if (!(sta->rrm_enabled_capa[0] &
|
||||
WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Beacon request: " MACSTR
|
||||
" does not support table beacon report",
|
||||
MAC2STR(addr));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_INFO,
|
||||
"Beacon request: Unknown measurement mode %d", mode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
hapd->beacon_req_token++;
|
||||
if (!hapd->beacon_req_token)
|
||||
hapd->beacon_req_token++;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
|
||||
wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
|
||||
wpabuf_put_u8(buf, hapd->beacon_req_token);
|
||||
wpabuf_put_le16(buf, 0); /* Number of repetitions */
|
||||
|
||||
/* Measurement Request element */
|
||||
wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
|
||||
wpabuf_put_u8(buf, 3 + wpabuf_len(req));
|
||||
wpabuf_put_u8(buf, 1); /* Measurement Token */
|
||||
wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
|
||||
wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
|
||||
wpabuf_put_buf(buf, req);
|
||||
|
||||
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
wpabuf_free(buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return hapd->beacon_req_token;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len, int ok)
|
||||
{
|
||||
if (len < 24 + 3)
|
||||
return;
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
|
||||
" %u ack=%d", MAC2STR(mgmt->da),
|
||||
mgmt->u.action.u.rrm.dialog_token, ok);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_send_link_measurement_req(struct hostapd_data *hapd, const u8 *addr)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "Request Link Measurement: dest addr " MACSTR,
|
||||
MAC2STR(addr));
|
||||
|
||||
if (!(hapd->iface->drv_rrm_flags &
|
||||
WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Request Link Measurement: the driver does not support TX power insertion");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Request Link Measurement: specied STA is not connected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(sta->rrm_enabled_capa[0] & WLAN_RRM_CAPS_LINK_MEASUREMENT)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Request Link Measurement: destination STA does not support link measurement");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hapd->link_mesr_req_active) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Request Link Measurement: request already in process - overriding");
|
||||
hapd->link_mesr_req_active = 0;
|
||||
eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler,
|
||||
hapd, NULL);
|
||||
}
|
||||
|
||||
/* Action + Action type + token + Tx Power used + Max Tx Power = 5 */
|
||||
buf = wpabuf_alloc(5);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
hapd->link_measurement_req_token++;
|
||||
if (!hapd->link_measurement_req_token)
|
||||
hapd->link_measurement_req_token++;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
|
||||
wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
|
||||
wpabuf_put_u8(buf, hapd->link_measurement_req_token);
|
||||
/* NOTE: The driver is expected to fill the Tx Power Used and Max Tx
|
||||
* Power */
|
||||
wpabuf_put_u8(buf, 0);
|
||||
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);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hapd->link_mesr_req_active = 1;
|
||||
|
||||
eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
|
||||
hostapd_link_mesr_rep_timeout_handler, hapd,
|
||||
NULL);
|
||||
|
||||
return hapd->link_measurement_req_token;
|
||||
}
|
||||
35
src/ap/rrm.h
Normal file
35
src/ap/rrm.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* hostapd / Radio Measurement (RRM)
|
||||
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
|
||||
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef RRM_H
|
||||
#define RRM_H
|
||||
|
||||
/*
|
||||
* Max measure request length is 255, -6 of the body we have 249 for the
|
||||
* neighbor report elements. Each neighbor report element is at least 2 + 13
|
||||
* bytes, so we can't have more than 16 responders in the request.
|
||||
*/
|
||||
#define RRM_RANGE_REQ_MAX_RESPONDERS 16
|
||||
|
||||
void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
|
||||
const u8 *buf, size_t len);
|
||||
int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
|
||||
int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 random_interval, u8 min_ap,
|
||||
const u8 *responders, unsigned int n_responders);
|
||||
void hostapd_clean_rrm(struct hostapd_data *hapd);
|
||||
int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 req_mode, const struct wpabuf *req);
|
||||
void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len, int ok);
|
||||
int hostapd_send_link_measurement_req(struct hostapd_data *hapd,
|
||||
const u8 *addr);
|
||||
|
||||
#endif /* RRM_H */
|
||||
1888
src/ap/sta_info.c
Normal file
1888
src/ap/sta_info.c
Normal file
File diff suppressed because it is too large
Load Diff
446
src/ap/sta_info.h
Normal file
446
src/ap/sta_info.h
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* hostapd / Station table
|
||||
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef STA_INFO_H
|
||||
#define STA_INFO_H
|
||||
|
||||
#include "common/defs.h"
|
||||
#include "list.h"
|
||||
#include "vlan.h"
|
||||
#include "common/wpa_common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/sae.h"
|
||||
#include "crypto/sha384.h"
|
||||
#include "pasn/pasn_common.h"
|
||||
#include "hostapd.h"
|
||||
|
||||
/* STA flags */
|
||||
#define WLAN_STA_AUTH BIT(0)
|
||||
#define WLAN_STA_ASSOC BIT(1)
|
||||
#define WLAN_STA_AUTHORIZED BIT(5)
|
||||
#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
|
||||
#define WLAN_STA_SHORT_PREAMBLE BIT(7)
|
||||
#define WLAN_STA_PREAUTH BIT(8)
|
||||
#define WLAN_STA_WMM BIT(9)
|
||||
#define WLAN_STA_MFP BIT(10)
|
||||
#define WLAN_STA_HT BIT(11)
|
||||
#define WLAN_STA_WPS BIT(12)
|
||||
#define WLAN_STA_MAYBE_WPS BIT(13)
|
||||
#define WLAN_STA_WDS BIT(14)
|
||||
#define WLAN_STA_ASSOC_REQ_OK BIT(15)
|
||||
#define WLAN_STA_WPS2 BIT(16)
|
||||
#define WLAN_STA_GAS BIT(17)
|
||||
#define WLAN_STA_VHT BIT(18)
|
||||
#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
|
||||
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
|
||||
#define WLAN_STA_VENDOR_VHT BIT(21)
|
||||
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
|
||||
#define WLAN_STA_MULTI_AP BIT(23)
|
||||
#define WLAN_STA_HE BIT(24)
|
||||
#define WLAN_STA_6GHZ BIT(25)
|
||||
#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
|
||||
#define WLAN_STA_EHT BIT(27)
|
||||
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
|
||||
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
|
||||
#define WLAN_STA_NONERP BIT(31)
|
||||
|
||||
/* Maximum number of supported rates (from both Supported Rates and Extended
|
||||
* Supported Rates IEs). */
|
||||
#define WLAN_SUPP_RATES_MAX 32
|
||||
|
||||
struct hostapd_data;
|
||||
|
||||
struct mbo_non_pref_chan_info {
|
||||
struct mbo_non_pref_chan_info *next;
|
||||
u8 op_class;
|
||||
u8 pref;
|
||||
u8 reason_code;
|
||||
u8 num_channels;
|
||||
u8 channels[];
|
||||
};
|
||||
|
||||
struct pending_eapol_rx {
|
||||
struct wpabuf *buf;
|
||||
struct os_reltime rx_time;
|
||||
enum frame_encryption encrypted;
|
||||
};
|
||||
|
||||
#define EHT_ML_MAX_STA_PROF_LEN 1024
|
||||
struct mld_info {
|
||||
bool mld_sta;
|
||||
|
||||
struct ml_common_info {
|
||||
u8 mld_addr[ETH_ALEN];
|
||||
u16 medium_sync_delay;
|
||||
u16 eml_capa;
|
||||
u16 mld_capa;
|
||||
} common_info;
|
||||
|
||||
struct mld_link_info {
|
||||
u8 valid:1;
|
||||
u8 nstr_bitmap_len:2;
|
||||
u8 local_addr[ETH_ALEN];
|
||||
u8 peer_addr[ETH_ALEN];
|
||||
|
||||
u8 nstr_bitmap[2];
|
||||
|
||||
u16 capability;
|
||||
|
||||
u16 status;
|
||||
u16 resp_sta_profile_len;
|
||||
u8 *resp_sta_profile;
|
||||
} links[MAX_NUM_MLD_LINKS];
|
||||
};
|
||||
|
||||
struct sta_info {
|
||||
struct sta_info *next; /* next entry in sta list */
|
||||
struct sta_info *hnext; /* next entry in hash table list */
|
||||
u8 addr[6];
|
||||
be32 ipaddr;
|
||||
struct dl_list ip6addr; /* list head for struct ip6addr */
|
||||
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
|
||||
u16 disconnect_reason_code; /* RADIUS server override */
|
||||
u32 flags; /* Bitfield of WLAN_STA_* */
|
||||
u16 capability;
|
||||
u16 listen_interval; /* or beacon_int for APs */
|
||||
u8 supported_rates[WLAN_SUPP_RATES_MAX];
|
||||
int supported_rates_len;
|
||||
u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
|
||||
|
||||
#ifdef CONFIG_MESH
|
||||
enum mesh_plink_state plink_state;
|
||||
u16 peer_lid;
|
||||
u16 my_lid;
|
||||
u16 peer_aid;
|
||||
u16 mpm_close_reason;
|
||||
int mpm_retries;
|
||||
u8 my_nonce[WPA_NONCE_LEN];
|
||||
u8 peer_nonce[WPA_NONCE_LEN];
|
||||
u8 aek[32]; /* SHA256 digest length */
|
||||
u8 mtk[WPA_TK_MAX_LEN];
|
||||
size_t mtk_len;
|
||||
u8 mgtk_rsc[6];
|
||||
u8 mgtk_key_id;
|
||||
u8 mgtk[WPA_TK_MAX_LEN];
|
||||
size_t mgtk_len;
|
||||
u8 igtk_rsc[6];
|
||||
u8 igtk[WPA_TK_MAX_LEN];
|
||||
size_t igtk_len;
|
||||
u16 igtk_key_id;
|
||||
u8 sae_auth_retry;
|
||||
#endif /* CONFIG_MESH */
|
||||
|
||||
unsigned int nonerp_set:1;
|
||||
unsigned int no_short_slot_time_set:1;
|
||||
unsigned int no_short_preamble_set:1;
|
||||
unsigned int no_ht_gf_set:1;
|
||||
unsigned int no_ht_set:1;
|
||||
unsigned int ht40_intolerant_set:1;
|
||||
unsigned int ht_20mhz_set:1;
|
||||
unsigned int no_p2p_set:1;
|
||||
unsigned int qos_map_enabled:1;
|
||||
unsigned int remediation:1;
|
||||
unsigned int hs20_deauth_requested:1;
|
||||
unsigned int hs20_deauth_on_ack:1;
|
||||
unsigned int session_timeout_set:1;
|
||||
unsigned int radius_das_match:1;
|
||||
unsigned int ecsa_supported:1;
|
||||
unsigned int added_unassoc:1;
|
||||
unsigned int pending_wds_enable:1;
|
||||
unsigned int power_capab:1;
|
||||
unsigned int agreed_to_steer:1;
|
||||
unsigned int hs20_t_c_filtering:1;
|
||||
unsigned int ft_over_ds:1;
|
||||
unsigned int external_dh_updated:1;
|
||||
unsigned int post_csa_sa_query:1;
|
||||
|
||||
u16 auth_alg;
|
||||
|
||||
enum {
|
||||
STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
|
||||
STA_DISASSOC_FROM_CLI
|
||||
} timeout_next;
|
||||
|
||||
u16 deauth_reason;
|
||||
u16 disassoc_reason;
|
||||
|
||||
/* IEEE 802.1X related data */
|
||||
struct eapol_state_machine *eapol_sm;
|
||||
|
||||
struct pending_eapol_rx *pending_eapol_rx;
|
||||
|
||||
u64 acct_session_id;
|
||||
struct os_reltime acct_session_start;
|
||||
int acct_session_started;
|
||||
int acct_terminate_cause; /* Acct-Terminate-Cause */
|
||||
int acct_interim_interval; /* Acct-Interim-Interval */
|
||||
unsigned int acct_interim_errors;
|
||||
|
||||
/* For extending 32-bit driver counters to 64-bit counters */
|
||||
u32 last_rx_bytes_hi;
|
||||
u32 last_rx_bytes_lo;
|
||||
u32 last_tx_bytes_hi;
|
||||
u32 last_tx_bytes_lo;
|
||||
|
||||
u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
|
||||
|
||||
struct wpa_state_machine *wpa_sm;
|
||||
struct rsn_preauth_interface *preauth_iface;
|
||||
|
||||
int vlan_id; /* 0: none, >0: VID */
|
||||
struct vlan_description *vlan_desc;
|
||||
int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
|
||||
/* PSKs from RADIUS authentication server */
|
||||
struct hostapd_sta_wpa_psk_short *psk;
|
||||
|
||||
char *identity; /* User-Name from RADIUS */
|
||||
char *radius_cui; /* Chargeable-User-Identity from RADIUS */
|
||||
|
||||
struct ieee80211_ht_capabilities *ht_capabilities;
|
||||
struct ieee80211_vht_capabilities *vht_capabilities;
|
||||
struct ieee80211_vht_operation *vht_operation;
|
||||
u8 vht_opmode;
|
||||
struct ieee80211_he_capabilities *he_capab;
|
||||
size_t he_capab_len;
|
||||
struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
|
||||
struct ieee80211_eht_capabilities *eht_capab;
|
||||
size_t eht_capab_len;
|
||||
|
||||
int sa_query_count; /* number of pending SA Query requests;
|
||||
* 0 = no SA Query in progress */
|
||||
int sa_query_timed_out;
|
||||
u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
|
||||
* sa_query_count octets of pending SA Query
|
||||
* transaction identifiers */
|
||||
struct os_reltime sa_query_start;
|
||||
|
||||
#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
|
||||
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
|
||||
struct gas_dialog_info *gas_dialog;
|
||||
u8 gas_dialog_next;
|
||||
#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
|
||||
|
||||
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
|
||||
struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
|
||||
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
|
||||
/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
|
||||
struct wpabuf *roaming_consortium;
|
||||
u8 remediation_method;
|
||||
char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
|
||||
char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
|
||||
struct wpabuf *hs20_deauth_req;
|
||||
char *hs20_session_info_url;
|
||||
int hs20_disassoc_timer;
|
||||
#ifdef CONFIG_FST
|
||||
struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
struct os_reltime connected_time;
|
||||
|
||||
#ifdef CONFIG_SAE
|
||||
struct sae_data *sae;
|
||||
unsigned int mesh_sae_pmksa_caching:1;
|
||||
#endif /* CONFIG_SAE */
|
||||
|
||||
/* valid only if session_timeout_set == 1 */
|
||||
struct os_reltime session_timeout;
|
||||
|
||||
/* Last Authentication/(Re)Association Request/Action frame sequence
|
||||
* control */
|
||||
u16 last_seq_ctrl;
|
||||
/* Last Authentication/(Re)Association Request/Action frame subtype */
|
||||
u8 last_subtype;
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
|
||||
* enum mbo_cellular_capa values */
|
||||
struct mbo_non_pref_chan_info *non_pref_chan;
|
||||
int auth_rssi; /* Last Authentication frame RSSI */
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
u8 *supp_op_classes; /* Supported Operating Classes element, if
|
||||
* received, starting from the Length field */
|
||||
|
||||
u8 rrm_enabled_capa[5];
|
||||
|
||||
s8 min_tx_power;
|
||||
s8 max_tx_power;
|
||||
|
||||
#ifdef CONFIG_TAXONOMY
|
||||
struct wpabuf *probe_ie_taxonomy;
|
||||
struct wpabuf *assoc_ie_taxonomy;
|
||||
#endif /* CONFIG_TAXONOMY */
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
u8 fils_snonce[FILS_NONCE_LEN];
|
||||
u8 fils_session[FILS_SESSION_LEN];
|
||||
u8 fils_erp_pmkid[PMKID_LEN];
|
||||
u8 *fils_pending_assoc_req;
|
||||
size_t fils_pending_assoc_req_len;
|
||||
unsigned int fils_pending_assoc_is_reassoc:1;
|
||||
unsigned int fils_dhcp_rapid_commit_proxy:1;
|
||||
unsigned int fils_erp_pmkid_set:1;
|
||||
unsigned int fils_drv_assoc_finish:1;
|
||||
struct wpabuf *fils_hlp_resp;
|
||||
struct wpabuf *hlp_dhcp_discover;
|
||||
void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u16 resp, struct wpabuf *data, int pub);
|
||||
#ifdef CONFIG_FILS_SK_PFS
|
||||
struct crypto_ecdh *fils_ecdh;
|
||||
#endif /* CONFIG_FILS_SK_PFS */
|
||||
struct wpabuf *fils_dh_ss;
|
||||
struct wpabuf *fils_g_sta;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
#ifdef CONFIG_OWE
|
||||
u8 *owe_pmk;
|
||||
size_t owe_pmk_len;
|
||||
struct crypto_ecdh *owe_ecdh;
|
||||
u16 owe_group;
|
||||
#endif /* CONFIG_OWE */
|
||||
|
||||
u8 *ext_capability;
|
||||
char *ifname_wds; /* WDS ifname, if in use */
|
||||
|
||||
#ifdef CONFIG_DPP2
|
||||
struct dpp_pfs *dpp_pfs;
|
||||
#endif /* CONFIG_DPP2 */
|
||||
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
enum wpa_alg last_tk_alg;
|
||||
int last_tk_key_idx;
|
||||
u8 last_tk[WPA_TK_MAX_LEN];
|
||||
size_t last_tk_len;
|
||||
u8 *sae_postponed_commit;
|
||||
size_t sae_postponed_commit_len;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
#ifdef CONFIG_AIRTIME_POLICY
|
||||
unsigned int airtime_weight;
|
||||
struct os_reltime backlogged_until;
|
||||
#endif /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
#ifdef CONFIG_PASN
|
||||
struct pasn_data *pasn;
|
||||
#endif /* CONFIG_PASN */
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
struct mld_info mld_info;
|
||||
u8 mld_assoc_link_id;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
|
||||
* units of 1000 TUs */
|
||||
};
|
||||
|
||||
|
||||
/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
|
||||
* passed since last received frame from the station, a nullfunc data frame is
|
||||
* sent to the station. If this frame is not acknowledged and no other frames
|
||||
* have been received, the station will be disassociated after
|
||||
* AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
|
||||
* after AP_DEAUTH_DELAY seconds has passed after disassociation. */
|
||||
#define AP_MAX_INACTIVITY (5 * 60)
|
||||
#define AP_DISASSOC_DELAY (3)
|
||||
#define AP_DEAUTH_DELAY (1)
|
||||
/* Number of seconds to keep STA entry with Authenticated flag after it has
|
||||
* been disassociated. */
|
||||
#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
|
||||
/* Number of seconds to keep STA entry after it has been deauthenticated. */
|
||||
#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
|
||||
|
||||
|
||||
int ap_for_each_sta(struct hostapd_data *hapd,
|
||||
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
void *ctx),
|
||||
void *ctx);
|
||||
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
|
||||
struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
|
||||
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void hostapd_free_stas(struct hostapd_data *hapd);
|
||||
void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
|
||||
void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u32 session_timeout);
|
||||
void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u32 session_timeout);
|
||||
void ap_sta_no_session_timeout(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int warning_time);
|
||||
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
|
||||
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u16 reason);
|
||||
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u16 reason);
|
||||
#ifdef CONFIG_WPS
|
||||
int ap_sta_wps_cancel(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, void *ctx);
|
||||
#endif /* CONFIG_WPS */
|
||||
int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct vlan_description *vlan_desc);
|
||||
void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *addr, u16 reason);
|
||||
|
||||
bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int authorized);
|
||||
void ap_sta_set_authorized_event(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized);
|
||||
void ap_sta_set_authorized(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized);
|
||||
static inline int ap_sta_is_authorized(struct sta_info *sta)
|
||||
{
|
||||
return sta->flags & WLAN_STA_AUTHORIZED;
|
||||
}
|
||||
|
||||
void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
|
||||
void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
unsigned timeout);
|
||||
int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
|
||||
#else /* CONFIG_IEEE80211BE */
|
||||
return false;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
}
|
||||
|
||||
static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (sta)
|
||||
sta->mld_info.mld_sta = mld;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
}
|
||||
|
||||
void ap_sta_free_sta_profile(struct mld_info *info);
|
||||
|
||||
void hostapd_free_link_stas(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* STA_INFO_H */
|
||||
292
src/ap/taxonomy.c
Normal file
292
src/ap/taxonomy.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* hostapd / Client taxonomy
|
||||
* Copyright (c) 2015 Google, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*
|
||||
* Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
|
||||
* and render them to a descriptive string. The tag number of standard options
|
||||
* is written to the string, while the vendor ID and subtag are written for
|
||||
* vendor options.
|
||||
*
|
||||
* Example strings:
|
||||
* 0,1,50,45,221(00904c,51)
|
||||
* 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "taxonomy.h"
|
||||
|
||||
|
||||
/* Copy a string with no funny schtuff allowed; only alphanumerics. */
|
||||
static void no_mischief_strncpy(char *dst, const char *src, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned char s = src[i];
|
||||
int is_lower = s >= 'a' && s <= 'z';
|
||||
int is_upper = s >= 'A' && s <= 'Z';
|
||||
int is_digit = s >= '0' && s <= '9';
|
||||
|
||||
if (is_lower || is_upper || is_digit) {
|
||||
/* TODO: if any manufacturer uses Unicode within the
|
||||
* WPS header, it will get mangled here. */
|
||||
dst[i] = s;
|
||||
} else {
|
||||
/* Note that even spaces will be transformed to
|
||||
* underscores, so 'Nexus 7' will turn into 'Nexus_7'.
|
||||
* This is deliberate, to make the string easier to
|
||||
* parse. */
|
||||
dst[i] = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int get_wps_name(char *name, size_t name_len,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
/* Inside the WPS IE are a series of attributes, using two byte IDs
|
||||
* and two byte lengths. We're looking for the model name, if
|
||||
* present. */
|
||||
while (data_len >= 4) {
|
||||
u16 id, elen;
|
||||
|
||||
id = WPA_GET_BE16(data);
|
||||
elen = WPA_GET_BE16(data + 2);
|
||||
data += 4;
|
||||
data_len -= 4;
|
||||
|
||||
if (elen > data_len)
|
||||
return 0;
|
||||
|
||||
if (id == 0x1023) {
|
||||
/* Model name, like 'Nexus 7' */
|
||||
size_t n = (elen < name_len) ? elen : name_len;
|
||||
no_mischief_strncpy(name, (const char *) data, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
data += elen;
|
||||
data_len -= elen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
|
||||
{
|
||||
char *fpos = fstr;
|
||||
char *fend = fstr + fstr_len;
|
||||
char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
|
||||
char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
|
||||
char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
|
||||
char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
|
||||
char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
|
||||
char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
|
||||
#define MAX_EXTCAP 254
|
||||
char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
|
||||
*/
|
||||
char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
|
||||
#define WPS_NAME_LEN 32
|
||||
char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
|
||||
* NUL */
|
||||
int num = 0;
|
||||
const u8 *ie;
|
||||
size_t ie_len;
|
||||
int ret;
|
||||
|
||||
os_memset(htcap, 0, sizeof(htcap));
|
||||
os_memset(htagg, 0, sizeof(htagg));
|
||||
os_memset(htmcs, 0, sizeof(htmcs));
|
||||
os_memset(vhtcap, 0, sizeof(vhtcap));
|
||||
os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
|
||||
os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
|
||||
os_memset(extcap, 0, sizeof(extcap));
|
||||
os_memset(txpow, 0, sizeof(txpow));
|
||||
os_memset(wps, 0, sizeof(wps));
|
||||
*fpos = '\0';
|
||||
|
||||
if (!ies)
|
||||
return;
|
||||
ie = wpabuf_head(ies);
|
||||
ie_len = wpabuf_len(ies);
|
||||
|
||||
while (ie_len >= 2) {
|
||||
u8 id, elen;
|
||||
char *sep = (num++ == 0) ? "" : ",";
|
||||
|
||||
id = *ie++;
|
||||
elen = *ie++;
|
||||
ie_len -= 2;
|
||||
|
||||
if (elen > ie_len)
|
||||
break;
|
||||
|
||||
if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
|
||||
/* Vendor specific */
|
||||
if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
|
||||
/* WPS */
|
||||
char model_name[WPS_NAME_LEN + 1];
|
||||
const u8 *data = &ie[4];
|
||||
size_t data_len = elen - 4;
|
||||
|
||||
os_memset(model_name, 0, sizeof(model_name));
|
||||
if (get_wps_name(model_name, WPS_NAME_LEN, data,
|
||||
data_len)) {
|
||||
os_snprintf(wps, sizeof(wps),
|
||||
",wps:%s", model_name);
|
||||
}
|
||||
}
|
||||
|
||||
ret = os_snprintf(fpos, fend - fpos,
|
||||
"%s%d(%02x%02x%02x,%d)",
|
||||
sep, id, ie[0], ie[1], ie[2], ie[3]);
|
||||
} else {
|
||||
if (id == WLAN_EID_HT_CAP && elen >= 2) {
|
||||
/* HT Capabilities (802.11n) */
|
||||
os_snprintf(htcap, sizeof(htcap),
|
||||
",htcap:%04hx",
|
||||
WPA_GET_LE16(ie));
|
||||
}
|
||||
if (id == WLAN_EID_HT_CAP && elen >= 3) {
|
||||
/* HT Capabilities (802.11n), A-MPDU information
|
||||
*/
|
||||
os_snprintf(htagg, sizeof(htagg),
|
||||
",htagg:%02hx", (u16) ie[2]);
|
||||
}
|
||||
if (id == WLAN_EID_HT_CAP && elen >= 7) {
|
||||
/* HT Capabilities (802.11n), MCS information */
|
||||
os_snprintf(htmcs, sizeof(htmcs),
|
||||
",htmcs:%08hx",
|
||||
(u16) WPA_GET_LE32(ie + 3));
|
||||
}
|
||||
if (id == WLAN_EID_VHT_CAP && elen >= 4) {
|
||||
/* VHT Capabilities (802.11ac) */
|
||||
os_snprintf(vhtcap, sizeof(vhtcap),
|
||||
",vhtcap:%08x",
|
||||
WPA_GET_LE32(ie));
|
||||
}
|
||||
if (id == WLAN_EID_VHT_CAP && elen >= 8) {
|
||||
/* VHT Capabilities (802.11ac), RX MCS
|
||||
* information */
|
||||
os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
|
||||
",vhtrxmcs:%08x",
|
||||
WPA_GET_LE32(ie + 4));
|
||||
}
|
||||
if (id == WLAN_EID_VHT_CAP && elen >= 12) {
|
||||
/* VHT Capabilities (802.11ac), TX MCS
|
||||
* information */
|
||||
os_snprintf(vhttxmcs, sizeof(vhttxmcs),
|
||||
",vhttxmcs:%08x",
|
||||
WPA_GET_LE32(ie + 8));
|
||||
}
|
||||
if (id == WLAN_EID_EXT_CAPAB) {
|
||||
/* Extended Capabilities */
|
||||
int i;
|
||||
int len = (elen < MAX_EXTCAP) ? elen :
|
||||
MAX_EXTCAP;
|
||||
char *p = extcap;
|
||||
|
||||
p += os_snprintf(extcap, sizeof(extcap),
|
||||
",extcap:");
|
||||
for (i = 0; i < len; i++) {
|
||||
int lim;
|
||||
|
||||
lim = sizeof(extcap) -
|
||||
os_strlen(extcap);
|
||||
if (lim <= 0)
|
||||
break;
|
||||
p += os_snprintf(p, lim, "%02x",
|
||||
*(ie + i));
|
||||
}
|
||||
}
|
||||
if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
|
||||
/* TX Power */
|
||||
os_snprintf(txpow, sizeof(txpow),
|
||||
",txpow:%04hx",
|
||||
WPA_GET_LE16(ie));
|
||||
}
|
||||
|
||||
ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
|
||||
}
|
||||
if (os_snprintf_error(fend - fpos, ret))
|
||||
goto fail;
|
||||
fpos += ret;
|
||||
|
||||
ie += elen;
|
||||
ie_len -= elen;
|
||||
}
|
||||
|
||||
ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
|
||||
htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
|
||||
txpow, extcap, wps);
|
||||
if (os_snprintf_error(fend - fpos, ret)) {
|
||||
fail:
|
||||
fstr[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta, char *buf, size_t buflen)
|
||||
{
|
||||
int ret;
|
||||
char *pos, *end;
|
||||
|
||||
if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
|
||||
return 0;
|
||||
|
||||
ret = os_snprintf(buf, buflen, "wifi4|probe:");
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
return 0;
|
||||
pos = buf + ret;
|
||||
end = buf + buflen;
|
||||
|
||||
ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
|
||||
pos = os_strchr(pos, '\0');
|
||||
if (pos >= end)
|
||||
return 0;
|
||||
ret = os_snprintf(pos, end - pos, "|assoc:");
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return 0;
|
||||
pos += ret;
|
||||
ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
|
||||
pos = os_strchr(pos, '\0');
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
wpabuf_free(sta->probe_ie_taxonomy);
|
||||
sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
|
||||
}
|
||||
|
||||
|
||||
void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct hostapd_sta_info *info,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
wpabuf_free(info->probe_ie_taxonomy);
|
||||
info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
|
||||
}
|
||||
|
||||
|
||||
void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
wpabuf_free(sta->assoc_ie_taxonomy);
|
||||
sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
|
||||
}
|
||||
24
src/ap/taxonomy.h
Normal file
24
src/ap/taxonomy.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* hostapd / Station client taxonomy
|
||||
* Copyright (c) 2015 Google, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef TAXONOMY_H
|
||||
#define TAXONOMY_H
|
||||
|
||||
void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len);
|
||||
void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct hostapd_sta_info *sta,
|
||||
const u8 *ie, size_t ie_len);
|
||||
void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len);
|
||||
int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta, char *buf, size_t buflen);
|
||||
|
||||
#endif /* TAXONOMY_H */
|
||||
110
src/ap/tkip_countermeasures.c
Normal file
110
src/ap/tkip_countermeasures.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* hostapd / TKIP countermeasures
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "radius/radius.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_mlme.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "tkip_countermeasures.h"
|
||||
|
||||
|
||||
static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
|
||||
void *timeout_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
hapd->tkip_countermeasures = 0;
|
||||
hostapd_drv_set_countermeasures(hapd, 0);
|
||||
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
|
||||
|
||||
wpa_auth_countermeasures_start(hapd->wpa_auth);
|
||||
hapd->tkip_countermeasures = 1;
|
||||
hostapd_drv_set_countermeasures(hapd, 1);
|
||||
wpa_gtk_rekey(hapd->wpa_auth);
|
||||
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
|
||||
eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
|
||||
hapd, NULL);
|
||||
while ((sta = hapd->sta_list)) {
|
||||
sta->acct_terminate_cause =
|
||||
RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
|
||||
if (sta->flags & WLAN_STA_AUTH) {
|
||||
mlme_deauthenticate_indication(
|
||||
hapd, sta,
|
||||
WLAN_REASON_MICHAEL_MIC_FAILURE);
|
||||
}
|
||||
hostapd_drv_sta_deauth(hapd, sta->addr,
|
||||
WLAN_REASON_MICHAEL_MIC_FAILURE);
|
||||
ap_free_sta(hapd, sta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
|
||||
}
|
||||
|
||||
|
||||
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
|
||||
{
|
||||
struct os_reltime now;
|
||||
int ret = 0;
|
||||
|
||||
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Michael MIC failure detected in received frame%s",
|
||||
local ? " (local)" : "");
|
||||
|
||||
if (addr && local) {
|
||||
struct sta_info *sta = ap_get_sta(hapd, addr);
|
||||
if (sta != NULL) {
|
||||
wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
|
||||
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Michael MIC failure detected in "
|
||||
"received frame");
|
||||
mlme_michaelmicfailure_indication(hapd, addr);
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MLME-MICHAELMICFAILURE.indication "
|
||||
"for not associated STA (" MACSTR
|
||||
") ignored", MAC2STR(addr));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
os_get_reltime(&now);
|
||||
if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
|
||||
hapd->michael_mic_failures = 1;
|
||||
} else {
|
||||
hapd->michael_mic_failures++;
|
||||
if (hapd->michael_mic_failures > 1) {
|
||||
ieee80211_tkip_countermeasures_start(hapd);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
hapd->michael_mic_failure = now;
|
||||
|
||||
return ret;
|
||||
}
|
||||
15
src/ap/tkip_countermeasures.h
Normal file
15
src/ap/tkip_countermeasures.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* hostapd / TKIP countermeasures
|
||||
* 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 TKIP_COUNTERMEASURES_H
|
||||
#define TKIP_COUNTERMEASURES_H
|
||||
|
||||
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
|
||||
void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* TKIP_COUNTERMEASURES_H */
|
||||
112
src/ap/utils.c
Normal file
112
src/ap/utils.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* AP mode helper functions
|
||||
* Copyright (c) 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 "common/ieee802_11_defs.h"
|
||||
#include "fst/fst.h"
|
||||
#include "sta_info.h"
|
||||
#include "hostapd.h"
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
struct hostapd_probereq_cb *n;
|
||||
|
||||
n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
|
||||
sizeof(struct hostapd_probereq_cb));
|
||||
if (n == NULL)
|
||||
return -1;
|
||||
|
||||
hapd->probereq_cb = n;
|
||||
n = &hapd->probereq_cb[hapd->num_probereq_cb];
|
||||
hapd->num_probereq_cb++;
|
||||
|
||||
n->cb = cb;
|
||||
n->ctx = ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct prune_data {
|
||||
struct hostapd_data *hapd;
|
||||
const u8 *addr;
|
||||
int mld_assoc_link_id;
|
||||
};
|
||||
|
||||
static int prune_associations(struct hostapd_iface *iface, void *ctx)
|
||||
{
|
||||
struct prune_data *data = ctx;
|
||||
struct sta_info *osta;
|
||||
struct hostapd_data *ohapd;
|
||||
size_t j;
|
||||
|
||||
for (j = 0; j < iface->num_bss; j++) {
|
||||
ohapd = iface->bss[j];
|
||||
if (ohapd == data->hapd)
|
||||
continue;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
if (ohapd->conf->skip_prune_assoc)
|
||||
continue;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
#ifdef CONFIG_FST
|
||||
/* Don't prune STAs belong to same FST */
|
||||
if (ohapd->iface->fst &&
|
||||
data->hapd->iface->fst &&
|
||||
fst_are_ifaces_aggregated(ohapd->iface->fst,
|
||||
data->hapd->iface->fst))
|
||||
continue;
|
||||
#endif /* CONFIG_FST */
|
||||
osta = ap_get_sta(ohapd, data->addr);
|
||||
if (!osta)
|
||||
continue;
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (data->mld_assoc_link_id >= 0 &&
|
||||
osta->mld_assoc_link_id == data->mld_assoc_link_id)
|
||||
continue;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
|
||||
ohapd->conf->iface, MAC2STR(osta->addr));
|
||||
ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hostapd_prune_associations - Remove extraneous associations
|
||||
* @hapd: Pointer to BSS data for the most recent association
|
||||
* @addr: Associated STA address
|
||||
* @mld_assoc_link_id: MLD link id used for association or -1 for non MLO
|
||||
*
|
||||
* This function looks through all radios and BSS's for previous
|
||||
* (stale) associations of STA. If any are found they are removed.
|
||||
*/
|
||||
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
|
||||
int mld_assoc_link_id)
|
||||
{
|
||||
struct prune_data data;
|
||||
|
||||
data.hapd = hapd;
|
||||
data.addr = addr;
|
||||
data.mld_assoc_link_id = mld_assoc_link_id;
|
||||
|
||||
if (hapd->iface->interfaces &&
|
||||
hapd->iface->interfaces->for_each_interface)
|
||||
hapd->iface->interfaces->for_each_interface(
|
||||
hapd->iface->interfaces, prune_associations, &data);
|
||||
}
|
||||
34
src/ap/vlan.c
Normal file
34
src/ap/vlan.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* hostapd / VLAN definition
|
||||
* 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 "ap/vlan.h"
|
||||
|
||||
/* compare the two arguments, NULL is treated as empty
|
||||
* return zero iff they are equal
|
||||
*/
|
||||
int vlan_compare(struct vlan_description *a, struct vlan_description *b)
|
||||
{
|
||||
int i;
|
||||
const int a_empty = !a || !a->notempty;
|
||||
const int b_empty = !b || !b->notempty;
|
||||
|
||||
if (a_empty && b_empty)
|
||||
return 0;
|
||||
if (a_empty || b_empty)
|
||||
return 1;
|
||||
if (a->untagged != b->untagged)
|
||||
return 1;
|
||||
for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
|
||||
if (a->tagged[i] != b->tagged[i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
30
src/ap/vlan.h
Normal file
30
src/ap/vlan.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* hostapd / VLAN definition
|
||||
* Copyright (c) 2015, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef VLAN_H
|
||||
#define VLAN_H
|
||||
|
||||
#define MAX_NUM_TAGGED_VLAN 32
|
||||
|
||||
struct vlan_description {
|
||||
int notempty; /* 0 : no vlan information present, 1: else */
|
||||
int untagged; /* >0 802.1q vid */
|
||||
int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
|
||||
};
|
||||
|
||||
#ifndef CONFIG_NO_VLAN
|
||||
int vlan_compare(struct vlan_description *a, struct vlan_description *b);
|
||||
#else /* CONFIG_NO_VLAN */
|
||||
static inline int
|
||||
vlan_compare(struct vlan_description *a, struct vlan_description *b)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NO_VLAN */
|
||||
|
||||
#endif /* VLAN_H */
|
||||
801
src/ap/vlan_full.c
Normal file
801
src/ap/vlan_full.c
Normal file
@@ -0,0 +1,801 @@
|
||||
/*
|
||||
* hostapd / VLAN initialization - full dynamic VLAN
|
||||
* Copyright 2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 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 <net/if.h>
|
||||
/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
|
||||
#undef if_type
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "drivers/priv_netlink.h"
|
||||
#include "drivers/linux_ioctl.h"
|
||||
#include "common/linux_bridge.h"
|
||||
#include "common/linux_vlan.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "vlan_init.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
|
||||
struct full_dynamic_vlan {
|
||||
int s; /* socket on which to listen for new/removed interfaces. */
|
||||
};
|
||||
|
||||
#define DVLAN_CLEAN_BR 0x1
|
||||
#define DVLAN_CLEAN_VLAN 0x2
|
||||
#define DVLAN_CLEAN_VLAN_PORT 0x4
|
||||
|
||||
struct dynamic_iface {
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
int usage;
|
||||
int clean;
|
||||
struct dynamic_iface *next;
|
||||
};
|
||||
|
||||
|
||||
/* Increment ref counter for ifname and add clean flag.
|
||||
* If not in list, add it only if some flags are given.
|
||||
*/
|
||||
static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
|
||||
int clean)
|
||||
{
|
||||
struct dynamic_iface *next, **dynamic_ifaces;
|
||||
struct hapd_interfaces *interfaces;
|
||||
|
||||
interfaces = hapd->iface->interfaces;
|
||||
dynamic_ifaces = &interfaces->vlan_priv;
|
||||
|
||||
for (next = *dynamic_ifaces; next; next = next->next) {
|
||||
if (os_strcmp(ifname, next->ifname) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
next->usage++;
|
||||
next->clean |= clean;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clean)
|
||||
return;
|
||||
|
||||
next = os_zalloc(sizeof(*next));
|
||||
if (!next)
|
||||
return;
|
||||
os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
|
||||
next->usage = 1;
|
||||
next->clean = clean;
|
||||
next->next = *dynamic_ifaces;
|
||||
*dynamic_ifaces = next;
|
||||
}
|
||||
|
||||
|
||||
/* Decrement reference counter for given ifname.
|
||||
* Return clean flag iff reference counter was decreased to zero, else zero
|
||||
*/
|
||||
static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
|
||||
struct hapd_interfaces *interfaces;
|
||||
int clean;
|
||||
|
||||
interfaces = hapd->iface->interfaces;
|
||||
dynamic_ifaces = &interfaces->vlan_priv;
|
||||
|
||||
for (next = *dynamic_ifaces; next; next = next->next) {
|
||||
if (os_strcmp(ifname, next->ifname) == 0)
|
||||
break;
|
||||
prev = next;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return 0;
|
||||
|
||||
next->usage--;
|
||||
if (next->usage)
|
||||
return 0;
|
||||
|
||||
if (prev)
|
||||
prev->next = next->next;
|
||||
else
|
||||
*dynamic_ifaces = next->next;
|
||||
clean = next->clean;
|
||||
os_free(next);
|
||||
|
||||
return clean;
|
||||
}
|
||||
|
||||
|
||||
static int ifconfig_down(const char *if_name)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
|
||||
return ifconfig_helper(if_name, 0);
|
||||
}
|
||||
|
||||
|
||||
/* This value should be 256 ONLY. If it is something else, then hostapd
|
||||
* might crash!, as this value has been hard-coded in 2.4.x kernel
|
||||
* bridging code.
|
||||
*/
|
||||
#define MAX_BR_PORTS 256
|
||||
|
||||
static int br_delif(const char *br_name, const char *if_name)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
unsigned long args[2];
|
||||
int if_index;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_del_if(fd, br_name, if_name) == 0)
|
||||
goto done;
|
||||
|
||||
if_index = if_nametoindex(if_name);
|
||||
|
||||
if (if_index == 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
|
||||
"interface index for '%s'",
|
||||
__func__, if_name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args[0] = BRCTL_DEL_IF;
|
||||
args[1] = if_index;
|
||||
|
||||
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (void *) args;
|
||||
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
|
||||
/* No error if interface already removed. */
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
|
||||
"BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
|
||||
"%s", __func__, br_name, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add interface 'if_name' to the bridge 'br_name'
|
||||
|
||||
returns -1 on error
|
||||
returns 1 if the interface is already part of the bridge
|
||||
returns 0 otherwise
|
||||
*/
|
||||
static int br_addif(const char *br_name, const char *if_name)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
unsigned long args[2];
|
||||
int if_index;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_add_if(fd, br_name, if_name) == 0)
|
||||
goto done;
|
||||
if (errno == EBUSY) {
|
||||
/* The interface is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if_index = if_nametoindex(if_name);
|
||||
|
||||
if (if_index == 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
|
||||
"interface index for '%s'",
|
||||
__func__, if_name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args[0] = BRCTL_ADD_IF;
|
||||
args[1] = if_index;
|
||||
|
||||
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (void *) args;
|
||||
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
||||
if (errno == EBUSY) {
|
||||
/* The interface is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
|
||||
"BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
|
||||
"%s", __func__, br_name, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int br_delbr(const char *br_name)
|
||||
{
|
||||
int fd;
|
||||
unsigned long arg[2];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_del(fd, br_name) == 0)
|
||||
goto done;
|
||||
|
||||
arg[0] = BRCTL_DEL_BRIDGE;
|
||||
arg[1] = (unsigned long) br_name;
|
||||
|
||||
if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
|
||||
/* No error if bridge already removed. */
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
|
||||
"%s: %s", __func__, br_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add a bridge with the name 'br_name'.
|
||||
|
||||
returns -1 on error
|
||||
returns 1 if the bridge already exists
|
||||
returns 0 otherwise
|
||||
*/
|
||||
static int br_addbr(const char *br_name)
|
||||
{
|
||||
int fd;
|
||||
unsigned long arg[4];
|
||||
struct ifreq ifr;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_add(fd, br_name) == 0)
|
||||
goto done;
|
||||
if (errno == EEXIST) {
|
||||
/* The bridge is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
arg[0] = BRCTL_ADD_BRIDGE;
|
||||
arg[1] = (unsigned long) br_name;
|
||||
|
||||
if (ioctl(fd, SIOCGIFBR, arg) < 0) {
|
||||
if (errno == EEXIST) {
|
||||
/* The bridge is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
|
||||
"failed for %s: %s",
|
||||
__func__, br_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
/* Decrease forwarding delay to avoid EAPOL timeouts. */
|
||||
os_memset(&ifr, 0, sizeof(ifr));
|
||||
os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
|
||||
arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
|
||||
arg[1] = 1;
|
||||
arg[2] = 0;
|
||||
arg[3] = 0;
|
||||
ifr.ifr_data = (char *) &arg;
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: "
|
||||
"BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
|
||||
"%s: %s", __func__, br_name, strerror(errno));
|
||||
/* Continue anyway */
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int br_getnumports(const char *br_name)
|
||||
{
|
||||
int fd;
|
||||
int i;
|
||||
int port_cnt = 0;
|
||||
unsigned long arg[4];
|
||||
int ifindices[MAX_BR_PORTS];
|
||||
struct ifreq ifr;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
arg[0] = BRCTL_GET_PORT_LIST;
|
||||
arg[1] = (unsigned long) ifindices;
|
||||
arg[2] = MAX_BR_PORTS;
|
||||
arg[3] = 0;
|
||||
|
||||
os_memset(ifindices, 0, sizeof(ifindices));
|
||||
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (void *) arg;
|
||||
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
|
||||
"failed for %s: %s",
|
||||
__func__, br_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 1; i < MAX_BR_PORTS; i++) {
|
||||
if (ifindices[i] > 0) {
|
||||
port_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return port_cnt;
|
||||
}
|
||||
|
||||
|
||||
static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
|
||||
const char *br_name, int vid,
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
char vlan_ifname[IFNAMSIZ];
|
||||
int clean;
|
||||
int ret;
|
||||
|
||||
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
|
||||
tagged_interface, vid);
|
||||
else
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
|
||||
vid);
|
||||
if (ret >= (int) sizeof(vlan_ifname))
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
vlan_ifname);
|
||||
|
||||
clean = 0;
|
||||
ifconfig_up(tagged_interface);
|
||||
if (!vlan_add(tagged_interface, vid, vlan_ifname))
|
||||
clean |= DVLAN_CLEAN_VLAN;
|
||||
|
||||
if (!br_addif(br_name, vlan_ifname))
|
||||
clean |= DVLAN_CLEAN_VLAN_PORT;
|
||||
|
||||
dyn_iface_get(hapd, vlan_ifname, clean);
|
||||
|
||||
ifconfig_up(vlan_ifname);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
|
||||
struct hostapd_vlan *vlan, int vid)
|
||||
{
|
||||
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
||||
int ret;
|
||||
|
||||
if (vlan->bridge[0]) {
|
||||
os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
|
||||
ret = 0;
|
||||
} else if (hapd->conf->vlan_bridge[0]) {
|
||||
ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
|
||||
hapd->conf->vlan_bridge, vid);
|
||||
} else if (tagged_interface) {
|
||||
ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
|
||||
tagged_interface, vid);
|
||||
} else {
|
||||
ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
|
||||
}
|
||||
if (ret >= IFNAMSIZ)
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
br_name);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
|
||||
int vid)
|
||||
{
|
||||
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
||||
int vlan_naming = hapd->conf->ssid.vlan_naming;
|
||||
|
||||
dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
|
||||
|
||||
ifconfig_up(br_name);
|
||||
|
||||
if (tagged_interface)
|
||||
vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
|
||||
vid, hapd);
|
||||
}
|
||||
|
||||
|
||||
void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
|
||||
{
|
||||
char br_name[IFNAMSIZ];
|
||||
struct hostapd_vlan *vlan;
|
||||
int untagged, *tagged, i, notempty;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
|
||||
|
||||
for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
|
||||
if (vlan->configured ||
|
||||
os_strcmp(ifname, vlan->ifname) != 0)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (!vlan)
|
||||
return;
|
||||
|
||||
vlan->configured = 1;
|
||||
|
||||
notempty = vlan->vlan_desc.notempty;
|
||||
untagged = vlan->vlan_desc.untagged;
|
||||
tagged = vlan->vlan_desc.tagged;
|
||||
|
||||
if (!notempty) {
|
||||
/* Non-VLAN STA */
|
||||
if (hapd->conf->bridge[0] &&
|
||||
!br_addif(hapd->conf->bridge, ifname))
|
||||
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
|
||||
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
|
||||
vlan_bridge_name(br_name, hapd, vlan, untagged);
|
||||
|
||||
vlan_get_bridge(br_name, hapd, untagged);
|
||||
|
||||
if (!br_addif(br_name, ifname))
|
||||
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
|
||||
if (tagged[i] == untagged ||
|
||||
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
|
||||
(i > 0 && tagged[i] == tagged[i - 1]))
|
||||
continue;
|
||||
vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
|
||||
vlan_get_bridge(br_name, hapd, tagged[i]);
|
||||
vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
|
||||
ifname, br_name, tagged[i], hapd);
|
||||
}
|
||||
|
||||
ifconfig_up(ifname);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
|
||||
const char *br_name, int vid,
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
char vlan_ifname[IFNAMSIZ];
|
||||
int clean;
|
||||
int ret;
|
||||
|
||||
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
|
||||
tagged_interface, vid);
|
||||
else
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
|
||||
vid);
|
||||
if (ret >= (int) sizeof(vlan_ifname))
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
vlan_ifname);
|
||||
|
||||
|
||||
clean = dyn_iface_put(hapd, vlan_ifname);
|
||||
|
||||
if (clean & DVLAN_CLEAN_VLAN_PORT)
|
||||
br_delif(br_name, vlan_ifname);
|
||||
|
||||
if (clean & DVLAN_CLEAN_VLAN) {
|
||||
ifconfig_down(vlan_ifname);
|
||||
vlan_rem(vlan_ifname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
|
||||
int vid)
|
||||
{
|
||||
int clean;
|
||||
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
||||
int vlan_naming = hapd->conf->ssid.vlan_naming;
|
||||
|
||||
if (tagged_interface)
|
||||
vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
|
||||
vid, hapd);
|
||||
|
||||
clean = dyn_iface_put(hapd, br_name);
|
||||
if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
|
||||
ifconfig_down(br_name);
|
||||
br_delbr(br_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
|
||||
{
|
||||
struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
|
||||
|
||||
first = prev = vlan;
|
||||
|
||||
while (vlan) {
|
||||
if (os_strcmp(ifname, vlan->ifname) != 0) {
|
||||
prev = vlan;
|
||||
vlan = vlan->next;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!vlan)
|
||||
return;
|
||||
|
||||
if (vlan->configured) {
|
||||
int notempty = vlan->vlan_desc.notempty;
|
||||
int untagged = vlan->vlan_desc.untagged;
|
||||
int *tagged = vlan->vlan_desc.tagged;
|
||||
char br_name[IFNAMSIZ];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
|
||||
if (tagged[i] == untagged ||
|
||||
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
|
||||
(i > 0 && tagged[i] == tagged[i - 1]))
|
||||
continue;
|
||||
vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
|
||||
vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
|
||||
ifname, br_name, tagged[i], hapd);
|
||||
vlan_put_bridge(br_name, hapd, tagged[i]);
|
||||
}
|
||||
|
||||
if (!notempty) {
|
||||
/* Non-VLAN STA */
|
||||
if (hapd->conf->bridge[0] &&
|
||||
(vlan->clean & DVLAN_CLEAN_WLAN_PORT))
|
||||
br_delif(hapd->conf->bridge, ifname);
|
||||
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
|
||||
vlan_bridge_name(br_name, hapd, vlan, untagged);
|
||||
|
||||
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
|
||||
br_delif(br_name, vlan->ifname);
|
||||
|
||||
vlan_put_bridge(br_name, hapd, untagged);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure this VLAN interface is actually removed even if
|
||||
* NEWLINK message is only received later.
|
||||
*/
|
||||
if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
|
||||
wpa_printf(MSG_ERROR,
|
||||
"VLAN: Could not remove VLAN iface: %s: %s",
|
||||
vlan->ifname, strerror(errno));
|
||||
|
||||
if (vlan == first)
|
||||
hapd->conf->vlan = vlan->next;
|
||||
else
|
||||
prev->next = vlan->next;
|
||||
|
||||
os_free(vlan);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
struct ifinfomsg *ifi;
|
||||
int attrlen, nlmsg_len, rta_len;
|
||||
struct rtattr *attr;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
|
||||
if (len < sizeof(*ifi))
|
||||
return;
|
||||
|
||||
ifi = NLMSG_DATA(h);
|
||||
|
||||
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
||||
|
||||
attrlen = h->nlmsg_len - nlmsg_len;
|
||||
if (attrlen < 0)
|
||||
return;
|
||||
|
||||
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
|
||||
|
||||
os_memset(ifname, 0, sizeof(ifname));
|
||||
rta_len = RTA_ALIGN(sizeof(struct rtattr));
|
||||
while (RTA_OK(attr, attrlen)) {
|
||||
if (attr->rta_type == IFLA_IFNAME) {
|
||||
int n = attr->rta_len - rta_len;
|
||||
if (n < 0)
|
||||
break;
|
||||
|
||||
if ((size_t) n >= sizeof(ifname))
|
||||
n = sizeof(ifname) - 1;
|
||||
os_memcpy(ifname, ((char *) attr) + rta_len, n);
|
||||
|
||||
}
|
||||
|
||||
attr = RTA_NEXT(attr, attrlen);
|
||||
}
|
||||
|
||||
if (!ifname[0])
|
||||
return;
|
||||
if (del && if_nametoindex(ifname)) {
|
||||
/* interface still exists, race condition ->
|
||||
* iface has just been recreated */
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
|
||||
del ? "DEL" : "NEW",
|
||||
ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
|
||||
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
|
||||
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
|
||||
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
|
||||
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
|
||||
|
||||
if (del)
|
||||
vlan_dellink(ifname, hapd);
|
||||
else
|
||||
vlan_newlink(ifname, hapd);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
{
|
||||
char buf[8192];
|
||||
int left;
|
||||
struct sockaddr_nl from;
|
||||
socklen_t fromlen;
|
||||
struct nlmsghdr *h;
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
|
||||
fromlen = sizeof(from);
|
||||
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
|
||||
(struct sockaddr *) &from, &fromlen);
|
||||
if (left < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
|
||||
__func__, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
h = (struct nlmsghdr *) buf;
|
||||
while (NLMSG_OK(h, left)) {
|
||||
int len, plen;
|
||||
|
||||
len = h->nlmsg_len;
|
||||
plen = len - sizeof(*h);
|
||||
if (len > left || plen < 0) {
|
||||
wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
|
||||
"message: len=%d left=%d plen=%d",
|
||||
len, left, plen);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (h->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
vlan_read_ifnames(h, plen, 0, hapd);
|
||||
break;
|
||||
case RTM_DELLINK:
|
||||
vlan_read_ifnames(h, plen, 1, hapd);
|
||||
break;
|
||||
}
|
||||
|
||||
h = NLMSG_NEXT(h, left);
|
||||
}
|
||||
|
||||
if (left > 0) {
|
||||
wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
|
||||
"netlink message", __func__, left);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct full_dynamic_vlan *
|
||||
full_dynamic_vlan_init(struct hostapd_data *hapd)
|
||||
{
|
||||
struct sockaddr_nl local;
|
||||
struct full_dynamic_vlan *priv;
|
||||
|
||||
priv = os_zalloc(sizeof(*priv));
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
|
||||
vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
|
||||
DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
|
||||
VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
|
||||
VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
|
||||
|
||||
priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (priv->s < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
|
||||
"NETLINK_ROUTE) failed: %s",
|
||||
__func__, strerror(errno));
|
||||
os_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = RTMGRP_LINK;
|
||||
if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
|
||||
__func__, strerror(errno));
|
||||
close(priv->s);
|
||||
os_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
|
||||
{
|
||||
close(priv->s);
|
||||
os_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
||||
|
||||
void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
|
||||
{
|
||||
if (priv == NULL)
|
||||
return;
|
||||
eloop_unregister_read_sock(priv->s);
|
||||
close(priv->s);
|
||||
os_free(priv);
|
||||
}
|
||||
69
src/ap/vlan_ifconfig.c
Normal file
69
src/ap/vlan_ifconfig.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* hostapd / VLAN ifconfig helpers
|
||||
* Copyright 2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 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 <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
|
||||
int ifconfig_helper(const char *if_name, int up)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memset(&ifr, 0, sizeof(ifr));
|
||||
os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
|
||||
|
||||
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
|
||||
"for interface %s: %s",
|
||||
__func__, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (up)
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
else
|
||||
ifr.ifr_flags &= ~IFF_UP;
|
||||
|
||||
if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
|
||||
"for interface %s (up=%d): %s",
|
||||
__func__, if_name, up, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ifconfig_up(const char *if_name)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
|
||||
return ifconfig_helper(if_name, 1);
|
||||
}
|
||||
|
||||
|
||||
int iface_exists(const char *ifname)
|
||||
{
|
||||
return if_nametoindex(ifname);
|
||||
}
|
||||
267
src/ap/vlan_init.c
Normal file
267
src/ap/vlan_init.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* hostapd / VLAN initialization
|
||||
* Copyright 2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 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 "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "vlan_init.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
|
||||
static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
|
||||
int existsok)
|
||||
{
|
||||
int ret;
|
||||
#ifdef CONFIG_WEP
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_WEP_KEYS; i++) {
|
||||
if (!hapd->conf->ssid.wep.key[i])
|
||||
continue;
|
||||
wpa_printf(MSG_ERROR,
|
||||
"VLAN: Refusing to set up VLAN iface %s with WEP",
|
||||
vlan->ifname);
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_WEP */
|
||||
|
||||
if (!iface_exists(vlan->ifname))
|
||||
ret = hostapd_vlan_if_add(hapd, vlan->ifname);
|
||||
else if (!existsok)
|
||||
return -1;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
|
||||
|
||||
if (hapd->wpa_auth)
|
||||
ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
|
||||
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
|
||||
wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
|
||||
vlan->vlan_id, ret);
|
||||
if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
|
||||
wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
|
||||
|
||||
/* group state machine setup failed */
|
||||
if (hostapd_vlan_if_remove(hapd, vlan->ifname))
|
||||
wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
|
||||
if (ret)
|
||||
wpa_printf(MSG_ERROR,
|
||||
"WPA deinitialization for VLAN %d failed (%d)",
|
||||
vlan->vlan_id, ret);
|
||||
|
||||
return hostapd_vlan_if_remove(hapd, vlan->ifname);
|
||||
}
|
||||
|
||||
|
||||
static int vlan_dynamic_add(struct hostapd_data *hapd,
|
||||
struct hostapd_vlan *vlan)
|
||||
{
|
||||
while (vlan) {
|
||||
if (vlan->vlan_id != VLAN_ID_WILDCARD) {
|
||||
if (vlan_if_add(hapd, vlan, 1)) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"VLAN: Could not add VLAN %s: %s",
|
||||
vlan->ifname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||
vlan_newlink(vlan->ifname, hapd);
|
||||
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
}
|
||||
|
||||
vlan = vlan->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void vlan_dynamic_remove(struct hostapd_data *hapd,
|
||||
struct hostapd_vlan *vlan)
|
||||
{
|
||||
struct hostapd_vlan *next;
|
||||
|
||||
while (vlan) {
|
||||
next = vlan->next;
|
||||
|
||||
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||
/* vlan_dellink() takes care of cleanup and interface removal */
|
||||
if (vlan->vlan_id != VLAN_ID_WILDCARD)
|
||||
vlan_dellink(vlan->ifname, hapd);
|
||||
#else /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
if (vlan->vlan_id != VLAN_ID_WILDCARD &&
|
||||
vlan_if_remove(hapd, vlan)) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
|
||||
"iface: %s: %s",
|
||||
vlan->ifname, strerror(errno));
|
||||
}
|
||||
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
|
||||
vlan = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int vlan_init(struct hostapd_data *hapd)
|
||||
{
|
||||
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||
hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
|
||||
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
|
||||
if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
|
||||
hapd->conf->ssid.per_sta_vif) &&
|
||||
!hapd->conf->vlan) {
|
||||
/* dynamic vlans enabled but no (or empty) vlan_file given */
|
||||
struct hostapd_vlan *vlan;
|
||||
int ret;
|
||||
|
||||
vlan = os_zalloc(sizeof(*vlan));
|
||||
if (vlan == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Out of memory while assigning "
|
||||
"VLAN interfaces");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vlan->vlan_id = VLAN_ID_WILDCARD;
|
||||
ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
|
||||
hapd->conf->iface);
|
||||
if (ret >= (int) sizeof(vlan->ifname)) {
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
vlan->ifname);
|
||||
} else if (ret < 0) {
|
||||
os_free(vlan);
|
||||
return ret;
|
||||
}
|
||||
vlan->next = hapd->conf->vlan;
|
||||
hapd->conf->vlan = vlan;
|
||||
}
|
||||
|
||||
if (vlan_dynamic_add(hapd, hapd->conf->vlan))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void vlan_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
vlan_dynamic_remove(hapd, hapd->conf->vlan);
|
||||
|
||||
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||
full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
|
||||
hapd->full_dynamic_vlan = NULL;
|
||||
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
}
|
||||
|
||||
|
||||
struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
|
||||
struct hostapd_vlan *vlan,
|
||||
int vlan_id,
|
||||
struct vlan_description *vlan_desc)
|
||||
{
|
||||
struct hostapd_vlan *n;
|
||||
char ifname[IFNAMSIZ + 1], *pos;
|
||||
int ret;
|
||||
|
||||
if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
|
||||
return NULL;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
|
||||
__func__, vlan_id, vlan->ifname);
|
||||
os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
|
||||
pos = os_strchr(ifname, '#');
|
||||
if (pos == NULL)
|
||||
return NULL;
|
||||
*pos++ = '\0';
|
||||
|
||||
n = os_zalloc(sizeof(*n));
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
|
||||
n->vlan_id = vlan_id;
|
||||
if (vlan_desc)
|
||||
n->vlan_desc = *vlan_desc;
|
||||
n->dynamic_vlan = 1;
|
||||
|
||||
ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
|
||||
ifname, vlan_id, pos);
|
||||
if (os_snprintf_error(sizeof(n->ifname), ret)) {
|
||||
os_free(n);
|
||||
return NULL;
|
||||
}
|
||||
os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
|
||||
|
||||
n->next = hapd->conf->vlan;
|
||||
hapd->conf->vlan = n;
|
||||
|
||||
/* hapd->conf->vlan needs this new VLAN here for WPA setup */
|
||||
if (vlan_if_add(hapd, n, 0)) {
|
||||
hapd->conf->vlan = n->next;
|
||||
os_free(n);
|
||||
n = NULL;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
|
||||
{
|
||||
struct hostapd_vlan *vlan;
|
||||
|
||||
if (vlan_id <= 0)
|
||||
return 1;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
|
||||
__func__, hapd->conf->iface, vlan_id);
|
||||
|
||||
vlan = hapd->conf->vlan;
|
||||
while (vlan) {
|
||||
if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
|
||||
vlan->dynamic_vlan--;
|
||||
break;
|
||||
}
|
||||
vlan = vlan->next;
|
||||
}
|
||||
|
||||
if (vlan == NULL)
|
||||
return 1;
|
||||
|
||||
if (vlan->dynamic_vlan == 0) {
|
||||
vlan_if_remove(hapd, vlan);
|
||||
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||
vlan_dellink(vlan->ifname, hapd);
|
||||
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user