Initializing repository

This commit is contained in:
2025-09-23 15:51:56 +03:00
commit 58130b21a9
871 changed files with 604586 additions and 0 deletions

174
CONTRIBUTIONS Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,4 @@
hs20-osu-client
SP
osu-ca.pem
spp.xsd

91
hs20/client/Android.mk Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

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
View 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

File diff suppressed because it is too large Load Diff

12
src/Makefile Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

35
src/ap/acs.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

481
src/ap/ap_drv_ops.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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(&params, 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, &params)) {
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
View 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

File diff suppressed because it is too large Load Diff

39
src/ap/beacon.h Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

57
src/ap/ctrl_iface_ap.h Normal file
View 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

File diff suppressed because it is too large Load Diff

36
src/ap/dfs.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

54
src/ap/dpp_hostapd.h Normal file
View 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

File diff suppressed because it is too large Load Diff

290
src/ap/eap_user_db.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

95
src/ap/gas_serv.h Normal file
View 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

File diff suppressed because it is too large Load Diff

852
src/ap/hostapd.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

108
src/ap/hw_features.h Normal file
View 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

File diff suppressed because it is too large Load Diff

267
src/ap/ieee802_11.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

571
src/ap/ieee802_11_he.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

371
src/ap/ieee802_11_vht.c Normal file
View 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

File diff suppressed because it is too large Load Diff

69
src/ap/ieee802_1x.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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,
&center_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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

446
src/ap/sta_info.h Normal file
View 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
View 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
View 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 */

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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