mirror of
https://github.com/lwfinger/rtl8188eu.git
synced 2025-05-17 18:43:05 +00:00
rtl8188eu: Add hostapd-2.9 modified for the rtl871x driver
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
parent
13531f3844
commit
903b6fcb82
644 changed files with 378143 additions and 0 deletions
12
hostapd-2.9/src/Makefile
Normal file
12
hostapd-2.9/src/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
|
||||
SUBDIRS += fst
|
||||
|
||||
all:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
|
||||
|
||||
clean:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
|
||||
rm -f *~
|
||||
|
||||
install:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
|
76
hostapd-2.9/src/ap/Makefile
Normal file
76
hostapd-2.9/src/ap/Makefile
Normal file
|
@ -0,0 +1,76 @@
|
|||
all: libap.a
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
|
||||
|
||||
install:
|
||||
@echo Nothing to be made.
|
||||
|
||||
include ../lib.rules
|
||||
|
||||
CFLAGS += -DHOSTAPD
|
||||
CFLAGS += -DNEED_AP_MLME
|
||||
CFLAGS += -DCONFIG_ETH_P_OUI
|
||||
CFLAGS += -DCONFIG_HS20
|
||||
CFLAGS += -DCONFIG_INTERWORKING
|
||||
CFLAGS += -DCONFIG_IEEE80211R
|
||||
CFLAGS += -DCONFIG_IEEE80211R_AP
|
||||
CFLAGS += -DCONFIG_IEEE80211W
|
||||
CFLAGS += -DCONFIG_WPS
|
||||
CFLAGS += -DCONFIG_PROXYARP
|
||||
CFLAGS += -DCONFIG_IPV6
|
||||
CFLAGS += -DCONFIG_IAPP
|
||||
CFLAGS += -DCONFIG_AIRTIME_POLICY
|
||||
|
||||
LIB_OBJS= \
|
||||
accounting.o \
|
||||
ap_config.o \
|
||||
ap_drv_ops.o \
|
||||
ap_list.o \
|
||||
ap_mlme.o \
|
||||
airtime_policy.o \
|
||||
authsrv.o \
|
||||
beacon.o \
|
||||
bss_load.o \
|
||||
ctrl_iface_ap.o \
|
||||
dfs.o \
|
||||
dhcp_snoop.o \
|
||||
drv_callbacks.o \
|
||||
eap_user_db.o \
|
||||
eth_p_oui.o \
|
||||
gas_serv.o \
|
||||
hostapd.o \
|
||||
hs20.o \
|
||||
hw_features.o \
|
||||
iapp.o \
|
||||
ieee802_11_auth.o \
|
||||
ieee802_11.o \
|
||||
ieee802_11_ht.o \
|
||||
ieee802_11_shared.o \
|
||||
ieee802_11_vht.o \
|
||||
ieee802_1x.o \
|
||||
neighbor_db.o \
|
||||
ndisc_snoop.o \
|
||||
p2p_hostapd.o \
|
||||
pmksa_cache_auth.o \
|
||||
preauth_auth.o \
|
||||
rrm.o \
|
||||
sta_info.o \
|
||||
tkip_countermeasures.o \
|
||||
utils.o \
|
||||
vlan.o \
|
||||
vlan_ifconfig.o \
|
||||
vlan_init.o \
|
||||
wmm.o \
|
||||
wnm_ap.o \
|
||||
wpa_auth.o \
|
||||
wpa_auth_ft.o \
|
||||
wpa_auth_glue.o \
|
||||
wpa_auth_ie.o \
|
||||
wps_hostapd.o \
|
||||
x_snoop.o
|
||||
|
||||
libap.a: $(LIB_OBJS)
|
||||
$(AR) crT $@ $?
|
||||
|
||||
-include $(OBJS:%.o=%.d)
|
547
hostapd-2.9/src/ap/accounting.c
Normal file
547
hostapd-2.9/src/ap/accounting.c
Normal file
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* hostapd / RADIUS Accounting
|
||||
* Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "eapol_auth/eapol_auth_sm.h"
|
||||
#include "eapol_auth/eapol_auth_sm_i.h"
|
||||
#include "radius/radius.h"
|
||||
#include "radius/radius_client.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_1x.h"
|
||||
#include "ap_config.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "accounting.h"
|
||||
|
||||
|
||||
/* Default interval in seconds for polling TX/RX octets from the driver if
|
||||
* STA is not using interim accounting. This detects wrap arounds for
|
||||
* input/output octets and updates Acct-{Input,Output}-Gigawords. */
|
||||
#define ACCT_DEFAULT_UPDATE_INTERVAL 300
|
||||
|
||||
static void accounting_sta_interim(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
|
||||
static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
int status_type)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
char buf[128];
|
||||
u8 *val;
|
||||
size_t len;
|
||||
int i;
|
||||
struct wpabuf *b;
|
||||
struct os_time now;
|
||||
|
||||
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
|
||||
radius_client_get_id(hapd->radius));
|
||||
if (msg == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
|
||||
status_type)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sta) {
|
||||
if (!hostapd_config_get_radius_attr(
|
||||
hapd->conf->radius_acct_req_attr,
|
||||
RADIUS_ATTR_ACCT_AUTHENTIC) &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
|
||||
hapd->conf->ieee802_1x ?
|
||||
RADIUS_ACCT_AUTHENTIC_RADIUS :
|
||||
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Use 802.1X identity if available */
|
||||
val = ieee802_1x_get_identity(sta->eapol_sm, &len);
|
||||
|
||||
/* Use RADIUS ACL identity if 802.1X provides no identity */
|
||||
if (!val && sta->identity) {
|
||||
val = (u8 *) sta->identity;
|
||||
len = os_strlen(sta->identity);
|
||||
}
|
||||
|
||||
/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
|
||||
* identity */
|
||||
if (!val) {
|
||||
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
|
||||
MAC2STR(sta->addr));
|
||||
val = (u8 *) buf;
|
||||
len = os_strlen(buf);
|
||||
}
|
||||
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
|
||||
len)) {
|
||||
wpa_printf(MSG_INFO, "Could not add User-Name");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
|
||||
msg) < 0)
|
||||
goto fail;
|
||||
|
||||
if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
|
||||
goto fail;
|
||||
|
||||
if (sta) {
|
||||
for (i = 0; ; i++) {
|
||||
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
|
||||
i);
|
||||
if (val == NULL)
|
||||
break;
|
||||
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
|
||||
val, len)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Class");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
b = ieee802_1x_get_radius_cui(sta->eapol_sm);
|
||||
if (b &&
|
||||
!radius_msg_add_attr(msg,
|
||||
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||
wpabuf_head(b), wpabuf_len(b))) {
|
||||
wpa_printf(MSG_ERROR, "Could not add CUI");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!b && sta->radius_cui &&
|
||||
!radius_msg_add_attr(msg,
|
||||
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||
(u8 *) sta->radius_cui,
|
||||
os_strlen(sta->radius_cui))) {
|
||||
wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sta->ipaddr &&
|
||||
!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_FRAMED_IP_ADDRESS,
|
||||
be_to_host32(sta->ipaddr))) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Could not add Framed-IP-Address");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
os_get_time(&now);
|
||||
if (now.sec > 1000000000 &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
|
||||
now.sec)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add Acct-Delay-Time with zero value for the first transmission. This
|
||||
* will be updated within radius_client.c when retransmitting the frame.
|
||||
*/
|
||||
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
fail:
|
||||
radius_msg_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int accounting_sta_update_stats(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct hostap_sta_driver_data *data)
|
||||
{
|
||||
if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
|
||||
return -1;
|
||||
|
||||
if (!data->bytes_64bit) {
|
||||
/* Extend 32-bit counters from the driver to 64-bit counters */
|
||||
if (sta->last_rx_bytes_lo > data->rx_bytes)
|
||||
sta->last_rx_bytes_hi++;
|
||||
sta->last_rx_bytes_lo = data->rx_bytes;
|
||||
|
||||
if (sta->last_tx_bytes_lo > data->tx_bytes)
|
||||
sta->last_tx_bytes_hi++;
|
||||
sta->last_tx_bytes_lo = data->tx_bytes;
|
||||
}
|
||||
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
|
||||
data->rx_bytes, sta->last_rx_bytes_hi,
|
||||
sta->last_rx_bytes_lo,
|
||||
data->tx_bytes, sta->last_tx_bytes_hi,
|
||||
sta->last_tx_bytes_lo,
|
||||
data->bytes_64bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
struct sta_info *sta = timeout_ctx;
|
||||
int interval;
|
||||
|
||||
if (sta->acct_interim_interval) {
|
||||
accounting_sta_interim(hapd, sta);
|
||||
interval = sta->acct_interim_interval;
|
||||
} else {
|
||||
struct hostap_sta_driver_data data;
|
||||
accounting_sta_update_stats(hapd, sta, &data);
|
||||
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
|
||||
}
|
||||
|
||||
eloop_register_timeout(interval, 0, accounting_interim_update,
|
||||
hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_sta_start - Start STA accounting
|
||||
* @hapd: hostapd BSS data
|
||||
* @sta: The station
|
||||
*/
|
||||
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
int interval;
|
||||
|
||||
if (sta->acct_session_started)
|
||||
return;
|
||||
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"starting accounting session %016llX",
|
||||
(unsigned long long) sta->acct_session_id);
|
||||
|
||||
os_get_reltime(&sta->acct_session_start);
|
||||
sta->last_rx_bytes_hi = 0;
|
||||
sta->last_rx_bytes_lo = 0;
|
||||
sta->last_tx_bytes_hi = 0;
|
||||
sta->last_tx_bytes_lo = 0;
|
||||
hostapd_drv_sta_clear_stats(hapd, sta->addr);
|
||||
|
||||
if (!hapd->conf->radius->acct_server)
|
||||
return;
|
||||
|
||||
if (sta->acct_interim_interval)
|
||||
interval = sta->acct_interim_interval;
|
||||
else
|
||||
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
|
||||
eloop_register_timeout(interval, 0, accounting_interim_update,
|
||||
hapd, sta);
|
||||
|
||||
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
|
||||
if (msg &&
|
||||
radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
|
||||
radius_msg_free(msg);
|
||||
|
||||
sta->acct_session_started = 1;
|
||||
}
|
||||
|
||||
|
||||
static void accounting_sta_report(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int stop)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
int cause = sta->acct_terminate_cause;
|
||||
struct hostap_sta_driver_data data;
|
||||
struct os_reltime now_r, diff;
|
||||
u64 bytes;
|
||||
|
||||
if (!hapd->conf->radius->acct_server)
|
||||
return;
|
||||
|
||||
msg = accounting_msg(hapd, sta,
|
||||
stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
|
||||
RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
|
||||
if (!msg) {
|
||||
wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
|
||||
return;
|
||||
}
|
||||
|
||||
os_get_reltime(&now_r);
|
||||
os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
|
||||
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
|
||||
diff.sec)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_PACKETS,
|
||||
data.rx_packets)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
|
||||
goto fail;
|
||||
}
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
|
||||
data.tx_packets)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
|
||||
goto fail;
|
||||
}
|
||||
if (data.bytes_64bit)
|
||||
bytes = data.rx_bytes;
|
||||
else
|
||||
bytes = ((u64) sta->last_rx_bytes_hi << 32) |
|
||||
sta->last_rx_bytes_lo;
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_OCTETS,
|
||||
(u32) bytes)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
|
||||
goto fail;
|
||||
}
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
|
||||
(u32) (bytes >> 32))) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
|
||||
goto fail;
|
||||
}
|
||||
if (data.bytes_64bit)
|
||||
bytes = data.tx_bytes;
|
||||
else
|
||||
bytes = ((u64) sta->last_tx_bytes_hi << 32) |
|
||||
sta->last_tx_bytes_lo;
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
|
||||
(u32) bytes)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
|
||||
goto fail;
|
||||
}
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
|
||||
(u32) (bytes >> 32))) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (eloop_terminated())
|
||||
cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
|
||||
|
||||
if (stop && cause &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
|
||||
cause)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (radius_client_send(hapd->radius, msg,
|
||||
stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
|
||||
sta->addr) < 0)
|
||||
goto fail;
|
||||
return;
|
||||
|
||||
fail:
|
||||
radius_msg_free(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_sta_interim - Send a interim STA accounting report
|
||||
* @hapd: hostapd BSS data
|
||||
* @sta: The station
|
||||
*/
|
||||
static void accounting_sta_interim(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
if (sta->acct_session_started)
|
||||
accounting_sta_report(hapd, sta, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_sta_stop - Stop STA accounting
|
||||
* @hapd: hostapd BSS data
|
||||
* @sta: The station
|
||||
*/
|
||||
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
if (sta->acct_session_started) {
|
||||
accounting_sta_report(hapd, sta, 1);
|
||||
eloop_cancel_timeout(accounting_interim_update, hapd, sta);
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"stopped accounting session %016llX",
|
||||
(unsigned long long) sta->acct_session_id);
|
||||
sta->acct_session_started = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
return radius_gen_session_id((u8 *) &sta->acct_session_id,
|
||||
sizeof(sta->acct_session_id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_receive - Process the RADIUS frames from Accounting Server
|
||||
* @msg: RADIUS response message
|
||||
* @req: RADIUS request message
|
||||
* @shared_secret: RADIUS shared secret
|
||||
* @shared_secret_len: Length of shared_secret in octets
|
||||
* @data: Context data (struct hostapd_data *)
|
||||
* Returns: Processing status
|
||||
*/
|
||||
static RadiusRxResult
|
||||
accounting_receive(struct radius_msg *msg, struct radius_msg *req,
|
||||
const u8 *shared_secret, size_t shared_secret_len,
|
||||
void *data)
|
||||
{
|
||||
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
|
||||
wpa_printf(MSG_INFO, "Unknown RADIUS message code");
|
||||
return RADIUS_RX_UNKNOWN;
|
||||
}
|
||||
|
||||
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
|
||||
wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
|
||||
return RADIUS_RX_INVALID_AUTHENTICATOR;
|
||||
}
|
||||
|
||||
return RADIUS_RX_PROCESSED;
|
||||
}
|
||||
|
||||
|
||||
static void accounting_report_state(struct hostapd_data *hapd, int on)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
|
||||
if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
|
||||
return;
|
||||
|
||||
/* Inform RADIUS server that accounting will start/stop so that the
|
||||
* server can close old accounting sessions. */
|
||||
msg = accounting_msg(hapd, NULL,
|
||||
on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
|
||||
RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (hapd->acct_session_id) {
|
||||
char buf[20];
|
||||
|
||||
os_snprintf(buf, sizeof(buf), "%016llX",
|
||||
(unsigned long long) hapd->acct_session_id);
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
|
||||
(u8 *) buf, os_strlen(buf)))
|
||||
wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
|
||||
}
|
||||
|
||||
if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
|
||||
radius_msg_free(msg);
|
||||
}
|
||||
|
||||
|
||||
static void accounting_interim_error_cb(const u8 *addr, void *ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
struct sta_info *sta;
|
||||
unsigned int i, wait_time;
|
||||
int res;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta)
|
||||
return;
|
||||
sta->acct_interim_errors++;
|
||||
if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" - too many errors, abandon this interim accounting update",
|
||||
MAC2STR(addr));
|
||||
sta->acct_interim_errors = 0;
|
||||
/* Next update will be tried after normal update interval */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a shorter update interval as an improved retransmission mechanism
|
||||
* for failed interim accounting updates. This allows the statistics to
|
||||
* be updated for each retransmission.
|
||||
*
|
||||
* RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
|
||||
* Schedule the first retry attempt immediately and every following one
|
||||
* with exponential backoff.
|
||||
*/
|
||||
if (sta->acct_interim_errors == 1) {
|
||||
wait_time = 0;
|
||||
} else {
|
||||
wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
|
||||
for (i = 1; i < sta->acct_interim_errors; i++)
|
||||
wait_time *= 2;
|
||||
}
|
||||
res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
|
||||
hapd, sta);
|
||||
if (res == 1)
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" (error count: %u) - schedule next update in %u seconds",
|
||||
MAC2STR(addr), sta->acct_interim_errors, wait_time);
|
||||
else if (res == 0)
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" (error count: %u)", MAC2STR(addr),
|
||||
sta->acct_interim_errors);
|
||||
else
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" (error count: %u) - no timer found", MAC2STR(addr),
|
||||
sta->acct_interim_errors);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_init: Initialize accounting
|
||||
* @hapd: hostapd BSS data
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int accounting_init(struct hostapd_data *hapd)
|
||||
{
|
||||
if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
|
||||
sizeof(hapd->acct_session_id)) < 0)
|
||||
return -1;
|
||||
|
||||
if (radius_client_register(hapd->radius, RADIUS_ACCT,
|
||||
accounting_receive, hapd))
|
||||
return -1;
|
||||
radius_client_set_interim_error_cb(hapd->radius,
|
||||
accounting_interim_error_cb, hapd);
|
||||
|
||||
accounting_report_state(hapd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_deinit: Deinitialize accounting
|
||||
* @hapd: hostapd BSS data
|
||||
*/
|
||||
void accounting_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
accounting_report_state(hapd, 0);
|
||||
}
|
45
hostapd-2.9/src/ap/accounting.h
Normal file
45
hostapd-2.9/src/ap/accounting.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* hostapd / RADIUS Accounting
|
||||
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef ACCOUNTING_H
|
||||
#define ACCOUNTING_H
|
||||
|
||||
#ifdef CONFIG_NO_ACCOUNTING
|
||||
static inline int accounting_sta_get_id(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void accounting_sta_start(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void accounting_sta_stop(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int accounting_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void accounting_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
#else /* CONFIG_NO_ACCOUNTING */
|
||||
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int accounting_init(struct hostapd_data *hapd);
|
||||
void accounting_deinit(struct hostapd_data *hapd);
|
||||
#endif /* CONFIG_NO_ACCOUNTING */
|
||||
|
||||
#endif /* ACCOUNTING_H */
|
986
hostapd-2.9/src/ap/acs.c
Normal file
986
hostapd-2.9/src/ap/acs.c
Normal file
|
@ -0,0 +1,986 @@
|
|||
/*
|
||||
* ACS - Automatic Channel Selection module
|
||||
* Copyright (c) 2011, Atheros Communications
|
||||
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/list.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/hw_features_common.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "drivers/driver.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "ap_config.h"
|
||||
#include "hw_features.h"
|
||||
#include "acs.h"
|
||||
|
||||
/*
|
||||
* Automatic Channel Selection
|
||||
* ===========================
|
||||
*
|
||||
* More info at
|
||||
* ------------
|
||||
* http://wireless.kernel.org/en/users/Documentation/acs
|
||||
*
|
||||
* How to use
|
||||
* ----------
|
||||
* - make sure you have CONFIG_ACS=y in hostapd's .config
|
||||
* - use channel=0 or channel=acs to enable ACS
|
||||
*
|
||||
* How does it work
|
||||
* ----------------
|
||||
* 1. passive scans are used to collect survey data
|
||||
* (it is assumed that scan trigger collection of survey data in driver)
|
||||
* 2. interference factor is calculated for each channel
|
||||
* 3. ideal channel is picked depending on channel width by using adjacent
|
||||
* channel interference factors
|
||||
*
|
||||
* Known limitations
|
||||
* -----------------
|
||||
* - Current implementation depends heavily on the amount of time willing to
|
||||
* spend gathering survey data during hostapd startup. Short traffic bursts
|
||||
* may be missed and a suboptimal channel may be picked.
|
||||
* - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
|
||||
*
|
||||
* Todo / Ideas
|
||||
* ------------
|
||||
* - implement other interference computation methods
|
||||
* - BSS/RSSI based
|
||||
* - spectral scan based
|
||||
* (should be possibly to hook this up with current ACS scans)
|
||||
* - add wpa_supplicant support (for P2P)
|
||||
* - collect a histogram of interference over time allowing more educated
|
||||
* guess about an ideal channel (perhaps CSA could be used to migrate AP to a
|
||||
* new "better" channel while running)
|
||||
* - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
|
||||
* when choosing the ideal channel
|
||||
*
|
||||
* Survey interference factor implementation details
|
||||
* -------------------------------------------------
|
||||
* Generic interference_factor in struct hostapd_channel_data is used.
|
||||
*
|
||||
* The survey interference factor is defined as the ratio of the
|
||||
* observed busy time over the time we spent on the channel,
|
||||
* this value is then amplified by the observed noise floor on
|
||||
* the channel in comparison to the lowest noise floor observed
|
||||
* on the entire band.
|
||||
*
|
||||
* This corresponds to:
|
||||
* ---
|
||||
* (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
|
||||
* ---
|
||||
*
|
||||
* The coefficient of 2 reflects the way power in "far-field"
|
||||
* radiation decreases as the square of distance from the antenna [1].
|
||||
* What this does is it decreases the observed busy time ratio if the
|
||||
* noise observed was low but increases it if the noise was high,
|
||||
* proportionally to the way "far field" radiation changes over
|
||||
* distance.
|
||||
*
|
||||
* If channel busy time is not available the fallback is to use channel RX time.
|
||||
*
|
||||
* Since noise floor is in dBm it is necessary to convert it into Watts so that
|
||||
* combined channel interference (e.g., HT40, which uses two channels) can be
|
||||
* calculated easily.
|
||||
* ---
|
||||
* (busy time - tx time) / (active time - tx time) *
|
||||
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
|
||||
* ---
|
||||
*
|
||||
* However to account for cases where busy/rx time is 0 (channel load is then
|
||||
* 0%) channel noise floor signal power is combined into the equation so a
|
||||
* channel with lower noise floor is preferred. The equation becomes:
|
||||
* ---
|
||||
* 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
|
||||
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
|
||||
* ---
|
||||
*
|
||||
* All this "interference factor" is purely subjective and only time
|
||||
* will tell how usable this is. By using the minimum noise floor we
|
||||
* remove any possible issues due to card calibration. The computation
|
||||
* of the interference factor then is dependent on what the card itself
|
||||
* picks up as the minimum noise, not an actual real possible card
|
||||
* noise value.
|
||||
*
|
||||
* Total interference computation details
|
||||
* --------------------------------------
|
||||
* The above channel interference factor is calculated with no respect to
|
||||
* target operational bandwidth.
|
||||
*
|
||||
* To find an ideal channel the above data is combined by taking into account
|
||||
* the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
|
||||
* overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
|
||||
* on 5 GHz.
|
||||
*
|
||||
* Each valid and possible channel spec (i.e., channel + width) is taken and its
|
||||
* interference factor is computed by summing up interferences of each channel
|
||||
* it overlaps. The one with least total interference is picked up.
|
||||
*
|
||||
* Note: This implies base channel interference factor must be non-negative
|
||||
* allowing easy summing up.
|
||||
*
|
||||
* Example ACS analysis printout
|
||||
* -----------------------------
|
||||
*
|
||||
* ACS: Trying survey-based ACS
|
||||
* ACS: Survey analysis for channel 1 (2412 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
|
||||
* ACS: * interference factor average: 0.0557166
|
||||
* ACS: Survey analysis for channel 2 (2417 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
|
||||
* ACS: * interference factor average: 0.050832
|
||||
* ACS: Survey analysis for channel 3 (2422 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||
* ACS: * interference factor average: 0.0148838
|
||||
* ACS: Survey analysis for channel 4 (2427 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
|
||||
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: * interference factor average: 0.0160801
|
||||
* ACS: Survey analysis for channel 5 (2432 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
|
||||
* ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
|
||||
* ACS: * interference factor average: 0.232244
|
||||
* ACS: Survey analysis for channel 6 (2437 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
|
||||
* ACS: * interference factor average: 0.232298
|
||||
* ACS: Survey analysis for channel 7 (2442 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
|
||||
* ACS: * interference factor average: 0.195031
|
||||
* ACS: Survey analysis for channel 8 (2447 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
|
||||
* ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
|
||||
* ACS: * interference factor average: 0.0865885
|
||||
* ACS: Survey analysis for channel 9 (2452 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
|
||||
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
|
||||
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: * interference factor average: 0.00993022
|
||||
* ACS: Survey analysis for channel 10 (2457 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
|
||||
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: * interference factor average: 0.0136033
|
||||
* ACS: Survey analysis for channel 11 (2462 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||
* ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
|
||||
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
|
||||
* ACS: * interference factor average: 0.0271605
|
||||
* ACS: Survey analysis for channel 12 (2467 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
|
||||
* ACS: * interference factor average: 0.0148992
|
||||
* ACS: Survey analysis for channel 13 (2472 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
|
||||
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: * interference factor average: 0.0260179
|
||||
* ACS: Survey analysis for selected bandwidth 20MHz
|
||||
* ACS: * channel 1: total interference = 0.121432
|
||||
* ACS: * channel 2: total interference = 0.137512
|
||||
* ACS: * channel 3: total interference = 0.369757
|
||||
* ACS: * channel 4: total interference = 0.546338
|
||||
* ACS: * channel 5: total interference = 0.690538
|
||||
* ACS: * channel 6: total interference = 0.762242
|
||||
* ACS: * channel 7: total interference = 0.756092
|
||||
* ACS: * channel 8: total interference = 0.537451
|
||||
* ACS: * channel 9: total interference = 0.332313
|
||||
* ACS: * channel 10: total interference = 0.152182
|
||||
* ACS: * channel 11: total interference = 0.0916111
|
||||
* ACS: * channel 12: total interference = 0.0816809
|
||||
* ACS: * channel 13: total interference = 0.0680776
|
||||
* ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
|
||||
*
|
||||
* [1] http://en.wikipedia.org/wiki/Near_and_far_field
|
||||
*/
|
||||
|
||||
|
||||
static int acs_request_scan(struct hostapd_iface *iface);
|
||||
static int acs_survey_is_sufficient(struct freq_survey *survey);
|
||||
|
||||
|
||||
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
|
||||
{
|
||||
struct freq_survey *survey, *tmp;
|
||||
|
||||
if (dl_list_empty(&chan->survey_list))
|
||||
return;
|
||||
|
||||
dl_list_for_each_safe(survey, tmp, &chan->survey_list,
|
||||
struct freq_survey, list) {
|
||||
dl_list_del(&survey->list);
|
||||
os_free(survey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void acs_cleanup(struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
struct hostapd_channel_data *chan;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
|
||||
acs_clean_chan_surveys(chan);
|
||||
|
||||
dl_list_init(&chan->survey_list);
|
||||
chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
|
||||
chan->min_nf = 0;
|
||||
}
|
||||
|
||||
iface->chans_surveyed = 0;
|
||||
iface->acs_num_completed_scans = 0;
|
||||
}
|
||||
|
||||
|
||||
static void acs_fail(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to start");
|
||||
acs_cleanup(iface);
|
||||
hostapd_disable_iface(iface);
|
||||
}
|
||||
|
||||
|
||||
static long double
|
||||
acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
|
||||
{
|
||||
long double factor, busy, total;
|
||||
|
||||
if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
|
||||
busy = survey->channel_time_busy;
|
||||
else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
|
||||
busy = survey->channel_time_rx;
|
||||
else {
|
||||
/* This shouldn't really happen as survey data is checked in
|
||||
* acs_sanity_check() */
|
||||
wpa_printf(MSG_ERROR, "ACS: Survey data missing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
total = survey->channel_time;
|
||||
|
||||
if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
|
||||
busy -= survey->channel_time_tx;
|
||||
total -= survey->channel_time_tx;
|
||||
}
|
||||
|
||||
/* TODO: figure out the best multiplier for noise floor base */
|
||||
factor = pow(10, survey->nf / 5.0L) +
|
||||
(total ? (busy / total) : 0) *
|
||||
pow(2, pow(10, (long double) survey->nf / 10.0L) -
|
||||
pow(10, (long double) min_nf / 10.0L));
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acs_survey_chan_interference_factor(struct hostapd_iface *iface,
|
||||
struct hostapd_channel_data *chan)
|
||||
{
|
||||
struct freq_survey *survey;
|
||||
unsigned int i = 0;
|
||||
long double int_factor = 0;
|
||||
unsigned count = 0;
|
||||
|
||||
if (dl_list_empty(&chan->survey_list) ||
|
||||
(chan->flag & HOSTAPD_CHAN_DISABLED))
|
||||
return;
|
||||
|
||||
chan->interference_factor = 0;
|
||||
|
||||
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||
{
|
||||
i++;
|
||||
|
||||
if (!acs_survey_is_sufficient(survey)) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
int_factor = acs_survey_interference_factor(survey,
|
||||
iface->lowest_nf);
|
||||
chan->interference_factor += int_factor;
|
||||
wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
|
||||
i, chan->min_nf, int_factor,
|
||||
survey->nf, (unsigned long) survey->channel_time,
|
||||
(unsigned long) survey->channel_time_busy,
|
||||
(unsigned long) survey->channel_time_rx);
|
||||
}
|
||||
|
||||
if (count)
|
||||
chan->interference_factor /= count;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan)
|
||||
{
|
||||
const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
|
||||
157, 184, 192 };
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||
if (chan->chan == allowed[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan)
|
||||
{
|
||||
const int allowed[] = { 36, 52, 100, 116, 132, 149 };
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||
if (chan->chan == allowed[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan)
|
||||
{
|
||||
const int allowed[] = { 36, 100 };
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||
if (chan->chan == allowed[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_survey_is_sufficient(struct freq_survey *survey)
|
||||
{
|
||||
if (!(survey->filled & SURVEY_HAS_NF)) {
|
||||
wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
|
||||
wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
|
||||
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"ACS: Survey is missing RX and busy time (at least one is required)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
|
||||
{
|
||||
struct freq_survey *survey;
|
||||
int ret = -1;
|
||||
|
||||
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||
{
|
||||
if (acs_survey_is_sufficient(survey)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret == -1)
|
||||
ret = 1; /* no survey list entries */
|
||||
|
||||
if (!ret) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"ACS: Channel %d has insufficient survey data",
|
||||
chan->chan);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
struct hostapd_channel_data *chan;
|
||||
int valid = 0;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||
acs_survey_list_is_sufficient(chan))
|
||||
valid++;
|
||||
}
|
||||
|
||||
/* We need at least survey data for one channel */
|
||||
return !!valid;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_chan(struct hostapd_channel_data *chan)
|
||||
{
|
||||
return !dl_list_empty(&chan->survey_list) &&
|
||||
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||
acs_survey_list_is_sufficient(chan);
|
||||
}
|
||||
|
||||
|
||||
static int is_in_chanlist(struct hostapd_iface *iface,
|
||||
struct hostapd_channel_data *chan)
|
||||
{
|
||||
if (!iface->conf->acs_ch_list.num)
|
||||
return 1;
|
||||
|
||||
return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
|
||||
}
|
||||
|
||||
|
||||
static void acs_survey_all_chans_intereference_factor(
|
||||
struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
struct hostapd_channel_data *chan;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
if (!acs_usable_chan(chan))
|
||||
continue;
|
||||
|
||||
if (!is_in_chanlist(iface, chan))
|
||||
continue;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
|
||||
chan->chan, chan->freq);
|
||||
|
||||
acs_survey_chan_interference_factor(iface, chan);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
|
||||
chan->interference_factor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
|
||||
int freq)
|
||||
{
|
||||
struct hostapd_channel_data *chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
|
||||
if (chan->freq == freq)
|
||||
return chan;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int is_24ghz_mode(enum hostapd_hw_mode mode)
|
||||
{
|
||||
return mode == HOSTAPD_MODE_IEEE80211B ||
|
||||
mode == HOSTAPD_MODE_IEEE80211G;
|
||||
}
|
||||
|
||||
|
||||
static int is_common_24ghz_chan(int chan)
|
||||
{
|
||||
return chan == 1 || chan == 6 || chan == 11;
|
||||
}
|
||||
|
||||
|
||||
#ifndef ACS_ADJ_WEIGHT
|
||||
#define ACS_ADJ_WEIGHT 0.85
|
||||
#endif /* ACS_ADJ_WEIGHT */
|
||||
|
||||
#ifndef ACS_NEXT_ADJ_WEIGHT
|
||||
#define ACS_NEXT_ADJ_WEIGHT 0.55
|
||||
#endif /* ACS_NEXT_ADJ_WEIGHT */
|
||||
|
||||
#ifndef ACS_24GHZ_PREFER_1_6_11
|
||||
/*
|
||||
* Select commonly used channels 1, 6, 11 by default even if a neighboring
|
||||
* channel has a smaller interference factor as long as it is not better by more
|
||||
* than this multiplier.
|
||||
*/
|
||||
#define ACS_24GHZ_PREFER_1_6_11 0.8
|
||||
#endif /* ACS_24GHZ_PREFER_1_6_11 */
|
||||
|
||||
/*
|
||||
* At this point it's assumed chan->interface_factor has been computed.
|
||||
* This function should be reusable regardless of interference computation
|
||||
* option (survey, BSS, spectral, ...). chan->interference factor must be
|
||||
* summable (i.e., must be always greater than zero).
|
||||
*/
|
||||
static struct hostapd_channel_data *
|
||||
acs_find_ideal_chan(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
|
||||
*rand_chan = NULL;
|
||||
long double factor, ideal_factor = 0;
|
||||
int i, j;
|
||||
int n_chans = 1;
|
||||
u32 bw;
|
||||
unsigned int k;
|
||||
|
||||
/* TODO: HT40- support */
|
||||
|
||||
if (iface->conf->ieee80211n &&
|
||||
iface->conf->secondary_channel == -1) {
|
||||
wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iface->conf->ieee80211n &&
|
||||
iface->conf->secondary_channel)
|
||||
n_chans = 2;
|
||||
|
||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
|
||||
switch (hostapd_get_oper_chwidth(iface->conf)) {
|
||||
case CHANWIDTH_80MHZ:
|
||||
n_chans = 4;
|
||||
break;
|
||||
case CHANWIDTH_160MHZ:
|
||||
n_chans = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bw = num_chan_to_bw(n_chans);
|
||||
|
||||
/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Survey analysis for selected bandwidth %d MHz", bw);
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
double total_weight;
|
||||
struct acs_bias *bias, tmp_bias;
|
||||
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
/* Since in the current ACS implementation the first channel is
|
||||
* always a primary channel, skip channels not available as
|
||||
* primary until more sophisticated channel selection is
|
||||
* implemented. */
|
||||
if (!chan_pri_allowed(chan))
|
||||
continue;
|
||||
|
||||
if (!is_in_chanlist(iface, chan))
|
||||
continue;
|
||||
|
||||
if (!chan_bw_allowed(chan, bw, 1, 1)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Channel %d: BW %u is not supported",
|
||||
chan->chan, bw);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* HT40 on 5 GHz has a limited set of primary channels as per
|
||||
* 11n Annex J */
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
|
||||
iface->conf->ieee80211n &&
|
||||
iface->conf->secondary_channel &&
|
||||
!acs_usable_ht40_chan(chan)) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
|
||||
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
|
||||
if (hostapd_get_oper_chwidth(iface->conf) ==
|
||||
CHANWIDTH_80MHZ &&
|
||||
!acs_usable_vht80_chan(chan)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Channel %d: not allowed as primary channel for VHT80",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hostapd_get_oper_chwidth(iface->conf) ==
|
||||
CHANWIDTH_160MHZ &&
|
||||
!acs_usable_vht160_chan(chan)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Channel %d: not allowed as primary channel for VHT160",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
factor = 0;
|
||||
if (acs_usable_chan(chan))
|
||||
factor = chan->interference_factor;
|
||||
total_weight = 1;
|
||||
|
||||
for (j = 1; j < n_chans; j++) {
|
||||
adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
|
||||
if (!adj_chan)
|
||||
break;
|
||||
|
||||
if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
|
||||
chan->chan, adj_chan->chan, bw);
|
||||
break;
|
||||
}
|
||||
|
||||
if (acs_usable_chan(adj_chan)) {
|
||||
factor += adj_chan->interference_factor;
|
||||
total_weight += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (j != n_chans) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
|
||||
* channel interference factor. */
|
||||
if (is_24ghz_mode(iface->current_mode->mode)) {
|
||||
for (j = 0; j < n_chans; j++) {
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) - 5);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_ADJ_WEIGHT;
|
||||
}
|
||||
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) - 10);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_NEXT_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_NEXT_ADJ_WEIGHT;
|
||||
}
|
||||
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) + 5);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_ADJ_WEIGHT;
|
||||
}
|
||||
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) + 10);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_NEXT_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_NEXT_ADJ_WEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
factor /= total_weight;
|
||||
|
||||
bias = NULL;
|
||||
if (iface->conf->acs_chan_bias) {
|
||||
for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
|
||||
bias = &iface->conf->acs_chan_bias[k];
|
||||
if (bias->channel == chan->chan)
|
||||
break;
|
||||
bias = NULL;
|
||||
}
|
||||
} else if (is_24ghz_mode(iface->current_mode->mode) &&
|
||||
is_common_24ghz_chan(chan->chan)) {
|
||||
tmp_bias.channel = chan->chan;
|
||||
tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
|
||||
bias = &tmp_bias;
|
||||
}
|
||||
|
||||
if (bias) {
|
||||
factor *= bias->bias;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: * channel %d: total interference = %Lg (%f bias)",
|
||||
chan->chan, factor, bias->bias);
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: * channel %d: total interference = %Lg",
|
||||
chan->chan, factor);
|
||||
}
|
||||
|
||||
if (acs_usable_chan(chan) &&
|
||||
(!ideal_chan || factor < ideal_factor)) {
|
||||
ideal_factor = factor;
|
||||
ideal_chan = chan;
|
||||
}
|
||||
|
||||
/* This channel would at least be usable */
|
||||
if (!rand_chan)
|
||||
rand_chan = chan;
|
||||
}
|
||||
|
||||
if (ideal_chan) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
|
||||
ideal_chan->chan, ideal_chan->freq, ideal_factor);
|
||||
return ideal_chan;
|
||||
}
|
||||
|
||||
return rand_chan;
|
||||
}
|
||||
|
||||
|
||||
static void acs_adjust_center_freq(struct hostapd_iface *iface)
|
||||
{
|
||||
int offset;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
|
||||
|
||||
switch (hostapd_get_oper_chwidth(iface->conf)) {
|
||||
case CHANWIDTH_USE_HT:
|
||||
offset = 2 * iface->conf->secondary_channel;
|
||||
break;
|
||||
case CHANWIDTH_80MHZ:
|
||||
offset = 6;
|
||||
break;
|
||||
case CHANWIDTH_160MHZ:
|
||||
offset = 14;
|
||||
break;
|
||||
default:
|
||||
/* TODO: How can this be calculated? Adjust
|
||||
* acs_find_ideal_chan() */
|
||||
wpa_printf(MSG_INFO,
|
||||
"ACS: Only VHT20/40/80/160 is supported now");
|
||||
return;
|
||||
}
|
||||
|
||||
hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
|
||||
iface->conf->channel + offset);
|
||||
}
|
||||
|
||||
|
||||
static int acs_study_survey_based(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
|
||||
|
||||
if (!iface->chans_surveyed) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!acs_surveys_are_sufficient(iface)) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
acs_survey_all_chans_intereference_factor(iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_study_options(struct hostapd_iface *iface)
|
||||
{
|
||||
if (acs_study_survey_based(iface) == 0)
|
||||
return 0;
|
||||
|
||||
/* TODO: If no surveys are available/sufficient this is a good
|
||||
* place to fallback to BSS-based ACS */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void acs_study(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_channel_data *ideal_chan;
|
||||
int err;
|
||||
|
||||
err = acs_study_options(iface);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "ACS: All study options have failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ideal_chan = acs_find_ideal_chan(iface);
|
||||
if (!ideal_chan) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
|
||||
err = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
iface->conf->channel = ideal_chan->chan;
|
||||
|
||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
|
||||
acs_adjust_center_freq(iface);
|
||||
|
||||
err = 0;
|
||||
fail:
|
||||
/*
|
||||
* hostapd_setup_interface_complete() will return -1 on failure,
|
||||
* 0 on success and 0 is HOSTAPD_CHAN_VALID :)
|
||||
*/
|
||||
if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
|
||||
acs_cleanup(iface);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This can possibly happen if channel parameters (secondary
|
||||
* channel, center frequencies) are misconfigured */
|
||||
wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
|
||||
acs_fail(iface);
|
||||
}
|
||||
|
||||
|
||||
static void acs_scan_complete(struct hostapd_iface *iface)
|
||||
{
|
||||
int err;
|
||||
|
||||
iface->scan_cb = NULL;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
|
||||
iface->conf->acs_num_scans);
|
||||
|
||||
err = hostapd_drv_get_survey(iface->bss[0], 0);
|
||||
if (err) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
|
||||
err = acs_request_scan(iface);
|
||||
if (err) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
acs_study(iface);
|
||||
return;
|
||||
fail:
|
||||
hostapd_acs_completed(iface, 1);
|
||||
acs_fail(iface);
|
||||
}
|
||||
|
||||
|
||||
static int acs_request_scan(struct hostapd_iface *iface)
|
||||
{
|
||||
struct wpa_driver_scan_params params;
|
||||
struct hostapd_channel_data *chan;
|
||||
int i, *freq;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.freqs = os_calloc(iface->current_mode->num_channels + 1,
|
||||
sizeof(params.freqs[0]));
|
||||
if (params.freqs == NULL)
|
||||
return -1;
|
||||
|
||||
freq = params.freqs;
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
|
||||
if (!is_in_chanlist(iface, chan))
|
||||
continue;
|
||||
|
||||
*freq++ = chan->freq;
|
||||
}
|
||||
*freq = 0;
|
||||
|
||||
iface->scan_cb = acs_scan_complete;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
|
||||
iface->acs_num_completed_scans + 1,
|
||||
iface->conf->acs_num_scans);
|
||||
|
||||
if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
|
||||
acs_cleanup(iface);
|
||||
os_free(params.freqs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_free(params.freqs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
|
||||
|
||||
if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
|
||||
wpa_printf(MSG_INFO, "ACS: Offloading to driver");
|
||||
if (hostapd_drv_do_acs(iface->bss[0]))
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
return HOSTAPD_CHAN_ACS;
|
||||
}
|
||||
|
||||
if (!iface->current_mode)
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
|
||||
acs_cleanup(iface);
|
||||
|
||||
if (acs_request_scan(iface) < 0)
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
|
||||
hostapd_set_state(iface, HAPD_IFACE_ACS);
|
||||
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
|
||||
|
||||
return HOSTAPD_CHAN_ACS;
|
||||
}
|
32
hostapd-2.9/src/ap/acs.h
Normal file
32
hostapd-2.9/src/ap/acs.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* ACS - Automatic Channel Selection module
|
||||
* Copyright (c) 2011, Atheros Communications
|
||||
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef ACS_H
|
||||
#define ACS_H
|
||||
|
||||
#ifdef CONFIG_ACS
|
||||
|
||||
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
|
||||
void acs_cleanup(struct hostapd_iface *iface);
|
||||
|
||||
#else /* CONFIG_ACS */
|
||||
|
||||
static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
}
|
||||
|
||||
static inline void acs_cleanup(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ACS */
|
||||
|
||||
#endif /* ACS_H */
|
269
hostapd-2.9/src/ap/airtime_policy.c
Normal file
269
hostapd-2.9/src/ap/airtime_policy.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Airtime policy configuration
|
||||
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "sta_info.h"
|
||||
#include "airtime_policy.h"
|
||||
|
||||
/* Idea:
|
||||
* Two modes of airtime enforcement:
|
||||
* 1. Static weights: specify weights per MAC address with a per-BSS default
|
||||
* 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
|
||||
* enforce relative total shares between BSSes.
|
||||
*
|
||||
* - Periodic per-station callback to update queue status.
|
||||
*
|
||||
* Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
|
||||
* keep them updated in sta_info.
|
||||
*
|
||||
* - Separate periodic per-bss (or per-iface?) callback to update weights.
|
||||
*
|
||||
* Just need to loop through all interfaces, count sum the active stations (or
|
||||
* should the per-STA callback just adjust that for the BSS?) and calculate new
|
||||
* weights.
|
||||
*/
|
||||
|
||||
static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
|
||||
unsigned int *sec,
|
||||
unsigned int *usec)
|
||||
{
|
||||
unsigned int update_int = iface->conf->airtime_update_interval;
|
||||
|
||||
if (!update_int) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Airtime policy: Invalid airtime policy update interval %u",
|
||||
update_int);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sec = update_int / 1000;
|
||||
*usec = (update_int % 1000) * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void set_new_backlog_time(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct os_reltime *now)
|
||||
{
|
||||
sta->backlogged_until = *now;
|
||||
sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
|
||||
AIRTIME_BACKLOG_EXPIRY_FACTOR;
|
||||
while (sta->backlogged_until.usec >= 1000000) {
|
||||
sta->backlogged_until.sec++;
|
||||
sta->backlogged_until.usec -= 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void count_backlogged_sta(struct hostapd_data *hapd)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
struct hostap_sta_driver_data data = {};
|
||||
unsigned int num_backlogged = 0;
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
|
||||
continue;
|
||||
|
||||
if (data.backlog_bytes > 0)
|
||||
set_new_backlog_time(hapd, sta, &now);
|
||||
if (os_reltime_before(&now, &sta->backlogged_until))
|
||||
num_backlogged++;
|
||||
}
|
||||
hapd->num_backlogged_sta = num_backlogged;
|
||||
}
|
||||
|
||||
|
||||
static int sta_set_airtime_weight(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
unsigned int weight)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (weight != sta->airtime_weight &&
|
||||
(ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
|
||||
return ret;
|
||||
|
||||
sta->airtime_weight = weight;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next)
|
||||
sta_set_airtime_weight(hapd, sta, weight);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int get_airtime_quantum(unsigned int max_wt)
|
||||
{
|
||||
unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
|
||||
|
||||
if (quantum < AIRTIME_QUANTUM_MIN)
|
||||
quantum = AIRTIME_QUANTUM_MIN;
|
||||
else if (quantum > AIRTIME_QUANTUM_MAX)
|
||||
quantum = AIRTIME_QUANTUM_MAX;
|
||||
|
||||
return quantum;
|
||||
}
|
||||
|
||||
|
||||
static void update_airtime_weights(void *eloop_data, void *user_data)
|
||||
{
|
||||
struct hostapd_iface *iface = eloop_data;
|
||||
struct hostapd_data *bss;
|
||||
unsigned int sec, usec;
|
||||
unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
|
||||
wt_sum = 0;
|
||||
unsigned int quantum;
|
||||
Boolean all_div_min = TRUE;
|
||||
Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
|
||||
int wt, num_bss = 0, max_wt = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
|
||||
count_backlogged_sta(bss);
|
||||
if (!bss->num_backlogged_sta)
|
||||
continue;
|
||||
|
||||
if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
|
||||
num_sta_min = bss->num_backlogged_sta;
|
||||
|
||||
num_sta_prod *= bss->num_backlogged_sta;
|
||||
num_sta_sum += bss->num_backlogged_sta;
|
||||
wt_sum += bss->conf->airtime_weight;
|
||||
num_bss++;
|
||||
}
|
||||
|
||||
if (num_sta_min) {
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
|
||||
/* Check if we can divide all sta numbers by the
|
||||
* smallest number to keep weights as small as possible.
|
||||
* This is a lazy way to avoid having to factor
|
||||
* integers. */
|
||||
if (bss->num_backlogged_sta &&
|
||||
bss->num_backlogged_sta % num_sta_min > 0)
|
||||
all_div_min = FALSE;
|
||||
|
||||
/* If we're in LIMIT mode, we only apply the weight
|
||||
* scaling when the BSS(es) marked as limited would a
|
||||
* larger share than the relative BSS weights indicates
|
||||
* it should. */
|
||||
if (!apply_limit && bss->conf->airtime_limit) {
|
||||
if (bss->num_backlogged_sta * wt_sum >
|
||||
bss->conf->airtime_weight * num_sta_sum)
|
||||
apply_limit = TRUE;
|
||||
}
|
||||
}
|
||||
if (all_div_min)
|
||||
num_sta_prod /= num_sta_min;
|
||||
}
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
|
||||
/* We only set the calculated weight if the BSS has active
|
||||
* stations and there are other active interfaces as well -
|
||||
* otherwise we just set a unit weight. This ensures that
|
||||
* the weights are set reasonably when stations transition from
|
||||
* inactive to active. */
|
||||
if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
|
||||
wt = bss->conf->airtime_weight * num_sta_prod /
|
||||
bss->num_backlogged_sta;
|
||||
else
|
||||
wt = 1;
|
||||
|
||||
bss->airtime_weight = wt;
|
||||
if (wt > max_wt)
|
||||
max_wt = wt;
|
||||
}
|
||||
|
||||
quantum = get_airtime_quantum(max_wt);
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
set_sta_weights(bss, bss->airtime_weight * quantum);
|
||||
}
|
||||
|
||||
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
|
||||
return;
|
||||
|
||||
eloop_register_timeout(sec, usec, update_airtime_weights, iface,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
|
||||
{
|
||||
struct airtime_sta_weight *wt;
|
||||
|
||||
wt = hapd->conf->airtime_weight_list;
|
||||
while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
|
||||
wt = wt->next;
|
||||
|
||||
return wt ? wt->weight : hapd->conf->airtime_weight;
|
||||
}
|
||||
|
||||
|
||||
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
unsigned int weight;
|
||||
|
||||
if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
|
||||
weight = get_weight_for_sta(hapd, sta->addr);
|
||||
if (weight)
|
||||
return sta_set_airtime_weight(hapd, sta, weight);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int airtime_policy_update_init(struct hostapd_iface *iface)
|
||||
{
|
||||
unsigned int sec, usec;
|
||||
|
||||
if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
|
||||
return 0;
|
||||
|
||||
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
|
||||
return -1;
|
||||
|
||||
eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void airtime_policy_update_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
eloop_cancel_timeout(update_airtime_weights, iface, NULL);
|
||||
}
|
48
hostapd-2.9/src/ap/airtime_policy.h
Normal file
48
hostapd-2.9/src/ap/airtime_policy.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Airtime policy configuration
|
||||
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AIRTIME_POLICY_H
|
||||
#define AIRTIME_POLICY_H
|
||||
|
||||
struct hostapd_iface;
|
||||
|
||||
#ifdef CONFIG_AIRTIME_POLICY
|
||||
|
||||
#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
|
||||
#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
|
||||
|
||||
/* scale quantum so this becomes the effective quantum after applying the max
|
||||
* weight, but never go below min or above max */
|
||||
#define AIRTIME_QUANTUM_MIN 8 /* usec */
|
||||
#define AIRTIME_QUANTUM_MAX 256 /* usec */
|
||||
#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
|
||||
|
||||
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int airtime_policy_update_init(struct hostapd_iface *iface);
|
||||
void airtime_policy_update_deinit(struct hostapd_iface *iface);
|
||||
|
||||
#else /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int airtime_policy_update_init(struct hostapd_iface *iface)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
#endif /* AIRTIME_POLICY_H */
|
1379
hostapd-2.9/src/ap/ap_config.c
Normal file
1379
hostapd-2.9/src/ap/ap_config.c
Normal file
File diff suppressed because it is too large
Load diff
1104
hostapd-2.9/src/ap/ap_config.h
Normal file
1104
hostapd-2.9/src/ap/ap_config.h
Normal file
File diff suppressed because it is too large
Load diff
970
hostapd-2.9/src/ap/ap_drv_ops.c
Normal file
970
hostapd-2.9/src/ap/ap_drv_ops.c
Normal file
|
@ -0,0 +1,970 @@
|
|||
/*
|
||||
* hostapd - Driver operations
|
||||
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/hw_features_common.h"
|
||||
#include "wps/wps.h"
|
||||
#include "p2p/p2p.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_config.h"
|
||||
#include "p2p_hostapd.h"
|
||||
#include "hs20.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "ap_drv_ops.h"
|
||||
|
||||
|
||||
u32 hostapd_sta_flags_to_drv(u32 flags)
|
||||
{
|
||||
int res = 0;
|
||||
if (flags & WLAN_STA_AUTHORIZED)
|
||||
res |= WPA_STA_AUTHORIZED;
|
||||
if (flags & WLAN_STA_WMM)
|
||||
res |= WPA_STA_WMM;
|
||||
if (flags & WLAN_STA_SHORT_PREAMBLE)
|
||||
res |= WPA_STA_SHORT_PREAMBLE;
|
||||
if (flags & WLAN_STA_MFP)
|
||||
res |= WPA_STA_MFP;
|
||||
if (flags & WLAN_STA_AUTH)
|
||||
res |= WPA_STA_AUTHENTICATED;
|
||||
if (flags & WLAN_STA_ASSOC)
|
||||
res |= WPA_STA_ASSOCIATED;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
|
||||
{
|
||||
if (!src)
|
||||
return 0;
|
||||
if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
|
||||
return -1;
|
||||
wpabuf_put_buf(*dst, src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
|
||||
{
|
||||
if (!data || !len)
|
||||
return 0;
|
||||
if (wpabuf_resize(dst, len) != 0)
|
||||
return -1;
|
||||
wpabuf_put_data(*dst, data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
|
||||
struct wpabuf **beacon_ret,
|
||||
struct wpabuf **proberesp_ret,
|
||||
struct wpabuf **assocresp_ret)
|
||||
{
|
||||
struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
|
||||
u8 buf[200], *pos;
|
||||
|
||||
*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
|
||||
|
||||
pos = buf;
|
||||
pos = hostapd_eid_time_adv(hapd, pos);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
pos = hostapd_eid_time_zone(hapd, pos);
|
||||
if (add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
|
||||
pos = buf;
|
||||
pos = hostapd_eid_ext_capab(hapd, pos);
|
||||
if (add_buf_data(&assocresp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
pos = hostapd_eid_interworking(hapd, pos);
|
||||
pos = hostapd_eid_adv_proto(hapd, pos);
|
||||
pos = hostapd_eid_roaming_consortium(hapd, pos);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
|
||||
#ifdef CONFIG_FST
|
||||
if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
|
||||
add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
|
||||
add_buf(&assocresp, hapd->iface->fst_ies) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
pos = hostapd_eid_fils_indic(hapd, buf, 0);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
|
||||
add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
|
||||
goto fail;
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
|
||||
add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
if (hapd->conf->p2p & P2P_MANAGE) {
|
||||
if (wpabuf_resize(&beacon, 100) == 0) {
|
||||
u8 *start, *p;
|
||||
start = wpabuf_put(beacon, 0);
|
||||
p = hostapd_eid_p2p_manage(hapd, start);
|
||||
wpabuf_put(beacon, p - start);
|
||||
}
|
||||
|
||||
if (wpabuf_resize(&proberesp, 100) == 0) {
|
||||
u8 *start, *p;
|
||||
start = wpabuf_put(proberesp, 0);
|
||||
p = hostapd_eid_p2p_manage(hapd, start);
|
||||
wpabuf_put(proberesp, p - start);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
#ifdef CONFIG_WPS
|
||||
if (hapd->conf->wps_state) {
|
||||
struct wpabuf *a = wps_build_assoc_resp_ie();
|
||||
add_buf(&assocresp, a);
|
||||
wpabuf_free(a);
|
||||
}
|
||||
#endif /* CONFIG_WPS */
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
if (hapd->conf->p2p & P2P_MANAGE) {
|
||||
if (wpabuf_resize(&assocresp, 100) == 0) {
|
||||
u8 *start, *p;
|
||||
start = wpabuf_put(assocresp, 0);
|
||||
p = hostapd_eid_p2p_manage(hapd, start);
|
||||
wpabuf_put(assocresp, p - start);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
#ifdef CONFIG_WIFI_DISPLAY
|
||||
if (hapd->p2p_group) {
|
||||
struct wpabuf *a;
|
||||
a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
|
||||
add_buf(&assocresp, a);
|
||||
wpabuf_free(a);
|
||||
}
|
||||
#endif /* CONFIG_WIFI_DISPLAY */
|
||||
|
||||
#ifdef CONFIG_HS20
|
||||
pos = hostapd_eid_hs20_indication(hapd, buf);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
|
||||
pos = hostapd_eid_osen(hapd, buf);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
if (hapd->conf->mbo_enabled ||
|
||||
OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
|
||||
pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&assocresp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
}
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
#ifdef CONFIG_OWE
|
||||
pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_OWE */
|
||||
|
||||
add_buf(&beacon, hapd->conf->vendor_elements);
|
||||
add_buf(&proberesp, hapd->conf->vendor_elements);
|
||||
add_buf(&assocresp, hapd->conf->assocresp_elements);
|
||||
|
||||
*beacon_ret = beacon;
|
||||
*proberesp_ret = proberesp;
|
||||
*assocresp_ret = assocresp;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
wpabuf_free(beacon);
|
||||
wpabuf_free(proberesp);
|
||||
wpabuf_free(assocresp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
|
||||
struct wpabuf *beacon,
|
||||
struct wpabuf *proberesp,
|
||||
struct wpabuf *assocresp)
|
||||
{
|
||||
wpabuf_free(beacon);
|
||||
wpabuf_free(proberesp);
|
||||
wpabuf_free(assocresp);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
|
||||
return 0;
|
||||
|
||||
return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
|
||||
{
|
||||
struct wpabuf *beacon, *proberesp, *assocresp;
|
||||
int ret;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
|
||||
return 0;
|
||||
|
||||
if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
|
||||
0)
|
||||
return -1;
|
||||
|
||||
ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
|
||||
assocresp);
|
||||
|
||||
hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_authorized(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized)
|
||||
{
|
||||
if (authorized) {
|
||||
return hostapd_sta_set_flags(hapd, sta->addr,
|
||||
hostapd_sta_flags_to_drv(
|
||||
sta->flags),
|
||||
WPA_STA_AUTHORIZED, ~0);
|
||||
}
|
||||
|
||||
return hostapd_sta_set_flags(hapd, sta->addr,
|
||||
hostapd_sta_flags_to_drv(sta->flags),
|
||||
0, ~WPA_STA_AUTHORIZED);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
int set_flags, total_flags, flags_and, flags_or;
|
||||
total_flags = hostapd_sta_flags_to_drv(sta->flags);
|
||||
set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
|
||||
if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
|
||||
sta->auth_alg == WLAN_AUTH_FT) &&
|
||||
sta->flags & WLAN_STA_AUTHORIZED)
|
||||
set_flags |= WPA_STA_AUTHORIZED;
|
||||
flags_or = total_flags & set_flags;
|
||||
flags_and = total_flags | ~set_flags;
|
||||
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
|
||||
flags_or, flags_and);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
|
||||
int enabled)
|
||||
{
|
||||
struct wpa_bss_params params;
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.ifname = ifname;
|
||||
params.enabled = enabled;
|
||||
if (enabled) {
|
||||
params.wpa = hapd->conf->wpa;
|
||||
params.ieee802_1x = hapd->conf->ieee802_1x;
|
||||
params.wpa_group = hapd->conf->wpa_group;
|
||||
if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
|
||||
(WPA_PROTO_WPA | WPA_PROTO_RSN))
|
||||
params.wpa_pairwise = hapd->conf->wpa_pairwise |
|
||||
hapd->conf->rsn_pairwise;
|
||||
else if (hapd->conf->wpa & WPA_PROTO_RSN)
|
||||
params.wpa_pairwise = hapd->conf->rsn_pairwise;
|
||||
else if (hapd->conf->wpa & WPA_PROTO_WPA)
|
||||
params.wpa_pairwise = hapd->conf->wpa_pairwise;
|
||||
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
|
||||
params.rsn_preauth = hapd->conf->rsn_preauth;
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
params.ieee80211w = hapd->conf->ieee80211w;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
}
|
||||
return hostapd_set_ieee8021x(hapd, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
char force_ifname[IFNAMSIZ];
|
||||
u8 if_addr[ETH_ALEN];
|
||||
return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
|
||||
NULL, NULL, force_ifname, if_addr, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
|
||||
const u8 *addr, int aid, int val)
|
||||
{
|
||||
const char *bridge = NULL;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
|
||||
return -1;
|
||||
if (hapd->conf->wds_bridge[0])
|
||||
bridge = hapd->conf->wds_bridge;
|
||||
else if (hapd->conf->bridge[0])
|
||||
bridge = hapd->conf->bridge;
|
||||
return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
|
||||
bridge, ifname_wds);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 auth_alg)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
|
||||
return 0;
|
||||
return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 seq, u16 status, const u8 *ie, size_t len)
|
||||
{
|
||||
struct wpa_driver_sta_auth_params params;
|
||||
#ifdef CONFIG_FILS
|
||||
struct sta_info *sta;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta) {
|
||||
wpa_printf(MSG_DEBUG, "Station " MACSTR
|
||||
" not found for sta_auth processing",
|
||||
MAC2STR(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
|
||||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
|
||||
sta->auth_alg == WLAN_AUTH_FILS_PK) {
|
||||
params.fils_auth = 1;
|
||||
wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
|
||||
params.fils_snonce,
|
||||
params.fils_kek,
|
||||
¶ms.fils_kek_len);
|
||||
}
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
params.own_addr = hapd->own_addr;
|
||||
params.addr = addr;
|
||||
params.seq = seq;
|
||||
params.status = status;
|
||||
params.ie = ie;
|
||||
params.len = len;
|
||||
|
||||
return hapd->driver->sta_auth(hapd->drv_priv, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||
int reassoc, u16 status, const u8 *ie, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
|
||||
return 0;
|
||||
return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
|
||||
reassoc, status, ie, len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_add(struct hostapd_data *hapd,
|
||||
const u8 *addr, u16 aid, u16 capability,
|
||||
const u8 *supp_rates, size_t supp_rates_len,
|
||||
u16 listen_interval,
|
||||
const struct ieee80211_ht_capabilities *ht_capab,
|
||||
const struct ieee80211_vht_capabilities *vht_capab,
|
||||
const struct ieee80211_he_capabilities *he_capab,
|
||||
size_t he_capab_len,
|
||||
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||
int set)
|
||||
{
|
||||
struct hostapd_sta_add_params params;
|
||||
|
||||
if (hapd->driver == NULL)
|
||||
return 0;
|
||||
if (hapd->driver->sta_add == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.addr = addr;
|
||||
params.aid = aid;
|
||||
params.capability = capability;
|
||||
params.supp_rates = supp_rates;
|
||||
params.supp_rates_len = supp_rates_len;
|
||||
params.listen_interval = listen_interval;
|
||||
params.ht_capabilities = ht_capab;
|
||||
params.vht_capabilities = vht_capab;
|
||||
params.he_capab = he_capab;
|
||||
params.he_capab_len = he_capab_len;
|
||||
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
|
||||
params.vht_opmode = vht_opmode;
|
||||
params.flags = hostapd_sta_flags_to_drv(flags);
|
||||
params.qosinfo = qosinfo;
|
||||
params.support_p2p_ps = supp_p2p_ps;
|
||||
params.set = set;
|
||||
return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 *tspec_ie, size_t tspec_ielen)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
|
||||
return 0;
|
||||
return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
|
||||
tspec_ielen);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_privacy(hapd->drv_priv, enabled);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
|
||||
size_t elem_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname, const u8 *addr, void *bss_ctx,
|
||||
void **drv_priv, char *force_ifname, u8 *if_addr,
|
||||
const char *bridge, int use_existing)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->if_add == NULL)
|
||||
return -1;
|
||||
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
|
||||
bss_ctx, drv_priv, force_ifname, if_addr,
|
||||
bridge, use_existing, 1);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->if_remove == NULL)
|
||||
return -1;
|
||||
return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
|
||||
struct wpa_bss_params *params)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
|
||||
const u8 *addr, int idx, u8 *seq)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
|
||||
return 0;
|
||||
return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
|
||||
seq);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_flush(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->flush == NULL)
|
||||
return 0;
|
||||
return hapd->driver->flush(hapd->drv_priv);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
|
||||
int freq, int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1)
|
||||
{
|
||||
struct hostapd_freq_params data;
|
||||
struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
|
||||
|
||||
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
|
||||
vht_enabled, he_enabled, sec_channel_offset,
|
||||
oper_chwidth,
|
||||
center_segment0, center_segment1,
|
||||
cmode ? cmode->vht_capab : 0,
|
||||
cmode ?
|
||||
&cmode->he_capab[IEEE80211_MODE_AP] : NULL))
|
||||
return -1;
|
||||
|
||||
if (hapd->driver == NULL)
|
||||
return 0;
|
||||
if (hapd->driver->set_freq == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_freq(hapd->drv_priv, &data);
|
||||
}
|
||||
|
||||
int hostapd_set_rts(struct hostapd_data *hapd, int rts)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_rts(hapd->drv_priv, rts);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_frag(struct hostapd_data *hapd, int frag)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_frag(hapd->drv_priv, frag);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
|
||||
int total_flags, int flags_or, int flags_and)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
|
||||
return 0;
|
||||
return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
|
||||
flags_or, flags_and);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
|
||||
unsigned int weight)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
|
||||
return 0;
|
||||
return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
|
||||
weight);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_country(struct hostapd_data *hapd, const char *country)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->set_country == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_country(hapd->drv_priv, country);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
|
||||
int cw_min, int cw_max, int burst_time)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
|
||||
cw_min, cw_max, burst_time);
|
||||
}
|
||||
|
||||
|
||||
struct hostapd_hw_modes *
|
||||
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
|
||||
u16 *flags, u8 *dfs_domain)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->get_hw_feature_data == NULL)
|
||||
return NULL;
|
||||
return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
|
||||
flags, dfs_domain);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_driver_commit(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->commit == NULL)
|
||||
return 0;
|
||||
return hapd->driver->commit(hapd->drv_priv);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_none(struct hostapd_data *hapd)
|
||||
{
|
||||
return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_driver_scan(struct hostapd_data *hapd,
|
||||
struct wpa_driver_scan_params *params)
|
||||
{
|
||||
if (hapd->driver && hapd->driver->scan2)
|
||||
return hapd->driver->scan2(hapd->drv_priv, params);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
struct wpa_scan_results * hostapd_driver_get_scan_results(
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver && hapd->driver->get_scan_results2)
|
||||
return hapd->driver->get_scan_results2(hapd->drv_priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration)
|
||||
{
|
||||
if (hapd->driver && hapd->driver->set_noa)
|
||||
return hapd->driver->set_noa(hapd->drv_priv, count, start,
|
||||
duration);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
|
||||
enum wpa_alg alg, const u8 *addr,
|
||||
int key_idx, int set_tx,
|
||||
const u8 *seq, size_t seq_len,
|
||||
const u8 *key, size_t key_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_key == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
|
||||
key_idx, set_tx, seq, seq_len, key,
|
||||
key_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack,
|
||||
const u16 *csa_offs, size_t csa_offs_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
|
||||
return 0;
|
||||
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
|
||||
csa_offs, csa_offs_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
|
||||
reason);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
|
||||
reason);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
|
||||
const u8 *peer, u8 *buf, u16 *buf_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
|
||||
return -1;
|
||||
return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
|
||||
buf_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||
unsigned int wait, const u8 *dst, const u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
const u8 *bssid;
|
||||
const u8 wildcard_bssid[ETH_ALEN] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
|
||||
return 0;
|
||||
bssid = hapd->own_addr;
|
||||
if (!is_multicast_ether_addr(dst) &&
|
||||
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
|
||||
struct sta_info *sta;
|
||||
|
||||
/*
|
||||
* Public Action frames to a STA that is not a member of the BSS
|
||||
* shall use wildcard BSSID value.
|
||||
*/
|
||||
sta = ap_get_sta(hapd, dst);
|
||||
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
|
||||
bssid = wildcard_bssid;
|
||||
} else if (is_broadcast_ether_addr(dst) &&
|
||||
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
|
||||
/*
|
||||
* The only current use case of Public Action frames with
|
||||
* broadcast destination address is DPP PKEX. That case is
|
||||
* directing all devices and not just the STAs within the BSS,
|
||||
* so have to use the wildcard BSSID value.
|
||||
*/
|
||||
bssid = wildcard_bssid;
|
||||
}
|
||||
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
|
||||
hapd->own_addr, bssid, data, len, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
|
||||
unsigned int freq,
|
||||
unsigned int wait, const u8 *dst,
|
||||
const u8 *data, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->send_action == NULL)
|
||||
return 0;
|
||||
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
|
||||
hapd->own_addr, hapd->own_addr, data,
|
||||
len, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
|
||||
enum hostapd_hw_mode mode, int freq,
|
||||
int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1)
|
||||
{
|
||||
struct hostapd_data *hapd = iface->bss[0];
|
||||
struct hostapd_freq_params data;
|
||||
int res;
|
||||
struct hostapd_hw_modes *cmode = iface->current_mode;
|
||||
|
||||
if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
|
||||
return 0;
|
||||
|
||||
if (!iface->conf->ieee80211h) {
|
||||
wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
|
||||
"is not enabled");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
|
||||
vht_enabled, he_enabled, sec_channel_offset,
|
||||
oper_chwidth, center_segment0,
|
||||
center_segment1,
|
||||
cmode->vht_capab,
|
||||
&cmode->he_capab[IEEE80211_MODE_AP])) {
|
||||
wpa_printf(MSG_ERROR, "Can't set freq params");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
|
||||
if (!res) {
|
||||
iface->cac_started = 1;
|
||||
os_get_reltime(&iface->dfs_cac_start);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
|
||||
const u8 *qos_map_set, u8 qos_map_set_len)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
|
||||
qos_map_set_len);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
|
||||
struct hostapd_hw_modes *mode,
|
||||
int acs_ch_list_all,
|
||||
int **freq_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||
|
||||
if ((acs_ch_list_all ||
|
||||
freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
|
||||
chan->chan)) &&
|
||||
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||
!(hapd->iface->conf->acs_exclude_dfs &&
|
||||
(chan->flag & HOSTAPD_CHAN_RADAR)))
|
||||
int_array_add_unique(freq_list, chan->freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hostapd_get_ext_capa(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_data *hapd = iface->bss[0];
|
||||
|
||||
if (!hapd->driver || !hapd->driver->get_ext_capab)
|
||||
return;
|
||||
|
||||
hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
|
||||
&iface->extended_capa,
|
||||
&iface->extended_capa_mask,
|
||||
&iface->extended_capa_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_do_acs(struct hostapd_data *hapd)
|
||||
{
|
||||
struct drv_acs_params params;
|
||||
int ret, i, acs_ch_list_all = 0;
|
||||
u8 *channels = NULL;
|
||||
unsigned int num_channels = 0;
|
||||
struct hostapd_hw_modes *mode;
|
||||
int *freq_list = NULL;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.hw_mode = hapd->iface->conf->hw_mode;
|
||||
|
||||
/*
|
||||
* If no chanlist config parameter is provided, include all enabled
|
||||
* channels of the selected hw_mode.
|
||||
*/
|
||||
if (!hapd->iface->conf->acs_ch_list.num)
|
||||
acs_ch_list_all = 1;
|
||||
|
||||
mode = hapd->iface->current_mode;
|
||||
if (mode) {
|
||||
channels = os_malloc(mode->num_channels);
|
||||
if (channels == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||
if (!acs_ch_list_all &&
|
||||
!freq_range_list_includes(
|
||||
&hapd->iface->conf->acs_ch_list,
|
||||
chan->chan))
|
||||
continue;
|
||||
if (hapd->iface->conf->acs_exclude_dfs &&
|
||||
(chan->flag & HOSTAPD_CHAN_RADAR))
|
||||
continue;
|
||||
if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
|
||||
channels[num_channels++] = chan->chan;
|
||||
int_array_add_unique(&freq_list, chan->freq);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||
mode = &hapd->iface->hw_features[i];
|
||||
hostapd_get_hw_mode_any_channels(hapd, mode,
|
||||
acs_ch_list_all,
|
||||
&freq_list);
|
||||
}
|
||||
}
|
||||
|
||||
params.ch_list = channels;
|
||||
params.ch_list_len = num_channels;
|
||||
params.freq_list = freq_list;
|
||||
|
||||
params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
|
||||
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
|
||||
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
|
||||
params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
|
||||
params.ch_width = 20;
|
||||
if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
|
||||
params.ch_width = 40;
|
||||
|
||||
/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
|
||||
*/
|
||||
if ((hapd->iface->conf->ieee80211ax ||
|
||||
hapd->iface->conf->ieee80211ac) &&
|
||||
params.ht40_enabled) {
|
||||
u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
|
||||
|
||||
if (oper_chwidth == CHANWIDTH_80MHZ)
|
||||
params.ch_width = 80;
|
||||
else if (oper_chwidth == CHANWIDTH_160MHZ ||
|
||||
oper_chwidth == CHANWIDTH_80P80MHZ)
|
||||
params.ch_width = 160;
|
||||
}
|
||||
|
||||
ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms);
|
||||
os_free(channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
|
||||
u16 reason_code, const u8 *ie, size_t ielen)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
|
||||
ie, ielen);
|
||||
}
|
384
hostapd-2.9/src/ap/ap_drv_ops.h
Normal file
384
hostapd-2.9/src/ap/ap_drv_ops.h
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* hostapd - Driver operations
|
||||
* Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AP_DRV_OPS
|
||||
#define AP_DRV_OPS
|
||||
|
||||
enum wpa_driver_if_type;
|
||||
struct wpa_bss_params;
|
||||
struct wpa_driver_scan_params;
|
||||
struct ieee80211_ht_capabilities;
|
||||
struct ieee80211_vht_capabilities;
|
||||
struct hostapd_freq_params;
|
||||
|
||||
u32 hostapd_sta_flags_to_drv(u32 flags);
|
||||
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
|
||||
struct wpabuf **beacon,
|
||||
struct wpabuf **proberesp,
|
||||
struct wpabuf **assocresp);
|
||||
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
|
||||
struct wpabuf *proberesp,
|
||||
struct wpabuf *assocresp);
|
||||
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
|
||||
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
|
||||
int hostapd_set_authorized(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized);
|
||||
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
|
||||
int enabled);
|
||||
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
|
||||
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
|
||||
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
|
||||
const u8 *addr, int aid, int val);
|
||||
int hostapd_sta_add(struct hostapd_data *hapd,
|
||||
const u8 *addr, u16 aid, u16 capability,
|
||||
const u8 *supp_rates, size_t supp_rates_len,
|
||||
u16 listen_interval,
|
||||
const struct ieee80211_ht_capabilities *ht_capab,
|
||||
const struct ieee80211_vht_capabilities *vht_capab,
|
||||
const struct ieee80211_he_capabilities *he_capab,
|
||||
size_t he_capab_len,
|
||||
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||
int set);
|
||||
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
|
||||
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
|
||||
size_t elem_len);
|
||||
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
|
||||
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
|
||||
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname, const u8 *addr, void *bss_ctx,
|
||||
void **drv_priv, char *force_ifname, u8 *if_addr,
|
||||
const char *bridge, int use_existing);
|
||||
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname);
|
||||
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
|
||||
struct wpa_bss_params *params);
|
||||
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
|
||||
const u8 *addr, int idx, u8 *seq);
|
||||
int hostapd_flush(struct hostapd_data *hapd);
|
||||
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
|
||||
int freq, int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled, int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1);
|
||||
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
|
||||
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
|
||||
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
|
||||
int total_flags, int flags_or, int flags_and);
|
||||
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
|
||||
unsigned int weight);
|
||||
int hostapd_set_country(struct hostapd_data *hapd, const char *country);
|
||||
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
|
||||
int cw_min, int cw_max, int burst_time);
|
||||
struct hostapd_hw_modes *
|
||||
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
|
||||
u16 *flags, u8 *dfs_domain);
|
||||
int hostapd_driver_commit(struct hostapd_data *hapd);
|
||||
int hostapd_drv_none(struct hostapd_data *hapd);
|
||||
int hostapd_driver_scan(struct hostapd_data *hapd,
|
||||
struct wpa_driver_scan_params *params);
|
||||
struct wpa_scan_results * hostapd_driver_get_scan_results(
|
||||
struct hostapd_data *hapd);
|
||||
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration);
|
||||
int hostapd_drv_set_key(const char *ifname,
|
||||
struct hostapd_data *hapd,
|
||||
enum wpa_alg alg, const u8 *addr,
|
||||
int key_idx, int set_tx,
|
||||
const u8 *seq, size_t seq_len,
|
||||
const u8 *key, size_t key_len);
|
||||
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack);
|
||||
int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack,
|
||||
const u16 *csa_offs, size_t csa_offs_len);
|
||||
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason);
|
||||
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason);
|
||||
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||
unsigned int wait, const u8 *dst, const u8 *data,
|
||||
size_t len);
|
||||
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
|
||||
unsigned int freq,
|
||||
unsigned int wait, const u8 *dst,
|
||||
const u8 *data, size_t len);
|
||||
static inline void
|
||||
hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
|
||||
!hapd->drv_priv)
|
||||
return;
|
||||
hapd->driver->send_action_cancel_wait(hapd->drv_priv);
|
||||
}
|
||||
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 auth_alg);
|
||||
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 seq, u16 status, const u8 *ie, size_t len);
|
||||
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||
int reassoc, u16 status, const u8 *ie, size_t len);
|
||||
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 *tspec_ie, size_t tspec_ielen);
|
||||
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
|
||||
enum hostapd_hw_mode mode, int freq,
|
||||
int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1);
|
||||
int hostapd_drv_do_acs(struct hostapd_data *hapd);
|
||||
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
|
||||
u16 reason_code, const u8 *ie, size_t ielen);
|
||||
|
||||
|
||||
#include "drivers/driver.h"
|
||||
|
||||
int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
|
||||
enum wnm_oper oper, const u8 *peer,
|
||||
u8 *buf, u16 *buf_len);
|
||||
|
||||
int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
|
||||
u8 qos_map_set_len);
|
||||
|
||||
void hostapd_get_ext_capa(struct hostapd_iface *iface);
|
||||
|
||||
static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
|
||||
int enabled)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->hapd_set_countermeasures == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_sta_vlan(const char *ifname,
|
||||
struct hostapd_data *hapd,
|
||||
const u8 *addr, int vlan_id)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
|
||||
vlan_id);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
|
||||
return 0;
|
||||
return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->sta_remove(hapd->drv_priv, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *data,
|
||||
size_t data_len, int encrypt,
|
||||
u32 flags)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
|
||||
data_len, encrypt,
|
||||
hapd->own_addr, flags);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_read_sta_data(
|
||||
struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
|
||||
return -1;
|
||||
return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
|
||||
return 0;
|
||||
return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
|
||||
struct hostapd_acl_params *params)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_acl(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
|
||||
struct wpa_driver_ap_params *params)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_ap(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
|
||||
const u8 *mac, int accepted,
|
||||
u32 session_timeout)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
|
||||
session_timeout);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
|
||||
const u8 *mac)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->set_radius_acl_expire == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
|
||||
int auth_algs)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
|
||||
}
|
||||
|
||||
static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
|
||||
const u8 *own_addr, const u8 *addr,
|
||||
int qos)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
|
||||
return;
|
||||
hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
|
||||
unsigned int freq)
|
||||
{
|
||||
if (hapd->driver == NULL)
|
||||
return -1;
|
||||
if (!hapd->driver->get_survey)
|
||||
return -1;
|
||||
return hapd->driver->get_survey(hapd->drv_priv, freq);
|
||||
}
|
||||
|
||||
static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->get_country == NULL)
|
||||
return -1;
|
||||
return hapd->driver->get_country(hapd->drv_priv, alpha2);
|
||||
}
|
||||
|
||||
static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->get_radio_name == NULL)
|
||||
return NULL;
|
||||
return hapd->driver->get_radio_name(hapd->drv_priv);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
|
||||
struct csa_settings *settings)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
|
||||
hapd->drv_priv == NULL)
|
||||
return -1;
|
||||
|
||||
return hapd->driver->switch_channel(hapd->drv_priv, settings);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
|
||||
return -1;
|
||||
return hapd->driver->status(hapd->drv_priv, buf, buflen);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
|
||||
int version, const u8 *ipaddr,
|
||||
int prefixlen, const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_add_ip_neigh == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
|
||||
prefixlen, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
|
||||
u8 version, const u8 *ipaddr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_delete_ip_neigh == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
|
||||
ipaddr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
|
||||
enum drv_br_port_attr attr,
|
||||
unsigned int val)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_port_set_attr == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
|
||||
enum drv_br_net_param param,
|
||||
unsigned int val)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_set_net_param == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
|
||||
int vendor_id, int subcmd,
|
||||
const u8 *data, size_t data_len,
|
||||
struct wpabuf *buf)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
|
||||
return -1;
|
||||
return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
|
||||
data_len, buf);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->stop_ap(hapd->drv_priv);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
|
||||
struct wpa_channel_info *ci)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->channel_info)
|
||||
return -1;
|
||||
return hapd->driver->channel_info(hapd->drv_priv, ci);
|
||||
}
|
||||
|
||||
static inline int
|
||||
hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
|
||||
struct external_auth *params)
|
||||
{
|
||||
if (!hapd->driver || !hapd->drv_priv ||
|
||||
!hapd->driver->send_external_auth_status)
|
||||
return -1;
|
||||
return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
#endif /* AP_DRV_OPS */
|
312
hostapd-2.9/src/ap/ap_list.c
Normal file
312
hostapd-2.9/src/ap/ap_list.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* hostapd / AP table
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright (c) 2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "beacon.h"
|
||||
#include "ap_list.h"
|
||||
|
||||
|
||||
/* AP list is a double linked list with head->prev pointing to the end of the
|
||||
* list and tail->next = NULL. Entries are moved to the head of the list
|
||||
* whenever a beacon has been received from the AP in question. The tail entry
|
||||
* in this link will thus be the least recently used entry. */
|
||||
|
||||
|
||||
static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (iface->current_mode == NULL ||
|
||||
iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
|
||||
iface->conf->channel != ap->channel)
|
||||
return 0;
|
||||
|
||||
if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
|
||||
int rate = (ap->supported_rates[i] & 0x7f) * 5;
|
||||
if (rate == 60 || rate == 90 || rate > 110)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
|
||||
{
|
||||
struct ap_info *s;
|
||||
|
||||
s = iface->ap_hash[STA_HASH(ap)];
|
||||
while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
|
||||
s = s->hnext;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
if (iface->ap_list) {
|
||||
ap->prev = iface->ap_list->prev;
|
||||
iface->ap_list->prev = ap;
|
||||
} else
|
||||
ap->prev = ap;
|
||||
ap->next = iface->ap_list;
|
||||
iface->ap_list = ap;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
if (iface->ap_list == ap)
|
||||
iface->ap_list = ap->next;
|
||||
else
|
||||
ap->prev->next = ap->next;
|
||||
|
||||
if (ap->next)
|
||||
ap->next->prev = ap->prev;
|
||||
else if (iface->ap_list)
|
||||
iface->ap_list->prev = ap->prev;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
|
||||
iface->ap_hash[STA_HASH(ap->addr)] = ap;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
struct ap_info *s;
|
||||
|
||||
s = iface->ap_hash[STA_HASH(ap->addr)];
|
||||
if (s == NULL) return;
|
||||
if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
|
||||
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
|
||||
return;
|
||||
}
|
||||
|
||||
while (s->hnext != NULL &&
|
||||
os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
|
||||
s = s->hnext;
|
||||
if (s->hnext != NULL)
|
||||
s->hnext = s->hnext->hnext;
|
||||
else
|
||||
wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
|
||||
" from hash table", MAC2STR(ap->addr));
|
||||
}
|
||||
|
||||
|
||||
static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
ap_ap_hash_del(iface, ap);
|
||||
ap_ap_list_del(iface, ap);
|
||||
|
||||
iface->num_ap--;
|
||||
os_free(ap);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_free_aps(struct hostapd_iface *iface)
|
||||
{
|
||||
struct ap_info *ap, *prev;
|
||||
|
||||
ap = iface->ap_list;
|
||||
|
||||
while (ap) {
|
||||
prev = ap;
|
||||
ap = ap->next;
|
||||
ap_free_ap(iface, prev);
|
||||
}
|
||||
|
||||
iface->ap_list = NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
|
||||
{
|
||||
struct ap_info *ap;
|
||||
|
||||
ap = os_zalloc(sizeof(struct ap_info));
|
||||
if (ap == NULL)
|
||||
return NULL;
|
||||
|
||||
/* initialize AP info data */
|
||||
os_memcpy(ap->addr, addr, ETH_ALEN);
|
||||
ap_ap_list_add(iface, ap);
|
||||
iface->num_ap++;
|
||||
ap_ap_hash_add(iface, ap);
|
||||
|
||||
if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
|
||||
wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
|
||||
MACSTR " from AP table", MAC2STR(ap->prev->addr));
|
||||
ap_free_ap(iface, ap->prev);
|
||||
}
|
||||
|
||||
return ap;
|
||||
}
|
||||
|
||||
|
||||
void ap_list_process_beacon(struct hostapd_iface *iface,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
struct ieee802_11_elems *elems,
|
||||
struct hostapd_frame_info *fi)
|
||||
{
|
||||
struct ap_info *ap;
|
||||
int new_ap = 0;
|
||||
int set_beacon = 0;
|
||||
|
||||
if (iface->conf->ap_table_max_size < 1)
|
||||
return;
|
||||
|
||||
ap = ap_get_ap(iface, mgmt->bssid);
|
||||
if (!ap) {
|
||||
ap = ap_ap_add(iface, mgmt->bssid);
|
||||
if (!ap) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Failed to allocate AP information entry");
|
||||
return;
|
||||
}
|
||||
new_ap = 1;
|
||||
}
|
||||
|
||||
merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
|
||||
elems->supp_rates, elems->supp_rates_len,
|
||||
elems->ext_supp_rates, elems->ext_supp_rates_len);
|
||||
|
||||
if (elems->erp_info)
|
||||
ap->erp = elems->erp_info[0];
|
||||
else
|
||||
ap->erp = -1;
|
||||
|
||||
if (elems->ds_params)
|
||||
ap->channel = elems->ds_params[0];
|
||||
else if (elems->ht_operation)
|
||||
ap->channel = elems->ht_operation[0];
|
||||
else if (fi)
|
||||
ap->channel = fi->channel;
|
||||
|
||||
if (elems->ht_capabilities)
|
||||
ap->ht_support = 1;
|
||||
else
|
||||
ap->ht_support = 0;
|
||||
|
||||
os_get_reltime(&ap->last_beacon);
|
||||
|
||||
if (!new_ap && ap != iface->ap_list) {
|
||||
/* move AP entry into the beginning of the list so that the
|
||||
* oldest entry is always in the end of the list */
|
||||
ap_ap_list_del(iface, ap);
|
||||
ap_ap_list_add(iface, ap);
|
||||
}
|
||||
|
||||
if (!iface->olbc &&
|
||||
ap_list_beacon_olbc(iface, ap)) {
|
||||
iface->olbc = 1;
|
||||
wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
|
||||
" (channel %d) - enable protection",
|
||||
MAC2STR(ap->addr), ap->channel);
|
||||
set_beacon++;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
if (!iface->olbc_ht && !ap->ht_support &&
|
||||
(ap->channel == 0 ||
|
||||
ap->channel == iface->conf->channel ||
|
||||
ap->channel == iface->conf->channel +
|
||||
iface->conf->secondary_channel * 4)) {
|
||||
iface->olbc_ht = 1;
|
||||
hostapd_ht_operation_update(iface);
|
||||
wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
|
||||
" (channel %d) - enable protection",
|
||||
MAC2STR(ap->addr), ap->channel);
|
||||
set_beacon++;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
if (set_beacon)
|
||||
ieee802_11_update_beacons(iface);
|
||||
}
|
||||
|
||||
|
||||
void ap_list_timer(struct hostapd_iface *iface)
|
||||
{
|
||||
struct os_reltime now;
|
||||
struct ap_info *ap;
|
||||
int set_beacon = 0;
|
||||
|
||||
if (!iface->ap_list)
|
||||
return;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
while (iface->ap_list) {
|
||||
ap = iface->ap_list->prev;
|
||||
if (!os_reltime_expired(&now, &ap->last_beacon,
|
||||
iface->conf->ap_table_expiration_time))
|
||||
break;
|
||||
|
||||
ap_free_ap(iface, ap);
|
||||
}
|
||||
|
||||
if (iface->olbc || iface->olbc_ht) {
|
||||
int olbc = 0;
|
||||
int olbc_ht = 0;
|
||||
|
||||
ap = iface->ap_list;
|
||||
while (ap && (olbc == 0 || olbc_ht == 0)) {
|
||||
if (ap_list_beacon_olbc(iface, ap))
|
||||
olbc = 1;
|
||||
if (!ap->ht_support)
|
||||
olbc_ht = 1;
|
||||
ap = ap->next;
|
||||
}
|
||||
if (!olbc && iface->olbc) {
|
||||
wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
|
||||
iface->olbc = 0;
|
||||
set_beacon++;
|
||||
}
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
if (!olbc_ht && iface->olbc_ht) {
|
||||
wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
|
||||
iface->olbc_ht = 0;
|
||||
hostapd_ht_operation_update(iface);
|
||||
set_beacon++;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
}
|
||||
|
||||
if (set_beacon)
|
||||
ieee802_11_update_beacons(iface);
|
||||
}
|
||||
|
||||
|
||||
int ap_list_init(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ap_list_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
hostapd_free_aps(iface);
|
||||
}
|
58
hostapd-2.9/src/ap/ap_list.h
Normal file
58
hostapd-2.9/src/ap/ap_list.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* hostapd / AP table
|
||||
* Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright (c) 2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AP_LIST_H
|
||||
#define AP_LIST_H
|
||||
|
||||
struct ap_info {
|
||||
/* Note: next/prev pointers are updated whenever a new beacon is
|
||||
* received because these are used to find the least recently used
|
||||
* entries. */
|
||||
struct ap_info *next; /* next entry in AP list */
|
||||
struct ap_info *prev; /* previous entry in AP list */
|
||||
struct ap_info *hnext; /* next entry in hash table list */
|
||||
u8 addr[6];
|
||||
u8 supported_rates[WLAN_SUPP_RATES_MAX];
|
||||
int erp; /* ERP Info or -1 if ERP info element not present */
|
||||
|
||||
int channel;
|
||||
|
||||
int ht_support;
|
||||
|
||||
struct os_reltime last_beacon;
|
||||
};
|
||||
|
||||
struct ieee802_11_elems;
|
||||
struct hostapd_frame_info;
|
||||
|
||||
void ap_list_process_beacon(struct hostapd_iface *iface,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
struct ieee802_11_elems *elems,
|
||||
struct hostapd_frame_info *fi);
|
||||
#ifdef NEED_AP_MLME
|
||||
int ap_list_init(struct hostapd_iface *iface);
|
||||
void ap_list_deinit(struct hostapd_iface *iface);
|
||||
void ap_list_timer(struct hostapd_iface *iface);
|
||||
#else /* NEED_AP_MLME */
|
||||
static inline int ap_list_init(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ap_list_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ap_list_timer(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
#endif /* NEED_AP_MLME */
|
||||
|
||||
#endif /* AP_LIST_H */
|
191
hostapd-2.9/src/ap/ap_mlme.c
Normal file
191
hostapd-2.9/src/ap/ap_mlme.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11 MLME
|
||||
* Copyright 2003-2006, Jouni Malinen <j@w1.fi>
|
||||
* Copyright 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_mlme.h"
|
||||
#include "hostapd.h"
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_HOSTAPD_LOGGER
|
||||
static const char * mlme_auth_alg_str(int alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case WLAN_AUTH_OPEN:
|
||||
return "OPEN_SYSTEM";
|
||||
case WLAN_AUTH_SHARED_KEY:
|
||||
return "SHARED_KEY";
|
||||
case WLAN_AUTH_FT:
|
||||
return "FT";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
|
||||
|
||||
|
||||
/**
|
||||
* mlme_authenticate_indication - Report the establishment of an authentication
|
||||
* relationship with a specific peer MAC entity
|
||||
* @hapd: BSS data
|
||||
* @sta: peer STA data
|
||||
*
|
||||
* MLME calls this function as a result of the establishment of an
|
||||
* authentication relationship with a specific peer MAC entity that
|
||||
* resulted from an authentication procedure that was initiated by
|
||||
* that specific peer MAC entity.
|
||||
*
|
||||
* PeerSTAAddress = sta->addr
|
||||
* AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
|
||||
*/
|
||||
void mlme_authenticate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
|
||||
MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
|
||||
if (sta->auth_alg != WLAN_AUTH_FT &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_SK &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_PK &&
|
||||
!(sta->flags & WLAN_STA_MFP))
|
||||
mlme_deletekeys_request(hapd, sta);
|
||||
ap_sta_clear_disconnect_timeouts(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mlme_deauthenticate_indication - Report the invalidation of an
|
||||
* authentication relationship with a specific peer MAC entity
|
||||
* @hapd: BSS data
|
||||
* @sta: Peer STA data
|
||||
* @reason_code: ReasonCode from Deauthentication frame
|
||||
*
|
||||
* MLME calls this function as a result of the invalidation of an
|
||||
* authentication relationship with a specific peer MAC entity.
|
||||
*
|
||||
* PeerSTAAddress = sta->addr
|
||||
*/
|
||||
void mlme_deauthenticate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, u16 reason_code)
|
||||
{
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
|
||||
MAC2STR(sta->addr), reason_code);
|
||||
if (!hapd->iface->driver_ap_teardown)
|
||||
mlme_deletekeys_request(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mlme_associate_indication - Report the establishment of an association with
|
||||
* a specific peer MAC entity
|
||||
* @hapd: BSS data
|
||||
* @sta: peer STA data
|
||||
*
|
||||
* MLME calls this function as a result of the establishment of an
|
||||
* association with a specific peer MAC entity that resulted from an
|
||||
* association procedure that was initiated by that specific peer MAC entity.
|
||||
*
|
||||
* PeerSTAAddress = sta->addr
|
||||
*/
|
||||
void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-ASSOCIATE.indication(" MACSTR ")",
|
||||
MAC2STR(sta->addr));
|
||||
if (sta->auth_alg != WLAN_AUTH_FT &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_SK &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_PK)
|
||||
mlme_deletekeys_request(hapd, sta);
|
||||
ap_sta_clear_disconnect_timeouts(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mlme_reassociate_indication - Report the establishment of an reassociation
|
||||
* with a specific peer MAC entity
|
||||
* @hapd: BSS data
|
||||
* @sta: peer STA data
|
||||
*
|
||||
* MLME calls this function as a result of the establishment of an
|
||||
* reassociation with a specific peer MAC entity that resulted from a
|
||||
* reassociation procedure that was initiated by that specific peer MAC entity.
|
||||
*
|
||||
* PeerSTAAddress = sta->addr
|
||||
*/
|
||||
void mlme_reassociate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-REASSOCIATE.indication(" MACSTR ")",
|
||||
MAC2STR(sta->addr));
|
||||
if (sta->auth_alg != WLAN_AUTH_FT &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_SK &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
|
||||
sta->auth_alg != WLAN_AUTH_FILS_PK)
|
||||
mlme_deletekeys_request(hapd, sta);
|
||||
ap_sta_clear_disconnect_timeouts(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mlme_disassociate_indication - Report disassociation with a specific peer
|
||||
* MAC entity
|
||||
* @hapd: BSS data
|
||||
* @sta: Peer STA data
|
||||
* @reason_code: ReasonCode from Disassociation frame
|
||||
*
|
||||
* MLME calls this function as a result of the invalidation of an association
|
||||
* relationship with a specific peer MAC entity.
|
||||
*
|
||||
* PeerSTAAddress = sta->addr
|
||||
*/
|
||||
void mlme_disassociate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, u16 reason_code)
|
||||
{
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
|
||||
MAC2STR(sta->addr), reason_code);
|
||||
mlme_deletekeys_request(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-MichaelMICFailure.indication(" MACSTR ")",
|
||||
MAC2STR(addr));
|
||||
}
|
||||
|
||||
|
||||
void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"MLME-DELETEKEYS.request(" MACSTR ")",
|
||||
MAC2STR(sta->addr));
|
||||
|
||||
if (sta->wpa_sm)
|
||||
wpa_remove_ptk(sta->wpa_sm);
|
||||
}
|
34
hostapd-2.9/src/ap/ap_mlme.h
Normal file
34
hostapd-2.9/src/ap/ap_mlme.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11 MLME
|
||||
* Copyright 2003, Jouni Malinen <j@w1.fi>
|
||||
* Copyright 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef MLME_H
|
||||
#define MLME_H
|
||||
|
||||
void mlme_authenticate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
void mlme_deauthenticate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, u16 reason_code);
|
||||
|
||||
void mlme_associate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
void mlme_reassociate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
void mlme_disassociate_indication(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, u16 reason_code);
|
||||
|
||||
void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
|
||||
const u8 *addr);
|
||||
|
||||
void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
#endif /* MLME_H */
|
306
hostapd-2.9/src/ap/authsrv.c
Normal file
306
hostapd-2.9/src/ap/authsrv.c
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* Authentication server setup
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "crypto/tls.h"
|
||||
#include "eap_server/eap.h"
|
||||
#include "eap_server/eap_sim_db.h"
|
||||
#include "eapol_auth/eapol_auth_sm.h"
|
||||
#include "radius/radius_server.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "sta_info.h"
|
||||
#include "authsrv.h"
|
||||
|
||||
|
||||
#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
|
||||
#define EAP_SIM_DB
|
||||
#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
|
||||
|
||||
|
||||
#ifdef EAP_SIM_DB
|
||||
static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, void *ctx)
|
||||
{
|
||||
if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
|
||||
#ifdef RADIUS_SERVER
|
||||
radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
|
||||
#endif /* RADIUS_SERVER */
|
||||
}
|
||||
}
|
||||
#endif /* EAP_SIM_DB */
|
||||
|
||||
|
||||
#ifdef RADIUS_SERVER
|
||||
|
||||
static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
|
||||
size_t identity_len, int phase2,
|
||||
struct eap_user *user)
|
||||
{
|
||||
const struct hostapd_eap_user *eap_user;
|
||||
int i;
|
||||
int rv = -1;
|
||||
|
||||
eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
|
||||
if (eap_user == NULL)
|
||||
goto out;
|
||||
|
||||
if (user == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(user, 0, sizeof(*user));
|
||||
for (i = 0; i < EAP_MAX_METHODS; i++) {
|
||||
user->methods[i].vendor = eap_user->methods[i].vendor;
|
||||
user->methods[i].method = eap_user->methods[i].method;
|
||||
}
|
||||
|
||||
if (eap_user->password) {
|
||||
user->password = os_memdup(eap_user->password,
|
||||
eap_user->password_len);
|
||||
if (user->password == NULL)
|
||||
goto out;
|
||||
user->password_len = eap_user->password_len;
|
||||
user->password_hash = eap_user->password_hash;
|
||||
if (eap_user->salt && eap_user->salt_len) {
|
||||
user->salt = os_memdup(eap_user->salt,
|
||||
eap_user->salt_len);
|
||||
if (!user->salt)
|
||||
goto out;
|
||||
user->salt_len = eap_user->salt_len;
|
||||
}
|
||||
}
|
||||
user->force_version = eap_user->force_version;
|
||||
user->macacl = eap_user->macacl;
|
||||
user->ttls_auth = eap_user->ttls_auth;
|
||||
user->remediation = eap_user->remediation;
|
||||
user->accept_attr = eap_user->accept_attr;
|
||||
user->t_c_timestamp = eap_user->t_c_timestamp;
|
||||
rv = 0;
|
||||
|
||||
out:
|
||||
if (rv)
|
||||
wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
|
||||
{
|
||||
struct radius_server_conf srv;
|
||||
struct hostapd_bss_config *conf = hapd->conf;
|
||||
os_memset(&srv, 0, sizeof(srv));
|
||||
srv.client_file = conf->radius_server_clients;
|
||||
srv.auth_port = conf->radius_server_auth_port;
|
||||
srv.acct_port = conf->radius_server_acct_port;
|
||||
srv.conf_ctx = hapd;
|
||||
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
|
||||
srv.ssl_ctx = hapd->ssl_ctx;
|
||||
srv.msg_ctx = hapd->msg_ctx;
|
||||
srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
|
||||
srv.eap_fast_a_id = conf->eap_fast_a_id;
|
||||
srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
|
||||
srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
|
||||
srv.eap_fast_prov = conf->eap_fast_prov;
|
||||
srv.pac_key_lifetime = conf->pac_key_lifetime;
|
||||
srv.pac_key_refresh_time = conf->pac_key_refresh_time;
|
||||
srv.eap_teap_auth = conf->eap_teap_auth;
|
||||
srv.eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
|
||||
srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
|
||||
srv.eap_sim_id = conf->eap_sim_id;
|
||||
srv.tnc = conf->tnc;
|
||||
srv.wps = hapd->wps;
|
||||
srv.ipv6 = conf->radius_server_ipv6;
|
||||
srv.get_eap_user = hostapd_radius_get_eap_user;
|
||||
srv.eap_req_id_text = conf->eap_req_id_text;
|
||||
srv.eap_req_id_text_len = conf->eap_req_id_text_len;
|
||||
srv.pwd_group = conf->pwd_group;
|
||||
srv.server_id = conf->server_id ? conf->server_id : "hostapd";
|
||||
srv.sqlite_file = conf->eap_user_sqlite;
|
||||
#ifdef CONFIG_RADIUS_TEST
|
||||
srv.dump_msk_file = conf->dump_msk_file;
|
||||
#endif /* CONFIG_RADIUS_TEST */
|
||||
#ifdef CONFIG_HS20
|
||||
srv.subscr_remediation_url = conf->subscr_remediation_url;
|
||||
srv.subscr_remediation_method = conf->subscr_remediation_method;
|
||||
srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
|
||||
srv.t_c_server_url = conf->t_c_server_url;
|
||||
#endif /* CONFIG_HS20 */
|
||||
srv.erp = conf->eap_server_erp;
|
||||
srv.erp_domain = conf->erp_domain;
|
||||
srv.tls_session_lifetime = conf->tls_session_lifetime;
|
||||
srv.tls_flags = conf->tls_flags;
|
||||
|
||||
hapd->radius_srv = radius_server_init(&srv);
|
||||
if (hapd->radius_srv == NULL) {
|
||||
wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* RADIUS_SERVER */
|
||||
|
||||
|
||||
#ifdef EAP_TLS_FUNCS
|
||||
static void authsrv_tls_event(void *ctx, enum tls_event ev,
|
||||
union tls_event_data *data)
|
||||
{
|
||||
switch (ev) {
|
||||
case TLS_CERT_CHAIN_SUCCESS:
|
||||
wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
|
||||
break;
|
||||
case TLS_CERT_CHAIN_FAILURE:
|
||||
wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
|
||||
data->cert_fail.reason,
|
||||
data->cert_fail.depth,
|
||||
data->cert_fail.subject,
|
||||
data->cert_fail.reason_txt);
|
||||
break;
|
||||
case TLS_PEER_CERTIFICATE:
|
||||
wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
|
||||
data->peer_cert.depth,
|
||||
data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
|
||||
data->peer_cert.subject);
|
||||
break;
|
||||
case TLS_ALERT:
|
||||
if (data->alert.is_local)
|
||||
wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
|
||||
data->alert.description);
|
||||
else
|
||||
wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
|
||||
data->alert.description);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* EAP_TLS_FUNCS */
|
||||
|
||||
|
||||
int authsrv_init(struct hostapd_data *hapd)
|
||||
{
|
||||
#ifdef EAP_TLS_FUNCS
|
||||
if (hapd->conf->eap_server &&
|
||||
(hapd->conf->ca_cert || hapd->conf->server_cert ||
|
||||
hapd->conf->private_key || hapd->conf->dh_file ||
|
||||
hapd->conf->server_cert2 || hapd->conf->private_key2)) {
|
||||
struct tls_config conf;
|
||||
struct tls_connection_params params;
|
||||
|
||||
os_memset(&conf, 0, sizeof(conf));
|
||||
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
|
||||
if (hapd->conf->crl_reload_interval > 0 &&
|
||||
hapd->conf->check_crl <= 0) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Cannot enable CRL reload functionality - it depends on check_crl being set");
|
||||
} else if (hapd->conf->crl_reload_interval > 0) {
|
||||
conf.crl_reload_interval =
|
||||
hapd->conf->crl_reload_interval;
|
||||
wpa_printf(MSG_INFO,
|
||||
"Enabled CRL reload functionality");
|
||||
}
|
||||
conf.tls_flags = hapd->conf->tls_flags;
|
||||
conf.event_cb = authsrv_tls_event;
|
||||
conf.cb_ctx = hapd;
|
||||
hapd->ssl_ctx = tls_init(&conf);
|
||||
if (hapd->ssl_ctx == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize TLS");
|
||||
authsrv_deinit(hapd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.ca_cert = hapd->conf->ca_cert;
|
||||
params.client_cert = hapd->conf->server_cert;
|
||||
params.client_cert2 = hapd->conf->server_cert2;
|
||||
params.private_key = hapd->conf->private_key;
|
||||
params.private_key2 = hapd->conf->private_key2;
|
||||
params.private_key_passwd = hapd->conf->private_key_passwd;
|
||||
params.private_key_passwd2 = hapd->conf->private_key_passwd2;
|
||||
params.dh_file = hapd->conf->dh_file;
|
||||
params.openssl_ciphers = hapd->conf->openssl_ciphers;
|
||||
params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
|
||||
params.ocsp_stapling_response =
|
||||
hapd->conf->ocsp_stapling_response;
|
||||
params.ocsp_stapling_response_multi =
|
||||
hapd->conf->ocsp_stapling_response_multi;
|
||||
params.check_cert_subject = hapd->conf->check_cert_subject;
|
||||
|
||||
if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) {
|
||||
wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
|
||||
authsrv_deinit(hapd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tls_global_set_verify(hapd->ssl_ctx,
|
||||
hapd->conf->check_crl,
|
||||
hapd->conf->check_crl_strict)) {
|
||||
wpa_printf(MSG_ERROR, "Failed to enable check_crl");
|
||||
authsrv_deinit(hapd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* EAP_TLS_FUNCS */
|
||||
|
||||
#ifdef EAP_SIM_DB
|
||||
if (hapd->conf->eap_sim_db) {
|
||||
hapd->eap_sim_db_priv =
|
||||
eap_sim_db_init(hapd->conf->eap_sim_db,
|
||||
hapd->conf->eap_sim_db_timeout,
|
||||
hostapd_sim_db_cb, hapd);
|
||||
if (hapd->eap_sim_db_priv == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
|
||||
"database interface");
|
||||
authsrv_deinit(hapd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* EAP_SIM_DB */
|
||||
|
||||
#ifdef RADIUS_SERVER
|
||||
if (hapd->conf->radius_server_clients &&
|
||||
hostapd_setup_radius_srv(hapd))
|
||||
return -1;
|
||||
#endif /* RADIUS_SERVER */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void authsrv_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
#ifdef RADIUS_SERVER
|
||||
radius_server_deinit(hapd->radius_srv);
|
||||
hapd->radius_srv = NULL;
|
||||
#endif /* RADIUS_SERVER */
|
||||
|
||||
#ifdef EAP_TLS_FUNCS
|
||||
if (hapd->ssl_ctx) {
|
||||
tls_deinit(hapd->ssl_ctx);
|
||||
hapd->ssl_ctx = NULL;
|
||||
}
|
||||
#endif /* EAP_TLS_FUNCS */
|
||||
|
||||
#ifdef EAP_SIM_DB
|
||||
if (hapd->eap_sim_db_priv) {
|
||||
eap_sim_db_deinit(hapd->eap_sim_db_priv);
|
||||
hapd->eap_sim_db_priv = NULL;
|
||||
}
|
||||
#endif /* EAP_SIM_DB */
|
||||
}
|
15
hostapd-2.9/src/ap/authsrv.h
Normal file
15
hostapd-2.9/src/ap/authsrv.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Authentication server setup
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AUTHSRV_H
|
||||
#define AUTHSRV_H
|
||||
|
||||
int authsrv_init(struct hostapd_data *hapd);
|
||||
void authsrv_deinit(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* AUTHSRV_H */
|
1485
hostapd-2.9/src/ap/beacon.c
Normal file
1485
hostapd-2.9/src/ap/beacon.c
Normal file
File diff suppressed because it is too large
Load diff
33
hostapd-2.9/src/ap/beacon.h
Normal file
33
hostapd-2.9/src/ap/beacon.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
|
||||
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
|
||||
* Copyright (c) 2005-2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef BEACON_H
|
||||
#define BEACON_H
|
||||
|
||||
struct ieee80211_mgmt;
|
||||
|
||||
void handle_probe_req(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len,
|
||||
int ssi_signal);
|
||||
int ieee802_11_set_beacon(struct hostapd_data *hapd);
|
||||
int ieee802_11_set_beacons(struct hostapd_iface *iface);
|
||||
int ieee802_11_update_beacons(struct hostapd_iface *iface);
|
||||
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
|
||||
struct wpa_driver_ap_params *params);
|
||||
void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
|
||||
void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
|
||||
void sta_track_del(struct hostapd_sta_info *info);
|
||||
void sta_track_expire(struct hostapd_iface *iface, int force);
|
||||
struct hostapd_data *
|
||||
sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
|
||||
const char *ifname);
|
||||
void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
|
||||
struct wpabuf **probe_ie_taxonomy);
|
||||
|
||||
#endif /* BEACON_H */
|
99
hostapd-2.9/src/ap/bss_load.c
Normal file
99
hostapd-2.9/src/ap/bss_load.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* BSS Load Element / Channel Utilization
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "hostapd.h"
|
||||
#include "bss_load.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "beacon.h"
|
||||
|
||||
|
||||
static int get_bss_load_update_timeout(struct hostapd_data *hapd,
|
||||
unsigned int *sec, unsigned int *usec)
|
||||
{
|
||||
unsigned int update_period = hapd->conf->bss_load_update_period;
|
||||
unsigned int beacon_int = hapd->iconf->beacon_int;
|
||||
unsigned int update_timeout;
|
||||
|
||||
if (!update_period || !beacon_int) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
|
||||
update_period, beacon_int);
|
||||
return -1;
|
||||
}
|
||||
|
||||
update_timeout = update_period * beacon_int;
|
||||
|
||||
*sec = ((update_timeout / 1000) * 1024) / 1000;
|
||||
*usec = (update_timeout % 1000) * 1024;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void update_channel_utilization(void *eloop_data, void *user_data)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_data;
|
||||
unsigned int sec, usec;
|
||||
int err;
|
||||
struct hostapd_iface *iface = hapd->iface;
|
||||
|
||||
if (!(hapd->beacon_set_done && hapd->started))
|
||||
return;
|
||||
|
||||
err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
|
||||
if (err) {
|
||||
wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
|
||||
return;
|
||||
}
|
||||
|
||||
ieee802_11_set_beacon(hapd);
|
||||
|
||||
if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
|
||||
return;
|
||||
|
||||
if (hapd->conf->chan_util_avg_period) {
|
||||
iface->chan_util_samples_sum += iface->channel_utilization;
|
||||
iface->chan_util_num_sample_periods +=
|
||||
hapd->conf->bss_load_update_period;
|
||||
if (iface->chan_util_num_sample_periods >=
|
||||
hapd->conf->chan_util_avg_period) {
|
||||
iface->chan_util_average =
|
||||
iface->chan_util_samples_sum /
|
||||
(iface->chan_util_num_sample_periods /
|
||||
hapd->conf->bss_load_update_period);
|
||||
iface->chan_util_samples_sum = 0;
|
||||
iface->chan_util_num_sample_periods = 0;
|
||||
}
|
||||
}
|
||||
|
||||
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
int bss_load_update_init(struct hostapd_data *hapd)
|
||||
{
|
||||
unsigned int sec, usec;
|
||||
|
||||
if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
|
||||
return -1;
|
||||
|
||||
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
|
||||
NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void bss_load_update_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
|
||||
}
|
17
hostapd-2.9/src/ap/bss_load.h
Normal file
17
hostapd-2.9/src/ap/bss_load.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* BSS load update
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef BSS_LOAD_UPDATE_H
|
||||
#define BSS_LOAD_UPDATE_H
|
||||
|
||||
|
||||
int bss_load_update_init(struct hostapd_data *hapd);
|
||||
void bss_load_update_deinit(struct hostapd_data *hapd);
|
||||
|
||||
|
||||
#endif /* BSS_LOAD_UPDATE_H */
|
996
hostapd-2.9/src/ap/ctrl_iface_ap.c
Normal file
996
hostapd-2.9/src/ap/ctrl_iface_ap.c
Normal file
|
@ -0,0 +1,996 @@
|
|||
/*
|
||||
* Control interface for shared AP commands
|
||||
* Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/sae.h"
|
||||
#include "eapol_auth/eapol_auth_sm.h"
|
||||
#include "fst/fst_ctrl_iface.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_1x.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "wps_hostapd.h"
|
||||
#include "p2p_hostapd.h"
|
||||
#include "ctrl_iface_ap.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "mbo_ap.h"
|
||||
#include "taxonomy.h"
|
||||
|
||||
|
||||
static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
|
||||
size_t curr_len, const u8 *mcs_set)
|
||||
{
|
||||
int ret;
|
||||
size_t len = curr_len;
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"ht_mcs_bitmask=");
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
/* 77 first bits (+ 3 reserved bits) */
|
||||
len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return curr_len;
|
||||
len += ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
struct hostap_sta_driver_data data;
|
||||
int ret;
|
||||
int len = 0;
|
||||
|
||||
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
|
||||
return 0;
|
||||
|
||||
ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
|
||||
"rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
|
||||
"signal=%d\n",
|
||||
data.rx_packets, data.tx_packets,
|
||||
data.rx_bytes, data.tx_bytes, data.inactive_msec,
|
||||
data.signal);
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
return 0;
|
||||
len += ret;
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
|
||||
data.current_rx_rate);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
if (data.flags & STA_DRV_DATA_RX_MCS) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " mcs %u",
|
||||
data.rx_mcs);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
|
||||
data.rx_vhtmcs);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
|
||||
data.rx_vht_nss);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " shortGI");
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
|
||||
data.current_tx_rate);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
if (data.flags & STA_DRV_DATA_TX_MCS) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " mcs %u",
|
||||
data.tx_mcs);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
|
||||
data.tx_vhtmcs);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
|
||||
data.tx_vht_nss);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
|
||||
ret = os_snprintf(buf + len, buflen - len, " shortGI");
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
|
||||
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"rx_vht_mcs_map=%04x\n"
|
||||
"tx_vht_mcs_map=%04x\n",
|
||||
le_to_host16(sta->vht_capabilities->
|
||||
vht_supported_mcs_set.rx_map),
|
||||
le_to_host16(sta->vht_capabilities->
|
||||
vht_supported_mcs_set.tx_map));
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
|
||||
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
|
||||
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
|
||||
sta->ht_capabilities->
|
||||
supported_mcs_set);
|
||||
}
|
||||
|
||||
if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"last_ack_signal=%d\n", data.last_ack_rssi);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_get_sta_conn_time(struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
struct os_reltime age;
|
||||
int ret;
|
||||
|
||||
if (!sta->connected_time.sec)
|
||||
return 0;
|
||||
|
||||
os_reltime_age(&sta->connected_time, &age);
|
||||
|
||||
ret = os_snprintf(buf, buflen, "connected_time=%u\n",
|
||||
(unsigned int) age.sec);
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const char * timeout_next_str(int val)
|
||||
{
|
||||
switch (val) {
|
||||
case STA_NULLFUNC:
|
||||
return "NULLFUNC POLL";
|
||||
case STA_DISASSOC:
|
||||
return "DISASSOC";
|
||||
case STA_DEAUTH:
|
||||
return "DEAUTH";
|
||||
case STA_REMOVE:
|
||||
return "REMOVE";
|
||||
case STA_DISASSOC_FROM_CLI:
|
||||
return "DISASSOC_FROM_CLI";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
int len, res, ret, i;
|
||||
const char *keyid;
|
||||
|
||||
if (!sta)
|
||||
return 0;
|
||||
|
||||
len = 0;
|
||||
ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
|
||||
MAC2STR(sta->addr));
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
|
||||
if (ret < 0)
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
|
||||
"listen_interval=%d\nsupported_rates=",
|
||||
sta->aid, sta->capability, sta->listen_interval);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
for (i = 0; i < sta->supported_rates_len; i++) {
|
||||
ret = os_snprintf(buf + len, buflen - len, "%02x%s",
|
||||
sta->supported_rates[i],
|
||||
i + 1 < sta->supported_rates_len ? " " : "");
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
|
||||
timeout_next_str(sta->timeout_next));
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
|
||||
if (res >= 0)
|
||||
len += res;
|
||||
res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
|
||||
if (res >= 0)
|
||||
len += res;
|
||||
res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
|
||||
if (res >= 0)
|
||||
len += res;
|
||||
res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
|
||||
buflen - len);
|
||||
if (res >= 0)
|
||||
len += res;
|
||||
res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
|
||||
if (res >= 0)
|
||||
len += res;
|
||||
|
||||
len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
|
||||
len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
|
||||
|
||||
#ifdef CONFIG_SAE
|
||||
if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
|
||||
res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
|
||||
sta->sae->group);
|
||||
if (!os_snprintf_error(buflen - len, res))
|
||||
len += res;
|
||||
}
|
||||
#endif /* CONFIG_SAE */
|
||||
|
||||
if (sta->vlan_id > 0) {
|
||||
res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
|
||||
sta->vlan_id);
|
||||
if (!os_snprintf_error(buflen - len, res))
|
||||
len += res;
|
||||
}
|
||||
|
||||
res = mbo_ap_get_info(sta, buf + len, buflen - len);
|
||||
if (res >= 0)
|
||||
len += res;
|
||||
|
||||
if (sta->supp_op_classes &&
|
||||
buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
|
||||
len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
|
||||
len += wpa_snprintf_hex(buf + len, buflen - len,
|
||||
sta->supp_op_classes + 1,
|
||||
sta->supp_op_classes[0]);
|
||||
len += os_snprintf(buf + len, buflen - len, "\n");
|
||||
}
|
||||
|
||||
if (sta->power_capab) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"min_txpower=%d\n"
|
||||
"max_txpower=%d\n",
|
||||
sta->min_tx_power, sta->max_tx_power);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211AC
|
||||
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
|
||||
res = os_snprintf(buf + len, buflen - len,
|
||||
"vht_caps_info=0x%08x\n",
|
||||
le_to_host32(sta->vht_capabilities->
|
||||
vht_capabilities_info));
|
||||
if (!os_snprintf_error(buflen - len, res))
|
||||
len += res;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211AC */
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
|
||||
res = os_snprintf(buf + len, buflen - len,
|
||||
"ht_caps_info=0x%04x\n",
|
||||
le_to_host16(sta->ht_capabilities->
|
||||
ht_capabilities_info));
|
||||
if (!os_snprintf_error(buflen - len, res))
|
||||
len += res;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
if (sta->ext_capability &&
|
||||
buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
|
||||
len += os_snprintf(buf + len, buflen - len, "ext_capab=");
|
||||
len += wpa_snprintf_hex(buf + len, buflen - len,
|
||||
sta->ext_capability + 1,
|
||||
sta->ext_capability[0]);
|
||||
len += os_snprintf(buf + len, buflen - len, "\n");
|
||||
}
|
||||
|
||||
if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"wds_sta_ifname=%s\n", sta->ifname_wds);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
|
||||
keyid = ap_sta_wpa_get_keyid(hapd, sta);
|
||||
if (keyid) {
|
||||
ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
|
||||
if (!os_snprintf_error(buflen - len, ret))
|
||||
len += ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
int ret;
|
||||
const char *pos;
|
||||
struct sta_info *sta;
|
||||
|
||||
if (hwaddr_aton(txtaddr, addr)) {
|
||||
ret = os_snprintf(buf, buflen, "FAIL\n");
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (sta == NULL)
|
||||
return -1;
|
||||
|
||||
pos = os_strchr(txtaddr, ' ');
|
||||
if (pos) {
|
||||
pos++;
|
||||
|
||||
#ifdef HOSTAPD_DUMP_STATE
|
||||
if (os_strcmp(pos, "eapol") == 0) {
|
||||
if (sta->eapol_sm == NULL)
|
||||
return -1;
|
||||
return eapol_auth_dump_state(sta->eapol_sm, buf,
|
||||
buflen);
|
||||
}
|
||||
#endif /* HOSTAPD_DUMP_STATE */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
|
||||
ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
if (hwaddr_aton(txtaddr, addr) ||
|
||||
(sta = ap_get_sta(hapd, addr)) == NULL) {
|
||||
ret = os_snprintf(buf, buflen, "FAIL\n");
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!sta->next)
|
||||
return 0;
|
||||
|
||||
return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
|
||||
u8 minor_reason_code, const u8 *addr)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
int ret;
|
||||
u8 *pos;
|
||||
|
||||
if (!hapd->drv_priv || !hapd->driver->send_frame)
|
||||
return -1;
|
||||
|
||||
mgmt = os_zalloc(sizeof(*mgmt) + 100);
|
||||
if (mgmt == NULL)
|
||||
return -1;
|
||||
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
|
||||
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
|
||||
" with minor reason code %u (stype=%u (%s))",
|
||||
MAC2STR(addr), minor_reason_code, stype,
|
||||
fc2str(le_to_host16(mgmt->frame_control)));
|
||||
|
||||
os_memcpy(mgmt->da, addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
if (stype == WLAN_FC_STYPE_DEAUTH) {
|
||||
mgmt->u.deauth.reason_code =
|
||||
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||
pos = mgmt->u.deauth.variable;
|
||||
} else {
|
||||
mgmt->u.disassoc.reason_code =
|
||||
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||
pos = mgmt->u.disassoc.variable;
|
||||
}
|
||||
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = 4 + 3 + 1;
|
||||
WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
|
||||
pos += 4;
|
||||
|
||||
*pos++ = P2P_ATTR_MINOR_REASON_CODE;
|
||||
WPA_PUT_LE16(pos, 1);
|
||||
pos += 2;
|
||||
*pos++ = minor_reason_code;
|
||||
|
||||
ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
|
||||
pos - (u8 *) mgmt, 1);
|
||||
os_free(mgmt);
|
||||
|
||||
return ret < 0 ? -1 : 0;
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
|
||||
const char *txtaddr)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
struct sta_info *sta;
|
||||
const char *pos;
|
||||
u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
||||
|
||||
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
|
||||
txtaddr);
|
||||
|
||||
if (hwaddr_aton(txtaddr, addr))
|
||||
return -1;
|
||||
|
||||
pos = os_strstr(txtaddr, " reason=");
|
||||
if (pos)
|
||||
reason = atoi(pos + 8);
|
||||
|
||||
pos = os_strstr(txtaddr, " test=");
|
||||
if (pos) {
|
||||
struct ieee80211_mgmt mgmt;
|
||||
int encrypt;
|
||||
if (!hapd->drv_priv || !hapd->driver->send_frame)
|
||||
return -1;
|
||||
pos += 6;
|
||||
encrypt = atoi(pos);
|
||||
os_memset(&mgmt, 0, sizeof(mgmt));
|
||||
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_DEAUTH);
|
||||
os_memcpy(mgmt.da, addr, ETH_ALEN);
|
||||
os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt.u.deauth.reason_code = host_to_le16(reason);
|
||||
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
|
||||
IEEE80211_HDRLEN +
|
||||
sizeof(mgmt.u.deauth),
|
||||
encrypt) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
pos = os_strstr(txtaddr, " p2p=");
|
||||
if (pos) {
|
||||
return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
|
||||
atoi(pos + 5), addr);
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
if (os_strstr(txtaddr, " tx=0"))
|
||||
hostapd_drv_sta_remove(hapd, addr);
|
||||
else
|
||||
hostapd_drv_sta_deauth(hapd, addr, reason);
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (sta)
|
||||
ap_sta_deauthenticate(hapd, sta, reason);
|
||||
else if (addr[0] == 0xff)
|
||||
hostapd_free_stas(hapd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
|
||||
const char *txtaddr)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
struct sta_info *sta;
|
||||
const char *pos;
|
||||
u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
||||
|
||||
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
|
||||
txtaddr);
|
||||
|
||||
if (hwaddr_aton(txtaddr, addr))
|
||||
return -1;
|
||||
|
||||
pos = os_strstr(txtaddr, " reason=");
|
||||
if (pos)
|
||||
reason = atoi(pos + 8);
|
||||
|
||||
pos = os_strstr(txtaddr, " test=");
|
||||
if (pos) {
|
||||
struct ieee80211_mgmt mgmt;
|
||||
int encrypt;
|
||||
if (!hapd->drv_priv || !hapd->driver->send_frame)
|
||||
return -1;
|
||||
pos += 6;
|
||||
encrypt = atoi(pos);
|
||||
os_memset(&mgmt, 0, sizeof(mgmt));
|
||||
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_DISASSOC);
|
||||
os_memcpy(mgmt.da, addr, ETH_ALEN);
|
||||
os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt.u.disassoc.reason_code = host_to_le16(reason);
|
||||
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
|
||||
IEEE80211_HDRLEN +
|
||||
sizeof(mgmt.u.deauth),
|
||||
encrypt) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
pos = os_strstr(txtaddr, " p2p=");
|
||||
if (pos) {
|
||||
return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
|
||||
atoi(pos + 5), addr);
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
if (os_strstr(txtaddr, " tx=0"))
|
||||
hostapd_drv_sta_remove(hapd, addr);
|
||||
else
|
||||
hostapd_drv_sta_disassoc(hapd, addr, reason);
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (sta)
|
||||
ap_sta_disassociate(hapd, sta, reason);
|
||||
else if (addr[0] == 0xff)
|
||||
hostapd_free_stas(hapd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_TAXONOMY
|
||||
int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
|
||||
const char *txtaddr,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
struct sta_info *sta;
|
||||
|
||||
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
|
||||
|
||||
if (hwaddr_aton(txtaddr, addr))
|
||||
return -1;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta)
|
||||
return -1;
|
||||
|
||||
return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
|
||||
}
|
||||
#endif /* CONFIG_TAXONOMY */
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
|
||||
const char *txtaddr)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
struct sta_info *sta;
|
||||
|
||||
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
|
||||
|
||||
if (hwaddr_aton(txtaddr, addr))
|
||||
return -1;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta)
|
||||
return -1;
|
||||
|
||||
hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
|
||||
sta->flags & WLAN_STA_WMM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
struct hostapd_iface *iface = hapd->iface;
|
||||
struct hostapd_hw_modes *mode = iface->current_mode;
|
||||
int len = 0, ret, j;
|
||||
size_t i;
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"state=%s\n"
|
||||
"phy=%s\n"
|
||||
"freq=%d\n"
|
||||
"num_sta_non_erp=%d\n"
|
||||
"num_sta_no_short_slot_time=%d\n"
|
||||
"num_sta_no_short_preamble=%d\n"
|
||||
"olbc=%d\n"
|
||||
"num_sta_ht_no_gf=%d\n"
|
||||
"num_sta_no_ht=%d\n"
|
||||
"num_sta_ht_20_mhz=%d\n"
|
||||
"num_sta_ht40_intolerant=%d\n"
|
||||
"olbc_ht=%d\n"
|
||||
"ht_op_mode=0x%x\n",
|
||||
hostapd_state_text(iface->state),
|
||||
iface->phy,
|
||||
iface->freq,
|
||||
iface->num_sta_non_erp,
|
||||
iface->num_sta_no_short_slot_time,
|
||||
iface->num_sta_no_short_preamble,
|
||||
iface->olbc,
|
||||
iface->num_sta_ht_no_gf,
|
||||
iface->num_sta_no_ht,
|
||||
iface->num_sta_ht_20mhz,
|
||||
iface->num_sta_ht40_intolerant,
|
||||
iface->olbc_ht,
|
||||
iface->ht_op_mode);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
if (!iface->cac_started || !iface->dfs_cac_ms) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"cac_time_seconds=%d\n"
|
||||
"cac_time_left_seconds=N/A\n",
|
||||
iface->dfs_cac_ms / 1000);
|
||||
} else {
|
||||
/* CAC started and CAC time set - calculate remaining time */
|
||||
struct os_reltime now;
|
||||
unsigned int left_time;
|
||||
|
||||
os_reltime_age(&iface->dfs_cac_start, &now);
|
||||
left_time = iface->dfs_cac_ms / 1000 - now.sec;
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"cac_time_seconds=%u\n"
|
||||
"cac_time_left_seconds=%u\n",
|
||||
iface->dfs_cac_ms / 1000,
|
||||
left_time);
|
||||
}
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"channel=%u\n"
|
||||
"secondary_channel=%d\n"
|
||||
"ieee80211n=%d\n"
|
||||
"ieee80211ac=%d\n"
|
||||
"ieee80211ax=%d\n"
|
||||
"beacon_int=%u\n"
|
||||
"dtim_period=%d\n",
|
||||
iface->conf->channel,
|
||||
iface->conf->ieee80211n && !hapd->conf->disable_11n ?
|
||||
iface->conf->secondary_channel : 0,
|
||||
iface->conf->ieee80211n && !hapd->conf->disable_11n,
|
||||
iface->conf->ieee80211ac &&
|
||||
!hapd->conf->disable_11ac,
|
||||
iface->conf->ieee80211ax,
|
||||
iface->conf->beacon_int,
|
||||
hapd->conf->dtim_period);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"vht_oper_chwidth=%d\n"
|
||||
"vht_oper_centr_freq_seg0_idx=%d\n"
|
||||
"vht_oper_centr_freq_seg1_idx=%d\n"
|
||||
"vht_caps_info=%08x\n",
|
||||
iface->conf->vht_oper_chwidth,
|
||||
iface->conf->vht_oper_centr_freq_seg0_idx,
|
||||
iface->conf->vht_oper_centr_freq_seg1_idx,
|
||||
iface->conf->vht_capab);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
|
||||
u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
|
||||
u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
|
||||
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"rx_vht_mcs_map=%04x\n"
|
||||
"tx_vht_mcs_map=%04x\n",
|
||||
rxmap, txmap);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"ht_caps_info=%04x\n",
|
||||
hapd->iconf->ht_capab);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
|
||||
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
|
||||
mode->mcs_set);
|
||||
}
|
||||
|
||||
if (iface->current_rates && iface->num_rates) {
|
||||
ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
|
||||
for (j = 0; j < iface->num_rates; j++) {
|
||||
ret = os_snprintf(buf + len, buflen - len, "%s%02x",
|
||||
j > 0 ? " " : "",
|
||||
iface->current_rates[j].rate / 5);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
ret = os_snprintf(buf + len, buflen - len, "\n");
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
for (j = 0; mode && j < mode->num_channels; j++) {
|
||||
if (mode->channels[j].freq == iface->freq) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"max_txpower=%u\n",
|
||||
mode->channels[j].max_tx_power);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
struct hostapd_data *bss = iface->bss[i];
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"bss[%d]=%s\n"
|
||||
"bssid[%d]=" MACSTR "\n"
|
||||
"ssid[%d]=%s\n"
|
||||
"num_sta[%d]=%d\n",
|
||||
(int) i, bss->conf->iface,
|
||||
(int) i, MAC2STR(bss->own_addr),
|
||||
(int) i,
|
||||
wpa_ssid_txt(bss->conf->ssid.ssid,
|
||||
bss->conf->ssid.ssid_len),
|
||||
(int) i, bss->num_sta);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
if (hapd->conf->chan_util_avg_period) {
|
||||
ret = os_snprintf(buf + len, buflen - len,
|
||||
"chan_util_avg=%u\n",
|
||||
iface->chan_util_average);
|
||||
if (os_snprintf_error(buflen - len, ret))
|
||||
return len;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_parse_csa_settings(const char *pos,
|
||||
struct csa_settings *settings)
|
||||
{
|
||||
char *end;
|
||||
|
||||
os_memset(settings, 0, sizeof(*settings));
|
||||
settings->cs_count = strtol(pos, &end, 10);
|
||||
if (pos == end) {
|
||||
wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
|
||||
return -1;
|
||||
}
|
||||
|
||||
settings->freq_params.freq = atoi(end);
|
||||
if (settings->freq_params.freq == 0) {
|
||||
wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define SET_CSA_SETTING(str) \
|
||||
do { \
|
||||
const char *pos2 = os_strstr(pos, " " #str "="); \
|
||||
if (pos2) { \
|
||||
pos2 += sizeof(" " #str "=") - 1; \
|
||||
settings->freq_params.str = atoi(pos2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
SET_CSA_SETTING(center_freq1);
|
||||
SET_CSA_SETTING(center_freq2);
|
||||
SET_CSA_SETTING(bandwidth);
|
||||
SET_CSA_SETTING(sec_channel_offset);
|
||||
settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
|
||||
settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
|
||||
settings->block_tx = !!os_strstr(pos, " blocktx");
|
||||
#undef SET_CSA_SETTING
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
|
||||
{
|
||||
return hostapd_drv_stop_ap(hapd);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
|
||||
size_t len)
|
||||
{
|
||||
return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
|
||||
}
|
||||
|
||||
|
||||
void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
|
||||
{
|
||||
wpa_auth_pmksa_flush(hapd->wpa_auth);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
|
||||
{
|
||||
u8 spa[ETH_ALEN];
|
||||
u8 pmkid[PMKID_LEN];
|
||||
u8 pmk[PMK_LEN_MAX];
|
||||
size_t pmk_len;
|
||||
char *pos, *pos2;
|
||||
int akmp = 0, expiration = 0;
|
||||
|
||||
/*
|
||||
* Entry format:
|
||||
* <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
|
||||
*/
|
||||
|
||||
if (hwaddr_aton(cmd, spa))
|
||||
return -1;
|
||||
|
||||
pos = os_strchr(cmd, ' ');
|
||||
if (!pos)
|
||||
return -1;
|
||||
pos++;
|
||||
|
||||
if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
|
||||
return -1;
|
||||
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (!pos)
|
||||
return -1;
|
||||
pos++;
|
||||
|
||||
pos2 = os_strchr(pos, ' ');
|
||||
if (!pos2)
|
||||
return -1;
|
||||
pmk_len = (pos2 - pos) / 2;
|
||||
if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
|
||||
hexstr2bin(pos, pmk, pmk_len) < 0)
|
||||
return -1;
|
||||
|
||||
pos = pos2 + 1;
|
||||
|
||||
if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
|
||||
return -1;
|
||||
|
||||
return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
|
||||
pmkid, expiration, akmp);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||
#ifdef CONFIG_MESH
|
||||
|
||||
int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
|
||||
const u8 *addr, char *buf, size_t len)
|
||||
{
|
||||
return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
|
||||
}
|
||||
|
||||
|
||||
void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
|
||||
{
|
||||
u8 spa[ETH_ALEN];
|
||||
u8 pmkid[PMKID_LEN];
|
||||
u8 pmk[PMK_LEN_MAX];
|
||||
char *pos;
|
||||
int expiration;
|
||||
|
||||
/*
|
||||
* Entry format:
|
||||
* <BSSID> <PMKID> <PMK> <expiration in seconds>
|
||||
*/
|
||||
|
||||
if (hwaddr_aton(cmd, spa))
|
||||
return NULL;
|
||||
|
||||
pos = os_strchr(cmd, ' ');
|
||||
if (!pos)
|
||||
return NULL;
|
||||
pos++;
|
||||
|
||||
if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
|
||||
return NULL;
|
||||
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (!pos)
|
||||
return NULL;
|
||||
pos++;
|
||||
|
||||
if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
|
||||
return NULL;
|
||||
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (!pos)
|
||||
return NULL;
|
||||
pos++;
|
||||
|
||||
if (sscanf(pos, "%d", &expiration) != 1)
|
||||
return NULL;
|
||||
|
||||
return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MESH */
|
||||
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
|
40
hostapd-2.9/src/ap/ctrl_iface_ap.h
Normal file
40
hostapd-2.9/src/ap/ctrl_iface_ap.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Control interface for shared AP commands
|
||||
* Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef CTRL_IFACE_AP_H
|
||||
#define CTRL_IFACE_AP_H
|
||||
|
||||
int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
|
||||
const char *txtaddr);
|
||||
int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
|
||||
const char *txtaddr);
|
||||
int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
|
||||
const char *txtaddr,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
|
||||
const char *txtaddr);
|
||||
int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
|
||||
size_t buflen);
|
||||
int hostapd_parse_csa_settings(const char *pos,
|
||||
struct csa_settings *settings);
|
||||
int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
|
||||
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
|
||||
size_t len);
|
||||
void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
|
||||
int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
|
||||
int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
|
||||
const u8 *addr, char *buf, size_t len);
|
||||
void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
|
||||
|
||||
#endif /* CTRL_IFACE_AP_H */
|
1140
hostapd-2.9/src/ap/dfs.c
Normal file
1140
hostapd-2.9/src/ap/dfs.c
Normal file
File diff suppressed because it is too large
Load diff
33
hostapd-2.9/src/ap/dfs.h
Normal file
33
hostapd-2.9/src/ap/dfs.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* DFS - Dynamic Frequency Selection
|
||||
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#ifndef DFS_H
|
||||
#define DFS_H
|
||||
|
||||
int hostapd_handle_dfs(struct hostapd_iface *iface);
|
||||
|
||||
int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
|
||||
int ht_enabled, int chan_offset, int chan_width,
|
||||
int cf1, int cf2);
|
||||
int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
|
||||
int ht_enabled, int chan_offset, int chan_width,
|
||||
int cf1, int cf2);
|
||||
int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
|
||||
int ht_enabled,
|
||||
int chan_offset, int chan_width,
|
||||
int cf1, int cf2);
|
||||
int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
|
||||
int ht_enabled,
|
||||
int chan_offset, int chan_width, int cf1, int cf2);
|
||||
int hostapd_is_dfs_required(struct hostapd_iface *iface);
|
||||
int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
|
||||
int ht_enabled, int chan_offset, int chan_width,
|
||||
int cf1, int cf2);
|
||||
int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
|
||||
|
||||
#endif /* DFS_H */
|
158
hostapd-2.9/src/ap/dhcp_snoop.c
Normal file
158
hostapd-2.9/src/ap/dhcp_snoop.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* DHCP snooping for Proxy ARP
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/dhcp.h"
|
||||
#include "l2_packet/l2_packet.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "x_snoop.h"
|
||||
#include "dhcp_snoop.h"
|
||||
|
||||
|
||||
static const char * ipaddr_str(u32 addr)
|
||||
{
|
||||
static char buf[17];
|
||||
|
||||
os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
|
||||
(addr >> 24) & 0xff, (addr >> 16) & 0xff,
|
||||
(addr >> 8) & 0xff, addr & 0xff);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
const struct bootp_pkt *b;
|
||||
struct sta_info *sta;
|
||||
int exten_len;
|
||||
const u8 *end, *pos;
|
||||
int res, msgtype = 0, prefixlen = 32;
|
||||
u32 subnet_mask = 0;
|
||||
u16 tot_len;
|
||||
|
||||
exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
|
||||
if (exten_len < 4)
|
||||
return;
|
||||
|
||||
b = (const struct bootp_pkt *) &buf[ETH_HLEN];
|
||||
tot_len = ntohs(b->iph.tot_len);
|
||||
if (tot_len > (unsigned int) (len - ETH_HLEN))
|
||||
return;
|
||||
|
||||
if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
|
||||
return;
|
||||
|
||||
/* Parse DHCP options */
|
||||
end = (const u8 *) b + tot_len;
|
||||
pos = &b->exten[4];
|
||||
while (pos < end && *pos != DHCP_OPT_END) {
|
||||
const u8 *opt = pos++;
|
||||
|
||||
if (*opt == DHCP_OPT_PAD)
|
||||
continue;
|
||||
|
||||
if (pos >= end || 1 + *pos > end - pos)
|
||||
break;
|
||||
pos += *pos + 1;
|
||||
if (pos >= end)
|
||||
break;
|
||||
|
||||
switch (*opt) {
|
||||
case DHCP_OPT_SUBNET_MASK:
|
||||
if (opt[1] == 4)
|
||||
subnet_mask = WPA_GET_BE32(&opt[2]);
|
||||
if (subnet_mask == 0)
|
||||
return;
|
||||
while (!(subnet_mask & 0x1)) {
|
||||
subnet_mask >>= 1;
|
||||
prefixlen--;
|
||||
}
|
||||
break;
|
||||
case DHCP_OPT_MSG_TYPE:
|
||||
if (opt[1])
|
||||
msgtype = opt[2];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
if (!(sta->flags & WLAN_STA_AUTHORIZED))
|
||||
continue;
|
||||
x_snoop_mcast_to_ucast_convert_send(hapd, sta,
|
||||
(u8 *) buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (msgtype == DHCPACK) {
|
||||
if (b->your_ip == 0)
|
||||
return;
|
||||
|
||||
/* DHCPACK for DHCPREQUEST */
|
||||
sta = ap_get_sta(hapd, b->hw_addr);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
|
||||
" @ IPv4 address %s/%d",
|
||||
MAC2STR(sta->addr),
|
||||
ipaddr_str(be_to_host32(b->your_ip)),
|
||||
prefixlen);
|
||||
|
||||
if (sta->ipaddr == b->your_ip)
|
||||
return;
|
||||
|
||||
if (sta->ipaddr != 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
|
||||
ipaddr_str(be_to_host32(sta->ipaddr)));
|
||||
hostapd_drv_br_delete_ip_neigh(hapd, 4,
|
||||
(u8 *) &sta->ipaddr);
|
||||
}
|
||||
|
||||
res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
|
||||
prefixlen, sta->addr);
|
||||
if (res) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"dhcp_snoop: Adding ip neigh table failed: %d",
|
||||
res);
|
||||
return;
|
||||
}
|
||||
sta->ipaddr = b->your_ip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int dhcp_snoop_init(struct hostapd_data *hapd)
|
||||
{
|
||||
hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
|
||||
L2_PACKET_FILTER_DHCP);
|
||||
if (hapd->sock_dhcp == NULL) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_snoop_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
l2_packet_deinit(hapd->sock_dhcp);
|
||||
hapd->sock_dhcp = NULL;
|
||||
}
|
30
hostapd-2.9/src/ap/dhcp_snoop.h
Normal file
30
hostapd-2.9/src/ap/dhcp_snoop.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* DHCP snooping for Proxy ARP
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef DHCP_SNOOP_H
|
||||
#define DHCP_SNOOP_H
|
||||
|
||||
#ifdef CONFIG_PROXYARP
|
||||
|
||||
int dhcp_snoop_init(struct hostapd_data *hapd);
|
||||
void dhcp_snoop_deinit(struct hostapd_data *hapd);
|
||||
|
||||
#else /* CONFIG_PROXYARP */
|
||||
|
||||
static inline int dhcp_snoop_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROXYARP */
|
||||
|
||||
#endif /* DHCP_SNOOP_H */
|
1725
hostapd-2.9/src/ap/dpp_hostapd.c
Normal file
1725
hostapd-2.9/src/ap/dpp_hostapd.c
Normal file
File diff suppressed because it is too large
Load diff
38
hostapd-2.9/src/ap/dpp_hostapd.h
Normal file
38
hostapd-2.9/src/ap/dpp_hostapd.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* hostapd / DPP integration
|
||||
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef DPP_HOSTAPD_H
|
||||
#define DPP_HOSTAPD_H
|
||||
|
||||
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
|
||||
void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
|
||||
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||
const u8 *buf, size_t len, unsigned int freq);
|
||||
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
|
||||
const u8 *data, size_t data_len, int ok);
|
||||
struct wpabuf *
|
||||
hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
|
||||
const u8 *query, size_t query_len,
|
||||
const u8 *data, size_t data_len);
|
||||
void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
|
||||
int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
|
||||
int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
|
||||
int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
|
||||
void hostapd_dpp_stop(struct hostapd_data *hapd);
|
||||
int hostapd_dpp_init(struct hostapd_data *hapd);
|
||||
void hostapd_dpp_deinit(struct hostapd_data *hapd);
|
||||
void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
|
||||
void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
|
||||
|
||||
#endif /* DPP_HOSTAPD_H */
|
1931
hostapd-2.9/src/ap/drv_callbacks.c
Normal file
1931
hostapd-2.9/src/ap/drv_callbacks.c
Normal file
File diff suppressed because it is too large
Load diff
290
hostapd-2.9/src/ap/eap_user_db.c
Normal file
290
hostapd-2.9/src/ap/eap_user_db.c
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* hostapd / EAP user database
|
||||
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#ifdef CONFIG_SQLITE
|
||||
#include <sqlite3.h>
|
||||
#endif /* CONFIG_SQLITE */
|
||||
|
||||
#include "common.h"
|
||||
#include "eap_common/eap_wsc_common.h"
|
||||
#include "eap_server/eap_methods.h"
|
||||
#include "eap_server/eap.h"
|
||||
#include "ap_config.h"
|
||||
#include "hostapd.h"
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
|
||||
static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
|
||||
{
|
||||
char *buf, *start;
|
||||
int num_methods;
|
||||
|
||||
buf = os_strdup(methods);
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
os_memset(&user->methods, 0, sizeof(user->methods));
|
||||
num_methods = 0;
|
||||
start = buf;
|
||||
while (*start) {
|
||||
char *pos3 = os_strchr(start, ',');
|
||||
if (pos3)
|
||||
*pos3++ = '\0';
|
||||
user->methods[num_methods].method =
|
||||
eap_server_get_type(start,
|
||||
&user->methods[num_methods].vendor);
|
||||
if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
|
||||
user->methods[num_methods].method == EAP_TYPE_NONE) {
|
||||
if (os_strcmp(start, "TTLS-PAP") == 0) {
|
||||
user->ttls_auth |= EAP_TTLS_AUTH_PAP;
|
||||
goto skip_eap;
|
||||
}
|
||||
if (os_strcmp(start, "TTLS-CHAP") == 0) {
|
||||
user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
|
||||
goto skip_eap;
|
||||
}
|
||||
if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
|
||||
user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
|
||||
goto skip_eap;
|
||||
}
|
||||
if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
|
||||
user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
|
||||
goto skip_eap;
|
||||
}
|
||||
wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
|
||||
start);
|
||||
os_free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
num_methods++;
|
||||
if (num_methods >= EAP_MAX_METHODS)
|
||||
break;
|
||||
skip_eap:
|
||||
if (pos3 == NULL)
|
||||
break;
|
||||
start = pos3;
|
||||
}
|
||||
|
||||
os_free(buf);
|
||||
}
|
||||
|
||||
|
||||
static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
|
||||
{
|
||||
struct hostapd_eap_user *user = ctx;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (os_strcmp(col[i], "password") == 0 && argv[i]) {
|
||||
bin_clear_free(user->password, user->password_len);
|
||||
user->password_len = os_strlen(argv[i]);
|
||||
user->password = (u8 *) os_strdup(argv[i]);
|
||||
user->next = (void *) 1;
|
||||
} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
|
||||
set_user_methods(user, argv[i]);
|
||||
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
|
||||
user->remediation = strlen(argv[i]) > 0;
|
||||
} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
|
||||
user->t_c_timestamp = strtol(argv[i], NULL, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
|
||||
{
|
||||
struct hostapd_eap_user *user = ctx;
|
||||
int i, id = -1, methods = -1;
|
||||
size_t len;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (os_strcmp(col[i], "identity") == 0 && argv[i])
|
||||
id = i;
|
||||
else if (os_strcmp(col[i], "methods") == 0 && argv[i])
|
||||
methods = i;
|
||||
}
|
||||
|
||||
if (id < 0 || methods < 0)
|
||||
return 0;
|
||||
|
||||
len = os_strlen(argv[id]);
|
||||
if (len <= user->identity_len &&
|
||||
os_memcmp(argv[id], user->identity, len) == 0 &&
|
||||
(user->password == NULL || len > user->password_len)) {
|
||||
bin_clear_free(user->password, user->password_len);
|
||||
user->password_len = os_strlen(argv[id]);
|
||||
user->password = (u8 *) os_strdup(argv[id]);
|
||||
user->next = (void *) 1;
|
||||
set_user_methods(user, argv[methods]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct hostapd_eap_user *
|
||||
eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
|
||||
size_t identity_len, int phase2)
|
||||
{
|
||||
sqlite3 *db;
|
||||
struct hostapd_eap_user *user = NULL;
|
||||
char id_str[256], cmd[300];
|
||||
size_t i;
|
||||
int res;
|
||||
|
||||
if (identity_len >= sizeof(id_str)) {
|
||||
wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
|
||||
__func__, (int) identity_len,
|
||||
(int) (sizeof(id_str)));
|
||||
return NULL;
|
||||
}
|
||||
os_memcpy(id_str, identity, identity_len);
|
||||
id_str[identity_len] = '\0';
|
||||
for (i = 0; i < identity_len; i++) {
|
||||
if (id_str[i] >= 'a' && id_str[i] <= 'z')
|
||||
continue;
|
||||
if (id_str[i] >= 'A' && id_str[i] <= 'Z')
|
||||
continue;
|
||||
if (id_str[i] >= '0' && id_str[i] <= '9')
|
||||
continue;
|
||||
if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
|
||||
id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
|
||||
id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
|
||||
id_str[i] == '=' || id_str[i] == ' ')
|
||||
continue;
|
||||
wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bin_clear_free(hapd->tmp_eap_user.identity,
|
||||
hapd->tmp_eap_user.identity_len);
|
||||
bin_clear_free(hapd->tmp_eap_user.password,
|
||||
hapd->tmp_eap_user.password_len);
|
||||
os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
|
||||
hapd->tmp_eap_user.phase2 = phase2;
|
||||
hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
|
||||
if (hapd->tmp_eap_user.identity == NULL)
|
||||
return NULL;
|
||||
os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
|
||||
hapd->tmp_eap_user.identity_len = identity_len;
|
||||
|
||||
if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
|
||||
wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
|
||||
hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = os_snprintf(cmd, sizeof(cmd),
|
||||
"SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
|
||||
id_str, phase2);
|
||||
if (os_snprintf_error(sizeof(cmd), res))
|
||||
goto fail;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
|
||||
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
|
||||
SQLITE_OK) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DB: Failed to complete SQL operation: %s db: %s",
|
||||
sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
|
||||
} else if (hapd->tmp_eap_user.next)
|
||||
user = &hapd->tmp_eap_user;
|
||||
|
||||
if (user == NULL && !phase2) {
|
||||
os_snprintf(cmd, sizeof(cmd),
|
||||
"SELECT identity,methods FROM wildcards;");
|
||||
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
|
||||
if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
|
||||
NULL) != SQLITE_OK) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DB: Failed to complete SQL operation: %s db: %s",
|
||||
sqlite3_errmsg(db),
|
||||
hapd->conf->eap_user_sqlite);
|
||||
} else if (hapd->tmp_eap_user.next) {
|
||||
user = &hapd->tmp_eap_user;
|
||||
os_free(user->identity);
|
||||
user->identity = user->password;
|
||||
user->identity_len = user->password_len;
|
||||
user->password = NULL;
|
||||
user->password_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
sqlite3_close(db);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SQLITE */
|
||||
|
||||
|
||||
const struct hostapd_eap_user *
|
||||
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
|
||||
size_t identity_len, int phase2)
|
||||
{
|
||||
const struct hostapd_bss_config *conf = hapd->conf;
|
||||
struct hostapd_eap_user *user = conf->eap_user;
|
||||
|
||||
#ifdef CONFIG_WPS
|
||||
if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
|
||||
os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
|
||||
static struct hostapd_eap_user wsc_enrollee;
|
||||
os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
|
||||
wsc_enrollee.methods[0].method = eap_server_get_type(
|
||||
"WSC", &wsc_enrollee.methods[0].vendor);
|
||||
return &wsc_enrollee;
|
||||
}
|
||||
|
||||
if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
|
||||
os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
|
||||
static struct hostapd_eap_user wsc_registrar;
|
||||
os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
|
||||
wsc_registrar.methods[0].method = eap_server_get_type(
|
||||
"WSC", &wsc_registrar.methods[0].vendor);
|
||||
wsc_registrar.password = (u8 *) conf->ap_pin;
|
||||
wsc_registrar.password_len = conf->ap_pin ?
|
||||
os_strlen(conf->ap_pin) : 0;
|
||||
return &wsc_registrar;
|
||||
}
|
||||
#endif /* CONFIG_WPS */
|
||||
|
||||
while (user) {
|
||||
if (!phase2 && user->identity == NULL) {
|
||||
/* Wildcard match */
|
||||
break;
|
||||
}
|
||||
|
||||
if (user->phase2 == !!phase2 && user->wildcard_prefix &&
|
||||
identity_len >= user->identity_len &&
|
||||
os_memcmp(user->identity, identity, user->identity_len) ==
|
||||
0) {
|
||||
/* Wildcard prefix match */
|
||||
break;
|
||||
}
|
||||
|
||||
if (user->phase2 == !!phase2 &&
|
||||
user->identity_len == identity_len &&
|
||||
os_memcmp(user->identity, identity, identity_len) == 0)
|
||||
break;
|
||||
user = user->next;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
if (user == NULL && conf->eap_user_sqlite) {
|
||||
return eap_user_sqlite_get(hapd, identity, identity_len,
|
||||
phase2);
|
||||
}
|
||||
#endif /* CONFIG_SQLITE */
|
||||
|
||||
return user;
|
||||
}
|
191
hostapd-2.9/src/ap/eth_p_oui.c
Normal file
191
hostapd-2.9/src/ap/eth_p_oui.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* hostapd / IEEE 802 OUI Extended EtherType 88-B7
|
||||
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "l2_packet/l2_packet.h"
|
||||
#include "hostapd.h"
|
||||
#include "eth_p_oui.h"
|
||||
|
||||
/*
|
||||
* See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
|
||||
* EtherType 88-B7. This file implements this with OUI 00:13:74 and
|
||||
* vendor-specific subtype 0x0001.
|
||||
*/
|
||||
static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
|
||||
|
||||
struct eth_p_oui_iface {
|
||||
struct dl_list list;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
struct l2_packet_data *l2;
|
||||
struct dl_list receiver;
|
||||
};
|
||||
|
||||
struct eth_p_oui_ctx {
|
||||
struct dl_list list;
|
||||
struct eth_p_oui_iface *iface;
|
||||
/* all data needed to deliver and unregister */
|
||||
u8 oui_suffix; /* last byte of OUI */
|
||||
void (*rx_callback)(void *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, u8 oui_suffix,
|
||||
const u8 *buf, size_t len);
|
||||
void *rx_callback_ctx;
|
||||
};
|
||||
|
||||
|
||||
void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, const u8 *buf, size_t len)
|
||||
{
|
||||
ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
|
||||
ctx->oui_suffix, buf, len);
|
||||
}
|
||||
|
||||
|
||||
static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
|
||||
{
|
||||
struct eth_p_oui_iface *iface = ctx;
|
||||
struct eth_p_oui_ctx *receiver;
|
||||
const struct l2_ethhdr *ethhdr;
|
||||
|
||||
if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
|
||||
/* too short packet */
|
||||
return;
|
||||
}
|
||||
|
||||
ethhdr = (struct l2_ethhdr *) buf;
|
||||
/* trim eth_hdr from buf and len */
|
||||
buf += sizeof(*ethhdr);
|
||||
len -= sizeof(*ethhdr);
|
||||
|
||||
/* verify OUI and vendor-specific subtype match */
|
||||
if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
|
||||
return;
|
||||
buf += sizeof(global_oui);
|
||||
len -= sizeof(global_oui);
|
||||
|
||||
dl_list_for_each(receiver, &iface->receiver,
|
||||
struct eth_p_oui_ctx, list) {
|
||||
if (buf[0] != receiver->oui_suffix)
|
||||
continue;
|
||||
|
||||
eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
|
||||
buf + 1, len - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct eth_p_oui_ctx *
|
||||
eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
|
||||
void (*rx_callback)(void *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, u8 oui_suffix,
|
||||
const u8 *buf, size_t len),
|
||||
void *rx_callback_ctx)
|
||||
{
|
||||
struct eth_p_oui_iface *iface;
|
||||
struct eth_p_oui_ctx *receiver;
|
||||
int found = 0;
|
||||
struct hapd_interfaces *interfaces;
|
||||
|
||||
receiver = os_zalloc(sizeof(*receiver));
|
||||
if (!receiver)
|
||||
goto err;
|
||||
|
||||
receiver->oui_suffix = oui_suffix;
|
||||
receiver->rx_callback = rx_callback;
|
||||
receiver->rx_callback_ctx = rx_callback_ctx;
|
||||
|
||||
interfaces = hapd->iface->interfaces;
|
||||
|
||||
dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
|
||||
list) {
|
||||
if (os_strcmp(iface->ifname, ifname) != 0)
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
iface = os_zalloc(sizeof(*iface));
|
||||
if (!iface)
|
||||
goto err;
|
||||
|
||||
os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
|
||||
iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
|
||||
iface, 1);
|
||||
if (!iface->l2) {
|
||||
os_free(iface);
|
||||
goto err;
|
||||
}
|
||||
dl_list_init(&iface->receiver);
|
||||
|
||||
dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
|
||||
}
|
||||
|
||||
dl_list_add_tail(&iface->receiver, &receiver->list);
|
||||
receiver->iface = iface;
|
||||
|
||||
return receiver;
|
||||
err:
|
||||
os_free(receiver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
|
||||
{
|
||||
struct eth_p_oui_iface *iface;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
iface = ctx->iface;
|
||||
|
||||
dl_list_del(&ctx->list);
|
||||
os_free(ctx);
|
||||
|
||||
if (dl_list_empty(&iface->receiver)) {
|
||||
dl_list_del(&iface->list);
|
||||
l2_packet_deinit(iface->l2);
|
||||
os_free(iface);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, const u8 *buf, size_t len)
|
||||
{
|
||||
struct eth_p_oui_iface *iface = ctx->iface;
|
||||
u8 *packet, *p;
|
||||
size_t packet_len;
|
||||
int ret;
|
||||
struct l2_ethhdr *ethhdr;
|
||||
|
||||
packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
|
||||
packet = os_zalloc(packet_len);
|
||||
if (!packet)
|
||||
return -1;
|
||||
p = packet;
|
||||
|
||||
ethhdr = (struct l2_ethhdr *) packet;
|
||||
os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
|
||||
os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
|
||||
ethhdr->h_proto = host_to_be16(ETH_P_OUI);
|
||||
p += sizeof(*ethhdr);
|
||||
|
||||
os_memcpy(p, global_oui, sizeof(global_oui));
|
||||
p[sizeof(global_oui)] = ctx->oui_suffix;
|
||||
p += sizeof(global_oui) + 1;
|
||||
|
||||
os_memcpy(p, buf, len);
|
||||
|
||||
ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
|
||||
os_free(packet);
|
||||
return ret;
|
||||
}
|
28
hostapd-2.9/src/ap/eth_p_oui.h
Normal file
28
hostapd-2.9/src/ap/eth_p_oui.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* hostapd / IEEE 802 OUI Extended Ethertype
|
||||
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef ETH_P_OUI_H
|
||||
#define ETH_P_OUI_H
|
||||
|
||||
struct eth_p_oui_ctx;
|
||||
struct hostapd_data;
|
||||
|
||||
/* rx_callback only gets payload after OUI passed as buf */
|
||||
struct eth_p_oui_ctx *
|
||||
eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
|
||||
void (*rx_callback)(void *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, u8 oui_suffix,
|
||||
const u8 *buf, size_t len),
|
||||
void *rx_callback_ctx);
|
||||
void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
|
||||
int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, const u8 *buf, size_t len);
|
||||
void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
|
||||
const u8 *dst_addr, const u8 *buf, size_t len);
|
||||
|
||||
#endif /* ETH_P_OUI_H */
|
654
hostapd-2.9/src/ap/fils_hlp.c
Normal file
654
hostapd-2.9/src/ap/fils_hlp.c
Normal file
|
@ -0,0 +1,654 @@
|
|||
/*
|
||||
* FILS HLP request processing
|
||||
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/dhcp.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "fils_hlp.h"
|
||||
|
||||
|
||||
static be16 ip_checksum(const void *buf, size_t len)
|
||||
{
|
||||
u32 sum = 0;
|
||||
const u16 *pos;
|
||||
|
||||
for (pos = buf; len >= 2; len -= 2)
|
||||
sum += ntohs(*pos++);
|
||||
if (len)
|
||||
sum += ntohs(*pos << 8);
|
||||
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += sum >> 16;
|
||||
return htons(~sum);
|
||||
}
|
||||
|
||||
|
||||
static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
|
||||
{
|
||||
u8 *pos, *end;
|
||||
struct dhcp_data *dhcp;
|
||||
struct sockaddr_in addr;
|
||||
ssize_t res;
|
||||
const u8 *server_id = NULL;
|
||||
|
||||
if (!sta->hlp_dhcp_discover) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: No pending HLP DHCPDISCOVER available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Convert to DHCPREQUEST, remove rapid commit option, replace requested
|
||||
* IP address option with yiaddr. */
|
||||
pos = wpabuf_mhead(sta->hlp_dhcp_discover);
|
||||
end = pos + wpabuf_len(sta->hlp_dhcp_discover);
|
||||
dhcp = (struct dhcp_data *) pos;
|
||||
pos = (u8 *) (dhcp + 1);
|
||||
pos += 4; /* skip magic */
|
||||
while (pos < end && *pos != DHCP_OPT_END) {
|
||||
u8 opt, olen;
|
||||
|
||||
opt = *pos++;
|
||||
if (opt == DHCP_OPT_PAD)
|
||||
continue;
|
||||
if (pos >= end)
|
||||
break;
|
||||
olen = *pos++;
|
||||
if (olen > end - pos)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case DHCP_OPT_MSG_TYPE:
|
||||
if (olen > 0)
|
||||
*pos = DHCPREQUEST;
|
||||
break;
|
||||
case DHCP_OPT_RAPID_COMMIT:
|
||||
case DHCP_OPT_REQUESTED_IP_ADDRESS:
|
||||
case DHCP_OPT_SERVER_ID:
|
||||
/* Remove option */
|
||||
pos -= 2;
|
||||
os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
|
||||
end -= 2 + olen;
|
||||
olen = 0;
|
||||
break;
|
||||
}
|
||||
pos += olen;
|
||||
}
|
||||
if (pos >= end || *pos != DHCP_OPT_END) {
|
||||
wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
|
||||
return -1;
|
||||
}
|
||||
sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
|
||||
|
||||
/* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
|
||||
pos = (u8 *) (dhcpoffer + 1);
|
||||
end = dhcpofferend;
|
||||
pos += 4; /* skip magic */
|
||||
while (pos < end && *pos != DHCP_OPT_END) {
|
||||
u8 opt, olen;
|
||||
|
||||
opt = *pos++;
|
||||
if (opt == DHCP_OPT_PAD)
|
||||
continue;
|
||||
if (pos >= end)
|
||||
break;
|
||||
olen = *pos++;
|
||||
if (olen > end - pos)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case DHCP_OPT_SERVER_ID:
|
||||
server_id = pos - 2;
|
||||
break;
|
||||
}
|
||||
pos += olen;
|
||||
}
|
||||
|
||||
if (wpabuf_resize(&sta->hlp_dhcp_discover,
|
||||
6 + 1 + (server_id ? 2 + server_id[1] : 0)))
|
||||
return -1;
|
||||
if (server_id)
|
||||
wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
|
||||
2 + server_id[1]);
|
||||
wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
|
||||
wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
|
||||
wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
|
||||
wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
|
||||
|
||||
os_memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||
addr.sin_port = htons(hapd->conf->dhcp_server_port);
|
||||
res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
|
||||
wpabuf_len(sta->hlp_dhcp_discover), 0,
|
||||
(const struct sockaddr *) &addr, sizeof(addr));
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: Acting as DHCP rapid commit proxy for %s:%d",
|
||||
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||
wpabuf_free(sta->hlp_dhcp_discover);
|
||||
sta->hlp_dhcp_discover = NULL;
|
||||
sta->fils_dhcp_rapid_commit_proxy = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = sock_ctx;
|
||||
struct sta_info *sta;
|
||||
u8 buf[1500], *pos, *end, *end_opt = NULL;
|
||||
struct dhcp_data *dhcp;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
ssize_t res;
|
||||
u8 msgtype = 0;
|
||||
int rapid_commit = 0;
|
||||
struct iphdr *iph;
|
||||
struct udphdr *udph;
|
||||
struct wpabuf *resp;
|
||||
const u8 *rpos;
|
||||
size_t left, len;
|
||||
|
||||
addr_len = sizeof(addr);
|
||||
res = recvfrom(sd, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *) &addr, &addr_len);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
|
||||
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
|
||||
wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
|
||||
if ((size_t) res < sizeof(*dhcp))
|
||||
return;
|
||||
dhcp = (struct dhcp_data *) buf;
|
||||
if (dhcp->op != 2)
|
||||
return; /* Not a BOOTREPLY */
|
||||
if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP - DHCP response to unknown relay address 0x%x",
|
||||
dhcp->relay_ip);
|
||||
return;
|
||||
}
|
||||
dhcp->relay_ip = 0;
|
||||
pos = (u8 *) (dhcp + 1);
|
||||
end = &buf[res];
|
||||
|
||||
if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
|
||||
wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
|
||||
return;
|
||||
}
|
||||
pos += 4;
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
|
||||
pos, end - pos);
|
||||
while (pos < end && *pos != DHCP_OPT_END) {
|
||||
u8 opt, olen;
|
||||
|
||||
opt = *pos++;
|
||||
if (opt == DHCP_OPT_PAD)
|
||||
continue;
|
||||
if (pos >= end)
|
||||
break;
|
||||
olen = *pos++;
|
||||
if (olen > end - pos)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case DHCP_OPT_MSG_TYPE:
|
||||
if (olen > 0)
|
||||
msgtype = pos[0];
|
||||
break;
|
||||
case DHCP_OPT_RAPID_COMMIT:
|
||||
rapid_commit = 1;
|
||||
break;
|
||||
}
|
||||
pos += olen;
|
||||
}
|
||||
if (pos < end && *pos == DHCP_OPT_END)
|
||||
end_opt = pos;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
|
||||
MACSTR ")",
|
||||
msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
|
||||
|
||||
sta = ap_get_sta(hapd, dhcp->hw_addr);
|
||||
if (!sta || !sta->fils_pending_assoc_req) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: No pending HLP DHCP exchange with hw_addr "
|
||||
MACSTR, MAC2STR(dhcp->hw_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
|
||||
!rapid_commit) {
|
||||
/* Use hostapd to take care of 4-message exchange and convert
|
||||
* the final DHCPACK to rapid commit version. */
|
||||
if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
|
||||
return;
|
||||
/* failed, so send the server response as-is */
|
||||
} else if (msgtype != DHCPACK) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
|
||||
}
|
||||
|
||||
pos = buf;
|
||||
resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
|
||||
sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
|
||||
if (!resp)
|
||||
return;
|
||||
wpabuf_put_data(resp, sta->addr, ETH_ALEN);
|
||||
wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
|
||||
wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
|
||||
wpabuf_put_be16(resp, ETH_P_IP);
|
||||
iph = wpabuf_put(resp, sizeof(*iph));
|
||||
iph->version = 4;
|
||||
iph->ihl = sizeof(*iph) / 4;
|
||||
iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
|
||||
iph->ttl = 1;
|
||||
iph->protocol = 17; /* UDP */
|
||||
iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||
iph->daddr = dhcp->client_ip;
|
||||
iph->check = ip_checksum(iph, sizeof(*iph));
|
||||
udph = wpabuf_put(resp, sizeof(*udph));
|
||||
udph->uh_sport = htons(DHCP_SERVER_PORT);
|
||||
udph->uh_dport = htons(DHCP_CLIENT_PORT);
|
||||
udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
|
||||
udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
|
||||
if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
|
||||
!rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
|
||||
/* Add rapid commit option */
|
||||
wpabuf_put_data(resp, pos, end_opt - pos);
|
||||
wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
|
||||
wpabuf_put_u8(resp, 0);
|
||||
wpabuf_put_data(resp, end_opt, end - end_opt);
|
||||
} else {
|
||||
wpabuf_put_data(resp, pos, end - pos);
|
||||
}
|
||||
if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
|
||||
2 * wpabuf_len(resp) / 255 + 100)) {
|
||||
wpabuf_free(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
rpos = wpabuf_head(resp);
|
||||
left = wpabuf_len(resp);
|
||||
|
||||
wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
|
||||
if (left <= 254)
|
||||
len = 1 + left;
|
||||
else
|
||||
len = 255;
|
||||
wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
|
||||
/* Element ID Extension */
|
||||
wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
|
||||
/* Destination MAC Address, Source MAC Address, HLP Packet.
|
||||
* HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
|
||||
* when LPD is used). */
|
||||
wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
|
||||
rpos += len - 1;
|
||||
left -= len - 1;
|
||||
while (left) {
|
||||
wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
|
||||
len = left > 255 ? 255 : left;
|
||||
wpabuf_put_u8(sta->fils_hlp_resp, len);
|
||||
wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
|
||||
rpos += len;
|
||||
left -= len;
|
||||
}
|
||||
wpabuf_free(resp);
|
||||
|
||||
if (sta->fils_drv_assoc_finish)
|
||||
hostapd_notify_assoc_fils_finish(hapd, sta);
|
||||
else
|
||||
fils_hlp_finish_assoc(hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *msg, size_t len)
|
||||
{
|
||||
const struct dhcp_data *dhcp;
|
||||
struct wpabuf *dhcp_buf;
|
||||
struct dhcp_data *dhcp_msg;
|
||||
u8 msgtype = 0;
|
||||
int rapid_commit = 0;
|
||||
const u8 *pos = msg, *end;
|
||||
struct sockaddr_in addr;
|
||||
ssize_t res;
|
||||
|
||||
if (len < sizeof(*dhcp))
|
||||
return 0;
|
||||
dhcp = (const struct dhcp_data *) pos;
|
||||
end = pos + len;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
|
||||
dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
|
||||
ntohl(dhcp->xid));
|
||||
pos += sizeof(*dhcp);
|
||||
if (dhcp->op != 1)
|
||||
return 0; /* Not a BOOTREQUEST */
|
||||
|
||||
if (end - pos < 4)
|
||||
return 0;
|
||||
if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
|
||||
wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
|
||||
return 0;
|
||||
}
|
||||
pos += 4;
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
|
||||
while (pos < end && *pos != DHCP_OPT_END) {
|
||||
u8 opt, olen;
|
||||
|
||||
opt = *pos++;
|
||||
if (opt == DHCP_OPT_PAD)
|
||||
continue;
|
||||
if (pos >= end)
|
||||
break;
|
||||
olen = *pos++;
|
||||
if (olen > end - pos)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case DHCP_OPT_MSG_TYPE:
|
||||
if (olen > 0)
|
||||
msgtype = pos[0];
|
||||
break;
|
||||
case DHCP_OPT_RAPID_COMMIT:
|
||||
rapid_commit = 1;
|
||||
break;
|
||||
}
|
||||
pos += olen;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
|
||||
if (msgtype != DHCPDISCOVER)
|
||||
return 0;
|
||||
|
||||
if (hapd->conf->dhcp_server.af != AF_INET ||
|
||||
hapd->conf->dhcp_server.u.v4.s_addr == 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP - no DHCPv4 server configured - drop request");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hapd->conf->own_ip_addr.af != AF_INET ||
|
||||
hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP - no IPv4 own_ip_addr configured - drop request");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hapd->dhcp_sock < 0) {
|
||||
int s;
|
||||
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"FILS: Failed to open DHCP socket: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hapd->conf->dhcp_relay_port) {
|
||||
os_memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr =
|
||||
hapd->conf->own_ip_addr.u.v4.s_addr;
|
||||
addr.sin_port = htons(hapd->conf->dhcp_relay_port);
|
||||
if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"FILS: Failed to bind DHCP socket: %s",
|
||||
strerror(errno));
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (eloop_register_sock(s, EVENT_TYPE_READ,
|
||||
fils_dhcp_handler, NULL, hapd)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hapd->dhcp_sock = s;
|
||||
}
|
||||
|
||||
dhcp_buf = wpabuf_alloc(len);
|
||||
if (!dhcp_buf)
|
||||
return 0;
|
||||
dhcp_msg = wpabuf_put(dhcp_buf, len);
|
||||
os_memcpy(dhcp_msg, msg, len);
|
||||
dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
|
||||
os_memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
|
||||
addr.sin_port = htons(hapd->conf->dhcp_server_port);
|
||||
res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
|
||||
(const struct sockaddr *) &addr, sizeof(addr));
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
|
||||
strerror(errno));
|
||||
wpabuf_free(dhcp_buf);
|
||||
/* Close the socket to try to recover from error */
|
||||
eloop_unregister_read_sock(hapd->dhcp_sock);
|
||||
close(hapd->dhcp_sock);
|
||||
hapd->dhcp_sock = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
|
||||
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
|
||||
rapid_commit);
|
||||
if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
|
||||
/* Store a copy of the DHCPDISCOVER for rapid commit proxying
|
||||
* purposes if the server does not support the rapid commit
|
||||
* option. */
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: Store DHCPDISCOVER for rapid commit proxy");
|
||||
wpabuf_free(sta->hlp_dhcp_discover);
|
||||
sta->hlp_dhcp_discover = dhcp_buf;
|
||||
} else {
|
||||
wpabuf_free(dhcp_buf);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int fils_process_hlp_udp(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, const u8 *dst,
|
||||
const u8 *pos, size_t len)
|
||||
{
|
||||
const struct iphdr *iph;
|
||||
const struct udphdr *udph;
|
||||
u16 sport, dport, ulen;
|
||||
|
||||
if (len < sizeof(*iph) + sizeof(*udph))
|
||||
return 0;
|
||||
iph = (const struct iphdr *) pos;
|
||||
udph = (const struct udphdr *) (iph + 1);
|
||||
sport = ntohs(udph->uh_sport);
|
||||
dport = ntohs(udph->uh_dport);
|
||||
ulen = ntohs(udph->uh_ulen);
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
|
||||
sport, dport, ulen, ntohs(udph->uh_sum));
|
||||
/* TODO: Check UDP checksum */
|
||||
if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
|
||||
return 0;
|
||||
|
||||
if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
|
||||
return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
|
||||
ulen - sizeof(*udph));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int fils_process_hlp_ip(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, const u8 *dst,
|
||||
const u8 *pos, size_t len)
|
||||
{
|
||||
const struct iphdr *iph;
|
||||
u16 tot_len;
|
||||
|
||||
if (len < sizeof(*iph))
|
||||
return 0;
|
||||
iph = (const struct iphdr *) pos;
|
||||
if (ip_checksum(iph, sizeof(*iph)) != 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP request IPv4 packet had invalid header checksum - dropped");
|
||||
return 0;
|
||||
}
|
||||
tot_len = ntohs(iph->tot_len);
|
||||
if (tot_len > len)
|
||||
return 0;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
|
||||
iph->saddr, iph->daddr, iph->protocol);
|
||||
switch (iph->protocol) {
|
||||
case 17:
|
||||
return fils_process_hlp_udp(hapd, sta, dst, pos, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int fils_process_hlp_req(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *pos, size_t len)
|
||||
{
|
||||
const u8 *pkt, *end;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
|
||||
" src=" MACSTR " len=%u)",
|
||||
MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
|
||||
(unsigned int) len);
|
||||
if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: Ignore HLP request with unexpected source address"
|
||||
MACSTR, MAC2STR(pos + ETH_ALEN));
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = pos + len;
|
||||
pkt = pos + 2 * ETH_ALEN;
|
||||
if (end - pkt >= 6 &&
|
||||
os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
|
||||
pkt += 6; /* Remove SNAP/LLC header */
|
||||
wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
|
||||
|
||||
if (end - pkt < 2)
|
||||
return 0;
|
||||
|
||||
switch (WPA_GET_BE16(pkt)) {
|
||||
case ETH_P_IP:
|
||||
return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
|
||||
end - pkt - 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *pos, int left)
|
||||
{
|
||||
const u8 *end = pos + left;
|
||||
u8 *tmp, *tmp_pos;
|
||||
int ret = 0;
|
||||
|
||||
if (sta->fils_pending_assoc_req &&
|
||||
eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
|
||||
/* Do not process FILS HLP request again if the station
|
||||
* retransmits (Re)Association Request frame before the previous
|
||||
* HLP response has either been received or timed out. */
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS: Do not relay another HLP request from "
|
||||
MACSTR
|
||||
" before processing of the already pending one has been completed",
|
||||
MAC2STR(sta->addr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Old DHCPDISCOVER is not needed anymore, if it was still pending */
|
||||
wpabuf_free(sta->hlp_dhcp_discover);
|
||||
sta->hlp_dhcp_discover = NULL;
|
||||
sta->fils_dhcp_rapid_commit_proxy = 0;
|
||||
|
||||
/* Check if there are any FILS HLP Container elements */
|
||||
while (end - pos >= 2) {
|
||||
if (2 + pos[1] > end - pos)
|
||||
return 0;
|
||||
if (pos[0] == WLAN_EID_EXTENSION &&
|
||||
pos[1] >= 1 + 2 * ETH_ALEN &&
|
||||
pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
|
||||
break;
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
if (end - pos < 2)
|
||||
return 0; /* No FILS HLP Container elements */
|
||||
|
||||
tmp = os_malloc(end - pos);
|
||||
if (!tmp)
|
||||
return 0;
|
||||
|
||||
while (end - pos >= 2) {
|
||||
if (2 + pos[1] > end - pos ||
|
||||
pos[0] != WLAN_EID_EXTENSION ||
|
||||
pos[1] < 1 + 2 * ETH_ALEN ||
|
||||
pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
|
||||
break;
|
||||
tmp_pos = tmp;
|
||||
os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
|
||||
tmp_pos += pos[1] - 1;
|
||||
pos += 2 + pos[1];
|
||||
|
||||
/* Add possible fragments */
|
||||
while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
|
||||
2 + pos[1] <= end - pos) {
|
||||
os_memcpy(tmp_pos, pos + 2, pos[1]);
|
||||
tmp_pos += pos[1];
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
|
||||
if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
os_free(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void fils_hlp_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->dhcp_sock >= 0) {
|
||||
eloop_unregister_read_sock(hapd->dhcp_sock);
|
||||
close(hapd->dhcp_sock);
|
||||
hapd->dhcp_sock = -1;
|
||||
}
|
||||
}
|
27
hostapd-2.9/src/ap/fils_hlp.h
Normal file
27
hostapd-2.9/src/ap/fils_hlp.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* FILS HLP request processing
|
||||
* Copyright (c) 2017, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FILS_HLP_H
|
||||
#define FILS_HLP_H
|
||||
|
||||
int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *pos, int left);
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
|
||||
void fils_hlp_deinit(struct hostapd_data *hapd);
|
||||
|
||||
#else /* CONFIG_FILS */
|
||||
|
||||
static inline void fils_hlp_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
#endif /* FILS_HLP_H */
|
714
hostapd-2.9/src/ap/gas_query_ap.c
Normal file
714
hostapd-2.9/src/ap/gas_query_ap.c
Normal file
|
@ -0,0 +1,714 @@
|
|||
/*
|
||||
* Generic advertisement service (GAS) query (hostapd)
|
||||
* Copyright (c) 2009, Atheros Communications
|
||||
* Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
|
||||
* Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "utils/list.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/gas.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "gas_query_ap.h"
|
||||
|
||||
|
||||
/** GAS query timeout in seconds */
|
||||
#define GAS_QUERY_TIMEOUT_PERIOD 2
|
||||
|
||||
/* GAS query wait-time / duration in ms */
|
||||
#define GAS_QUERY_WAIT_TIME_INITIAL 1000
|
||||
#define GAS_QUERY_WAIT_TIME_COMEBACK 150
|
||||
|
||||
/**
|
||||
* struct gas_query_pending - Pending GAS query
|
||||
*/
|
||||
struct gas_query_pending {
|
||||
struct dl_list list;
|
||||
struct gas_query_ap *gas;
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 dialog_token;
|
||||
u8 next_frag_id;
|
||||
unsigned int wait_comeback:1;
|
||||
unsigned int offchannel_tx_started:1;
|
||||
unsigned int retry:1;
|
||||
int freq;
|
||||
u16 status_code;
|
||||
struct wpabuf *req;
|
||||
struct wpabuf *adv_proto;
|
||||
struct wpabuf *resp;
|
||||
struct os_reltime last_oper;
|
||||
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
|
||||
enum gas_query_ap_result result,
|
||||
const struct wpabuf *adv_proto,
|
||||
const struct wpabuf *resp, u16 status_code);
|
||||
void *ctx;
|
||||
u8 sa[ETH_ALEN];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gas_query_ap - Internal GAS query data
|
||||
*/
|
||||
struct gas_query_ap {
|
||||
struct hostapd_data *hapd;
|
||||
void *msg_ctx;
|
||||
struct dl_list pending; /* struct gas_query_pending */
|
||||
struct gas_query_pending *current;
|
||||
};
|
||||
|
||||
|
||||
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
|
||||
static void gas_query_timeout(void *eloop_data, void *user_ctx);
|
||||
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
|
||||
static void gas_query_tx_initial_req(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query);
|
||||
static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
|
||||
|
||||
|
||||
static int ms_from_time(struct os_reltime *last)
|
||||
{
|
||||
struct os_reltime now, res;
|
||||
|
||||
os_get_reltime(&now);
|
||||
os_reltime_sub(&now, last, &res);
|
||||
return res.sec * 1000 + res.usec / 1000;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gas_query_ap_init - Initialize GAS query component
|
||||
* @hapd: Pointer to hostapd data
|
||||
* Returns: Pointer to GAS query data or %NULL on failure
|
||||
*/
|
||||
struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
|
||||
void *msg_ctx)
|
||||
{
|
||||
struct gas_query_ap *gas;
|
||||
|
||||
gas = os_zalloc(sizeof(*gas));
|
||||
if (!gas)
|
||||
return NULL;
|
||||
|
||||
gas->hapd = hapd;
|
||||
gas->msg_ctx = msg_ctx;
|
||||
dl_list_init(&gas->pending);
|
||||
|
||||
return gas;
|
||||
}
|
||||
|
||||
|
||||
static const char * gas_result_txt(enum gas_query_ap_result result)
|
||||
{
|
||||
switch (result) {
|
||||
case GAS_QUERY_AP_SUCCESS:
|
||||
return "SUCCESS";
|
||||
case GAS_QUERY_AP_FAILURE:
|
||||
return "FAILURE";
|
||||
case GAS_QUERY_AP_TIMEOUT:
|
||||
return "TIMEOUT";
|
||||
case GAS_QUERY_AP_PEER_ERROR:
|
||||
return "PEER_ERROR";
|
||||
case GAS_QUERY_AP_INTERNAL_ERROR:
|
||||
return "INTERNAL_ERROR";
|
||||
case GAS_QUERY_AP_DELETED_AT_DEINIT:
|
||||
return "DELETED_AT_DEINIT";
|
||||
}
|
||||
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_free(struct gas_query_pending *query, int del_list)
|
||||
{
|
||||
if (del_list)
|
||||
dl_list_del(&query->list);
|
||||
|
||||
wpabuf_free(query->req);
|
||||
wpabuf_free(query->adv_proto);
|
||||
wpabuf_free(query->resp);
|
||||
os_free(query);
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_done(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query,
|
||||
enum gas_query_ap_result result)
|
||||
{
|
||||
wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
|
||||
" dialog_token=%u freq=%d status_code=%u result=%s",
|
||||
MAC2STR(query->addr), query->dialog_token, query->freq,
|
||||
query->status_code, gas_result_txt(result));
|
||||
if (gas->current == query)
|
||||
gas->current = NULL;
|
||||
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
|
||||
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
|
||||
dl_list_del(&query->list);
|
||||
query->cb(query->ctx, query->addr, query->dialog_token, result,
|
||||
query->adv_proto, query->resp, query->status_code);
|
||||
gas_query_free(query, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gas_query_ap_deinit - Deinitialize GAS query component
|
||||
* @gas: GAS query data from gas_query_init()
|
||||
*/
|
||||
void gas_query_ap_deinit(struct gas_query_ap *gas)
|
||||
{
|
||||
struct gas_query_pending *query, *next;
|
||||
|
||||
if (gas == NULL)
|
||||
return;
|
||||
|
||||
dl_list_for_each_safe(query, next, &gas->pending,
|
||||
struct gas_query_pending, list)
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
|
||||
|
||||
os_free(gas);
|
||||
}
|
||||
|
||||
|
||||
static struct gas_query_pending *
|
||||
gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
|
||||
{
|
||||
struct gas_query_pending *q;
|
||||
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
|
||||
if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
|
||||
q->dialog_token == dialog_token)
|
||||
return q;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int gas_query_append(struct gas_query_pending *query, const u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
if (wpabuf_resize(&query->resp, len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
|
||||
return -1;
|
||||
}
|
||||
wpabuf_put_data(query->resp, data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
|
||||
const u8 *data, size_t data_len, int ok)
|
||||
{
|
||||
struct gas_query_pending *query;
|
||||
int dur;
|
||||
|
||||
if (!gas || !gas->current) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
|
||||
" ok=%d - no query in progress", MAC2STR(dst), ok);
|
||||
return;
|
||||
}
|
||||
|
||||
query = gas->current;
|
||||
|
||||
dur = ms_from_time(&query->last_oper);
|
||||
wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
|
||||
" ok=%d query=%p dialog_token=%u dur=%d ms",
|
||||
MAC2STR(dst), ok, query, query->dialog_token, dur);
|
||||
if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
|
||||
return;
|
||||
}
|
||||
os_get_reltime(&query->last_oper);
|
||||
|
||||
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||
if (!ok) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
|
||||
eloop_register_timeout(0, 250000, gas_query_timeout,
|
||||
gas, query);
|
||||
} else {
|
||||
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
|
||||
gas_query_timeout, gas, query);
|
||||
}
|
||||
if (query->wait_comeback && !query->retry) {
|
||||
eloop_cancel_timeout(gas_query_rx_comeback_timeout,
|
||||
gas, query);
|
||||
eloop_register_timeout(
|
||||
0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
|
||||
gas_query_rx_comeback_timeout, gas, query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
return sta && (sta->flags & WLAN_STA_MFP);
|
||||
}
|
||||
|
||||
|
||||
static int gas_query_tx(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query,
|
||||
struct wpabuf *req, unsigned int wait_time)
|
||||
{
|
||||
int res, prot = pmf_in_use(gas->hapd, query->addr);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
|
||||
"freq=%d prot=%d using src addr " MACSTR,
|
||||
MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
|
||||
query->freq, prot, MAC2STR(query->sa));
|
||||
if (prot) {
|
||||
u8 *categ = wpabuf_mhead_u8(req);
|
||||
*categ = WLAN_ACTION_PROTECTED_DUAL;
|
||||
}
|
||||
os_get_reltime(&query->last_oper);
|
||||
res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
|
||||
query->addr, wpabuf_head(req),
|
||||
wpabuf_len(req));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query)
|
||||
{
|
||||
struct wpabuf *req;
|
||||
unsigned int wait_time;
|
||||
|
||||
req = gas_build_comeback_req(query->dialog_token);
|
||||
if (req == NULL) {
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
wait_time = (query->retry || !query->offchannel_tx_started) ?
|
||||
GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
|
||||
|
||||
if (gas_query_tx(gas, query, req, wait_time) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
|
||||
MACSTR, MAC2STR(query->addr));
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
wpabuf_free(req);
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
|
||||
{
|
||||
struct gas_query_ap *gas = eloop_data;
|
||||
struct gas_query_pending *query = user_ctx;
|
||||
int dialog_token;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"GAS: No response to comeback request received (retry=%u)",
|
||||
query->retry);
|
||||
if (gas->current != query || query->retry)
|
||||
return;
|
||||
dialog_token = gas_query_new_dialog_token(gas, query->addr);
|
||||
if (dialog_token < 0)
|
||||
return;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"GAS: Retry GAS query due to comeback response timeout");
|
||||
query->retry = 1;
|
||||
query->dialog_token = dialog_token;
|
||||
*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
|
||||
query->wait_comeback = 0;
|
||||
query->next_frag_id = 0;
|
||||
wpabuf_free(query->adv_proto);
|
||||
query->adv_proto = NULL;
|
||||
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
|
||||
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||
gas_query_tx_initial_req(gas, query);
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
|
||||
{
|
||||
struct gas_query_ap *gas = eloop_data;
|
||||
struct gas_query_pending *query = user_ctx;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
|
||||
MAC2STR(query->addr));
|
||||
gas_query_tx_comeback_req(gas, query);
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query,
|
||||
u16 comeback_delay)
|
||||
{
|
||||
unsigned int secs, usecs;
|
||||
|
||||
secs = (comeback_delay * 1024) / 1000000;
|
||||
usecs = comeback_delay * 1024 - secs * 1000000;
|
||||
wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
|
||||
" in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
|
||||
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
|
||||
eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
|
||||
gas, query);
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_rx_initial(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query,
|
||||
const u8 *adv_proto, const u8 *resp,
|
||||
size_t len, u16 comeback_delay)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
|
||||
MACSTR " (dialog_token=%u comeback_delay=%u)",
|
||||
MAC2STR(query->addr), query->dialog_token, comeback_delay);
|
||||
|
||||
query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
|
||||
if (query->adv_proto == NULL) {
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comeback_delay) {
|
||||
eloop_cancel_timeout(gas_query_timeout, gas, query);
|
||||
query->wait_comeback = 1;
|
||||
gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Query was completed without comeback mechanism */
|
||||
if (gas_query_append(query, resp, len) < 0) {
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_rx_comeback(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query,
|
||||
const u8 *adv_proto, const u8 *resp,
|
||||
size_t len, u8 frag_id, u8 more_frags,
|
||||
u16 comeback_delay)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
|
||||
MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
|
||||
"comeback_delay=%u)",
|
||||
MAC2STR(query->addr), query->dialog_token, frag_id,
|
||||
more_frags, comeback_delay);
|
||||
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
|
||||
|
||||
if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
|
||||
os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
|
||||
wpabuf_len(query->adv_proto)) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
|
||||
"between initial and comeback response from "
|
||||
MACSTR, MAC2STR(query->addr));
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comeback_delay) {
|
||||
if (frag_id) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
|
||||
"with non-zero frag_id and comeback_delay "
|
||||
"from " MACSTR, MAC2STR(query->addr));
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
|
||||
return;
|
||||
}
|
||||
gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frag_id != query->next_frag_id) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
|
||||
"from " MACSTR, MAC2STR(query->addr));
|
||||
if (frag_id + 1 == query->next_frag_id) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
|
||||
"retry of previous fragment");
|
||||
return;
|
||||
}
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
|
||||
return;
|
||||
}
|
||||
query->next_frag_id++;
|
||||
|
||||
if (gas_query_append(query, resp, len) < 0) {
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (more_frags) {
|
||||
gas_query_tx_comeback_req(gas, query);
|
||||
return;
|
||||
}
|
||||
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
|
||||
* frame
|
||||
* @gas: GAS query data from gas_query_init()
|
||||
* @sa: Source MAC address of the Action frame
|
||||
* @categ: Category of the Action frame
|
||||
* @data: Payload of the Action frame
|
||||
* @len: Length of @data
|
||||
* @freq: Frequency (in MHz) on which the frame was received
|
||||
* Returns: 0 if the Public Action frame was a GAS frame or -1 if not
|
||||
*/
|
||||
int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
|
||||
const u8 *data, size_t len, int freq)
|
||||
{
|
||||
struct gas_query_pending *query;
|
||||
u8 action, dialog_token, frag_id = 0, more_frags = 0;
|
||||
u16 comeback_delay, resp_len;
|
||||
const u8 *pos, *adv_proto;
|
||||
int prot, pmf;
|
||||
unsigned int left;
|
||||
|
||||
if (!gas || len < 4)
|
||||
return -1;
|
||||
|
||||
pos = data;
|
||||
action = *pos++;
|
||||
dialog_token = *pos++;
|
||||
|
||||
if (action != WLAN_PA_GAS_INITIAL_RESP &&
|
||||
action != WLAN_PA_GAS_COMEBACK_RESP)
|
||||
return -1; /* Not a GAS response */
|
||||
|
||||
prot = categ == WLAN_ACTION_PROTECTED_DUAL;
|
||||
pmf = pmf_in_use(gas->hapd, sa);
|
||||
if (prot && !pmf) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
|
||||
return 0;
|
||||
}
|
||||
if (!prot && pmf) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
query = gas_query_get_pending(gas, sa, dialog_token);
|
||||
if (query == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
|
||||
" dialog token %u", MAC2STR(sa), dialog_token);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
|
||||
ms_from_time(&query->last_oper), MAC2STR(sa));
|
||||
|
||||
if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
|
||||
MACSTR " dialog token %u when waiting for comeback "
|
||||
"response", MAC2STR(sa), dialog_token);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
|
||||
MACSTR " dialog token %u when waiting for initial "
|
||||
"response", MAC2STR(sa), dialog_token);
|
||||
return 0;
|
||||
}
|
||||
|
||||
query->status_code = WPA_GET_LE16(pos);
|
||||
pos += 2;
|
||||
|
||||
if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
|
||||
action == WLAN_PA_GAS_COMEBACK_RESP) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
|
||||
} else if (query->status_code != WLAN_STATUS_SUCCESS) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
|
||||
"%u failed - status code %u",
|
||||
MAC2STR(sa), dialog_token, query->status_code);
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (action == WLAN_PA_GAS_COMEBACK_RESP) {
|
||||
if (pos + 1 > data + len)
|
||||
return 0;
|
||||
frag_id = *pos & 0x7f;
|
||||
more_frags = (*pos & 0x80) >> 7;
|
||||
pos++;
|
||||
}
|
||||
|
||||
/* Comeback Delay */
|
||||
if (pos + 2 > data + len)
|
||||
return 0;
|
||||
comeback_delay = WPA_GET_LE16(pos);
|
||||
pos += 2;
|
||||
|
||||
/* Advertisement Protocol element */
|
||||
if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
|
||||
"Protocol element in the response from " MACSTR,
|
||||
MAC2STR(sa));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*pos != WLAN_EID_ADV_PROTO) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
|
||||
"Protocol element ID %u in response from " MACSTR,
|
||||
*pos, MAC2STR(sa));
|
||||
return 0;
|
||||
}
|
||||
|
||||
adv_proto = pos;
|
||||
pos += 2 + pos[1];
|
||||
|
||||
/* Query Response Length */
|
||||
if (pos + 2 > data + len) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
|
||||
return 0;
|
||||
}
|
||||
resp_len = WPA_GET_LE16(pos);
|
||||
pos += 2;
|
||||
|
||||
left = data + len - pos;
|
||||
if (resp_len > left) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
|
||||
"response from " MACSTR, MAC2STR(sa));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (resp_len < left) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
|
||||
"after Query Response from " MACSTR,
|
||||
left - resp_len, MAC2STR(sa));
|
||||
}
|
||||
|
||||
if (action == WLAN_PA_GAS_COMEBACK_RESP)
|
||||
gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
|
||||
frag_id, more_frags, comeback_delay);
|
||||
else
|
||||
gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
|
||||
comeback_delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_timeout(void *eloop_data, void *user_ctx)
|
||||
{
|
||||
struct gas_query_ap *gas = eloop_data;
|
||||
struct gas_query_pending *query = user_ctx;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
|
||||
" dialog token %u",
|
||||
MAC2STR(query->addr), query->dialog_token);
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
static int gas_query_dialog_token_available(struct gas_query_ap *gas,
|
||||
const u8 *dst, u8 dialog_token)
|
||||
{
|
||||
struct gas_query_pending *q;
|
||||
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
|
||||
if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
|
||||
dialog_token == q->dialog_token)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void gas_query_tx_initial_req(struct gas_query_ap *gas,
|
||||
struct gas_query_pending *query)
|
||||
{
|
||||
if (gas_query_tx(gas, query, query->req,
|
||||
GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
|
||||
MACSTR, MAC2STR(query->addr));
|
||||
gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
gas->current = query;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
|
||||
query->dialog_token);
|
||||
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
|
||||
gas_query_timeout, gas, query);
|
||||
}
|
||||
|
||||
|
||||
static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
|
||||
{
|
||||
static int next_start = 0;
|
||||
int dialog_token;
|
||||
|
||||
for (dialog_token = 0; dialog_token < 256; dialog_token++) {
|
||||
if (gas_query_dialog_token_available(
|
||||
gas, dst, (next_start + dialog_token) % 256))
|
||||
break;
|
||||
}
|
||||
if (dialog_token == 256)
|
||||
return -1; /* Too many pending queries */
|
||||
dialog_token = (next_start + dialog_token) % 256;
|
||||
next_start = (dialog_token + 1) % 256;
|
||||
return dialog_token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gas_query_ap_req - Request a GAS query
|
||||
* @gas: GAS query data from gas_query_init()
|
||||
* @dst: Destination MAC address for the query
|
||||
* @freq: Frequency (in MHz) for the channel on which to send the query
|
||||
* @req: GAS query payload (to be freed by gas_query module in case of success
|
||||
* return)
|
||||
* @cb: Callback function for reporting GAS query result and response
|
||||
* @ctx: Context pointer to use with the @cb call
|
||||
* Returns: dialog token (>= 0) on success or -1 on failure
|
||||
*/
|
||||
int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
|
||||
struct wpabuf *req,
|
||||
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
|
||||
enum gas_query_ap_result result,
|
||||
const struct wpabuf *adv_proto,
|
||||
const struct wpabuf *resp, u16 status_code),
|
||||
void *ctx)
|
||||
{
|
||||
struct gas_query_pending *query;
|
||||
int dialog_token;
|
||||
|
||||
if (!gas || wpabuf_len(req) < 3)
|
||||
return -1;
|
||||
|
||||
dialog_token = gas_query_new_dialog_token(gas, dst);
|
||||
if (dialog_token < 0)
|
||||
return -1;
|
||||
|
||||
query = os_zalloc(sizeof(*query));
|
||||
if (query == NULL)
|
||||
return -1;
|
||||
|
||||
query->gas = gas;
|
||||
os_memcpy(query->addr, dst, ETH_ALEN);
|
||||
query->dialog_token = dialog_token;
|
||||
query->freq = freq;
|
||||
query->cb = cb;
|
||||
query->ctx = ctx;
|
||||
query->req = req;
|
||||
dl_list_add(&gas->pending, &query->list);
|
||||
|
||||
*(wpabuf_mhead_u8(req) + 2) = dialog_token;
|
||||
|
||||
wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
|
||||
" dialog_token=%u freq=%d",
|
||||
MAC2STR(query->addr), query->dialog_token, query->freq);
|
||||
|
||||
gas_query_tx_initial_req(gas, query);
|
||||
|
||||
return dialog_token;
|
||||
}
|
43
hostapd-2.9/src/ap/gas_query_ap.h
Normal file
43
hostapd-2.9/src/ap/gas_query_ap.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Generic advertisement service (GAS) query
|
||||
* Copyright (c) 2009, Atheros Communications
|
||||
* Copyright (c) 2011-2017, Qualcomm Atheros
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef GAS_QUERY_AP_H
|
||||
#define GAS_QUERY_AP_H
|
||||
|
||||
struct gas_query_ap;
|
||||
|
||||
struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
|
||||
void *msg_ctx);
|
||||
void gas_query_ap_deinit(struct gas_query_ap *gas);
|
||||
int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
|
||||
const u8 *data, size_t len, int freq);
|
||||
|
||||
/**
|
||||
* enum gas_query_ap_result - GAS query result
|
||||
*/
|
||||
enum gas_query_ap_result {
|
||||
GAS_QUERY_AP_SUCCESS,
|
||||
GAS_QUERY_AP_FAILURE,
|
||||
GAS_QUERY_AP_TIMEOUT,
|
||||
GAS_QUERY_AP_PEER_ERROR,
|
||||
GAS_QUERY_AP_INTERNAL_ERROR,
|
||||
GAS_QUERY_AP_DELETED_AT_DEINIT
|
||||
};
|
||||
|
||||
int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
|
||||
struct wpabuf *req,
|
||||
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
|
||||
enum gas_query_ap_result result,
|
||||
const struct wpabuf *adv_proto,
|
||||
const struct wpabuf *resp, u16 status_code),
|
||||
void *ctx);
|
||||
void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
|
||||
const u8 *data, size_t data_len, int ok);
|
||||
|
||||
#endif /* GAS_QUERY_AP_H */
|
1890
hostapd-2.9/src/ap/gas_serv.c
Normal file
1890
hostapd-2.9/src/ap/gas_serv.c
Normal file
File diff suppressed because it is too large
Load diff
95
hostapd-2.9/src/ap/gas_serv.h
Normal file
95
hostapd-2.9/src/ap/gas_serv.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Generic advertisement service (GAS) server
|
||||
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef GAS_SERV_H
|
||||
#define GAS_SERV_H
|
||||
|
||||
/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
|
||||
#define ANQP_REQ_CAPABILITY_LIST \
|
||||
(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_VENUE_NAME \
|
||||
(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
|
||||
(1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_NETWORK_AUTH_TYPE \
|
||||
(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_ROAMING_CONSORTIUM \
|
||||
(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
|
||||
(1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_NAI_REALM \
|
||||
(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
|
||||
(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
|
||||
(1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_AP_CIVIC_LOCATION \
|
||||
(1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
|
||||
(1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_DOMAIN_NAME \
|
||||
(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_EMERGENCY_ALERT_URI \
|
||||
(1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_TDLS_CAPABILITY \
|
||||
(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
|
||||
#define ANQP_REQ_EMERGENCY_NAI \
|
||||
(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
|
||||
/*
|
||||
* First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
|
||||
* optimized bitmap.
|
||||
*/
|
||||
#define ANQP_REQ_HS_CAPABILITY_LIST \
|
||||
(0x10000 << HS20_STYPE_CAPABILITY_LIST)
|
||||
#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
|
||||
(0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
|
||||
#define ANQP_REQ_WAN_METRICS \
|
||||
(0x10000 << HS20_STYPE_WAN_METRICS)
|
||||
#define ANQP_REQ_CONNECTION_CAPABILITY \
|
||||
(0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
|
||||
#define ANQP_REQ_NAI_HOME_REALM \
|
||||
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
|
||||
#define ANQP_REQ_OPERATING_CLASS \
|
||||
(0x10000 << HS20_STYPE_OPERATING_CLASS)
|
||||
#define ANQP_REQ_OSU_PROVIDERS_LIST \
|
||||
(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
|
||||
#define ANQP_REQ_ICON_REQUEST \
|
||||
(0x10000 << HS20_STYPE_ICON_REQUEST)
|
||||
#define ANQP_REQ_OPERATOR_ICON_METADATA \
|
||||
(0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
|
||||
#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
|
||||
(0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
|
||||
/* The first MBO ANQP-element can be included in the optimized bitmap. */
|
||||
#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
|
||||
(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
|
||||
|
||||
struct gas_dialog_info {
|
||||
u8 valid;
|
||||
struct wpabuf *sd_resp; /* Fragmented response */
|
||||
u8 dialog_token;
|
||||
size_t sd_resp_pos; /* Offset in sd_resp */
|
||||
u8 sd_frag_id;
|
||||
int prot; /* whether Protected Dual of Public Action frame is used */
|
||||
int dpp; /* whether this is a DPP Config Response */
|
||||
};
|
||||
|
||||
struct hostapd_data;
|
||||
|
||||
struct gas_dialog_info *
|
||||
gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 dialog_token);
|
||||
void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
|
||||
|
||||
int gas_serv_init(struct hostapd_data *hapd);
|
||||
void gas_serv_deinit(struct hostapd_data *hapd);
|
||||
|
||||
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
|
||||
const u8 *sa, u8 dialog_token,
|
||||
int prot, struct wpabuf *buf);
|
||||
|
||||
#endif /* GAS_SERV_H */
|
3573
hostapd-2.9/src/ap/hostapd.c
Normal file
3573
hostapd-2.9/src/ap/hostapd.c
Normal file
File diff suppressed because it is too large
Load diff
658
hostapd-2.9/src/ap/hostapd.h
Normal file
658
hostapd-2.9/src/ap/hostapd.h
Normal file
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* hostapd / Initialization and configuration
|
||||
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef HOSTAPD_H
|
||||
#define HOSTAPD_H
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
#include <sqlite3.h>
|
||||
#endif /* CONFIG_SQLITE */
|
||||
|
||||
#include "common/defs.h"
|
||||
#include "utils/list.h"
|
||||
#include "ap_config.h"
|
||||
#include "drivers/driver.h"
|
||||
|
||||
#define OCE_STA_CFON_ENABLED(hapd) \
|
||||
((hapd->conf->oce & OCE_STA_CFON) && \
|
||||
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
|
||||
#define OCE_AP_ENABLED(hapd) \
|
||||
((hapd->conf->oce & OCE_AP) && \
|
||||
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
|
||||
|
||||
struct wpa_ctrl_dst;
|
||||
struct radius_server_data;
|
||||
struct upnp_wps_device_sm;
|
||||
struct hostapd_data;
|
||||
struct sta_info;
|
||||
struct ieee80211_ht_capabilities;
|
||||
struct full_dynamic_vlan;
|
||||
enum wps_event;
|
||||
union wps_event_data;
|
||||
#ifdef CONFIG_MESH
|
||||
struct mesh_conf;
|
||||
#endif /* CONFIG_MESH */
|
||||
|
||||
struct hostapd_iface;
|
||||
|
||||
struct hapd_interfaces {
|
||||
int (*reload_config)(struct hostapd_iface *iface);
|
||||
struct hostapd_config * (*config_read_cb)(const char *config_fname);
|
||||
int (*ctrl_iface_init)(struct hostapd_data *hapd);
|
||||
void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
|
||||
int (*for_each_interface)(struct hapd_interfaces *interfaces,
|
||||
int (*cb)(struct hostapd_iface *iface,
|
||||
void *ctx), void *ctx);
|
||||
int (*driver_init)(struct hostapd_iface *iface);
|
||||
|
||||
size_t count;
|
||||
int global_ctrl_sock;
|
||||
struct dl_list global_ctrl_dst;
|
||||
char *global_iface_path;
|
||||
char *global_iface_name;
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
gid_t ctrl_iface_group;
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
struct hostapd_iface **iface;
|
||||
|
||||
size_t terminate_on_error;
|
||||
#ifndef CONFIG_NO_VLAN
|
||||
struct dynamic_iface *vlan_priv;
|
||||
#endif /* CONFIG_NO_VLAN */
|
||||
#ifdef CONFIG_ETH_P_OUI
|
||||
struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
|
||||
#endif /* CONFIG_ETH_P_OUI */
|
||||
int eloop_initialized;
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
struct dpp_global *dpp;
|
||||
#endif /* CONFIG_DPP */
|
||||
};
|
||||
|
||||
enum hostapd_chan_status {
|
||||
HOSTAPD_CHAN_VALID = 0, /* channel is ready */
|
||||
HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
|
||||
HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
|
||||
};
|
||||
|
||||
struct hostapd_probereq_cb {
|
||||
int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
|
||||
const u8 *ie, size_t ie_len, int ssi_signal);
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
#define HOSTAPD_RATE_BASIC 0x00000001
|
||||
|
||||
struct hostapd_rate_data {
|
||||
int rate; /* rate in 100 kbps */
|
||||
int flags; /* HOSTAPD_RATE_ flags */
|
||||
};
|
||||
|
||||
struct hostapd_frame_info {
|
||||
unsigned int freq;
|
||||
u32 channel;
|
||||
u32 datarate;
|
||||
int ssi_signal; /* dBm */
|
||||
};
|
||||
|
||||
enum wps_status {
|
||||
WPS_STATUS_SUCCESS = 1,
|
||||
WPS_STATUS_FAILURE
|
||||
};
|
||||
|
||||
enum pbc_status {
|
||||
WPS_PBC_STATUS_DISABLE,
|
||||
WPS_PBC_STATUS_ACTIVE,
|
||||
WPS_PBC_STATUS_TIMEOUT,
|
||||
WPS_PBC_STATUS_OVERLAP
|
||||
};
|
||||
|
||||
struct wps_stat {
|
||||
enum wps_status status;
|
||||
enum wps_error_indication failure_reason;
|
||||
enum pbc_status pbc_status;
|
||||
u8 peer_addr[ETH_ALEN];
|
||||
};
|
||||
|
||||
struct hostapd_neighbor_entry {
|
||||
struct dl_list list;
|
||||
u8 bssid[ETH_ALEN];
|
||||
struct wpa_ssid_value ssid;
|
||||
struct wpabuf *nr;
|
||||
struct wpabuf *lci;
|
||||
struct wpabuf *civic;
|
||||
/* LCI update time */
|
||||
struct os_time lci_date;
|
||||
int stationary;
|
||||
};
|
||||
|
||||
struct hostapd_sae_commit_queue {
|
||||
struct dl_list list;
|
||||
int rssi;
|
||||
size_t len;
|
||||
u8 msg[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hostapd_data - hostapd per-BSS data structure
|
||||
*/
|
||||
struct hostapd_data {
|
||||
struct hostapd_iface *iface;
|
||||
struct hostapd_config *iconf;
|
||||
struct hostapd_bss_config *conf;
|
||||
int interface_added; /* virtual interface added for this BSS */
|
||||
unsigned int started:1;
|
||||
unsigned int disabled:1;
|
||||
unsigned int reenable_beacon:1;
|
||||
|
||||
u8 own_addr[ETH_ALEN];
|
||||
|
||||
int num_sta; /* number of entries in sta_list */
|
||||
struct sta_info *sta_list; /* STA info list head */
|
||||
#define STA_HASH_SIZE 256
|
||||
#define STA_HASH(sta) (sta[5])
|
||||
struct sta_info *sta_hash[STA_HASH_SIZE];
|
||||
|
||||
/*
|
||||
* Bitfield for indicating which AIDs are allocated. Only AID values
|
||||
* 1-2007 are used and as such, the bit at index 0 corresponds to AID
|
||||
* 1.
|
||||
*/
|
||||
#define AID_WORDS ((2008 + 31) / 32)
|
||||
u32 sta_aid[AID_WORDS];
|
||||
|
||||
const struct wpa_driver_ops *driver;
|
||||
void *drv_priv;
|
||||
|
||||
void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int reassoc);
|
||||
|
||||
void *msg_ctx; /* ctx for wpa_msg() calls */
|
||||
void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
|
||||
|
||||
struct radius_client_data *radius;
|
||||
u64 acct_session_id;
|
||||
struct radius_das_data *radius_das;
|
||||
|
||||
struct iapp_data *iapp;
|
||||
|
||||
struct hostapd_cached_radius_acl *acl_cache;
|
||||
struct hostapd_acl_query_data *acl_queries;
|
||||
|
||||
struct wpa_authenticator *wpa_auth;
|
||||
struct eapol_authenticator *eapol_auth;
|
||||
|
||||
struct rsn_preauth_interface *preauth_iface;
|
||||
struct os_reltime michael_mic_failure;
|
||||
int michael_mic_failures;
|
||||
int tkip_countermeasures;
|
||||
|
||||
int ctrl_sock;
|
||||
struct dl_list ctrl_dst;
|
||||
|
||||
void *ssl_ctx;
|
||||
void *eap_sim_db_priv;
|
||||
struct radius_server_data *radius_srv;
|
||||
struct dl_list erp_keys; /* struct eap_server_erp_key */
|
||||
|
||||
int parameter_set_count;
|
||||
|
||||
/* Time Advertisement */
|
||||
u8 time_update_counter;
|
||||
struct wpabuf *time_adv;
|
||||
|
||||
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
||||
struct full_dynamic_vlan *full_dynamic_vlan;
|
||||
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
||||
|
||||
struct l2_packet_data *l2;
|
||||
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
struct dl_list l2_queue;
|
||||
struct dl_list l2_oui_queue;
|
||||
struct eth_p_oui_ctx *oui_pull;
|
||||
struct eth_p_oui_ctx *oui_resp;
|
||||
struct eth_p_oui_ctx *oui_push;
|
||||
struct eth_p_oui_ctx *oui_sreq;
|
||||
struct eth_p_oui_ctx *oui_sresp;
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
|
||||
struct wps_context *wps;
|
||||
|
||||
int beacon_set_done;
|
||||
struct wpabuf *wps_beacon_ie;
|
||||
struct wpabuf *wps_probe_resp_ie;
|
||||
#ifdef CONFIG_WPS
|
||||
unsigned int ap_pin_failures;
|
||||
unsigned int ap_pin_failures_consecutive;
|
||||
struct upnp_wps_device_sm *wps_upnp;
|
||||
unsigned int ap_pin_lockout_time;
|
||||
|
||||
struct wps_stat wps_stats;
|
||||
#endif /* CONFIG_WPS */
|
||||
|
||||
#ifdef CONFIG_MACSEC
|
||||
struct ieee802_1x_kay *kay;
|
||||
#endif /* CONFIG_MACSEC */
|
||||
|
||||
struct hostapd_probereq_cb *probereq_cb;
|
||||
size_t num_probereq_cb;
|
||||
|
||||
void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
|
||||
int freq);
|
||||
void *public_action_cb_ctx;
|
||||
void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
|
||||
int freq);
|
||||
void *public_action_cb2_ctx;
|
||||
|
||||
int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
|
||||
int freq);
|
||||
void *vendor_action_cb_ctx;
|
||||
|
||||
void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
|
||||
const u8 *uuid_e);
|
||||
void *wps_reg_success_cb_ctx;
|
||||
|
||||
void (*wps_event_cb)(void *ctx, enum wps_event event,
|
||||
union wps_event_data *data);
|
||||
void *wps_event_cb_ctx;
|
||||
|
||||
void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
|
||||
int authorized, const u8 *p2p_dev_addr);
|
||||
void *sta_authorized_cb_ctx;
|
||||
|
||||
void (*setup_complete_cb)(void *ctx);
|
||||
void *setup_complete_cb_ctx;
|
||||
|
||||
void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
|
||||
const u8 *p2p_dev_addr, const u8 *psk,
|
||||
size_t psk_len);
|
||||
void *new_psk_cb_ctx;
|
||||
|
||||
/* channel switch parameters */
|
||||
struct hostapd_freq_params cs_freq_params;
|
||||
u8 cs_count;
|
||||
int cs_block_tx;
|
||||
unsigned int cs_c_off_beacon;
|
||||
unsigned int cs_c_off_proberesp;
|
||||
int csa_in_progress;
|
||||
unsigned int cs_c_off_ecsa_beacon;
|
||||
unsigned int cs_c_off_ecsa_proberesp;
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
struct p2p_data *p2p;
|
||||
struct p2p_group *p2p_group;
|
||||
struct wpabuf *p2p_beacon_ie;
|
||||
struct wpabuf *p2p_probe_resp_ie;
|
||||
|
||||
/* Number of non-P2P association stations */
|
||||
int num_sta_no_p2p;
|
||||
|
||||
/* Periodic NoA (used only when no non-P2P clients in the group) */
|
||||
int noa_enabled;
|
||||
int noa_start;
|
||||
int noa_duration;
|
||||
#endif /* CONFIG_P2P */
|
||||
#ifdef CONFIG_PROXYARP
|
||||
struct l2_packet_data *sock_dhcp;
|
||||
struct l2_packet_data *sock_ndisc;
|
||||
#endif /* CONFIG_PROXYARP */
|
||||
#ifdef CONFIG_MESH
|
||||
int num_plinks;
|
||||
int max_plinks;
|
||||
void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
struct wpabuf *mesh_pending_auth;
|
||||
struct os_reltime mesh_pending_auth_time;
|
||||
u8 mesh_required_peer[ETH_ALEN];
|
||||
#endif /* CONFIG_MESH */
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
struct hostapd_eap_user tmp_eap_user;
|
||||
#endif /* CONFIG_SQLITE */
|
||||
|
||||
#ifdef CONFIG_SAE
|
||||
/** Key used for generating SAE anti-clogging tokens */
|
||||
u8 sae_token_key[8];
|
||||
struct os_reltime last_sae_token_key_update;
|
||||
u16 sae_token_idx;
|
||||
u16 sae_pending_token_idx[256];
|
||||
int dot11RSNASAERetransPeriod; /* msec */
|
||||
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
|
||||
#endif /* CONFIG_SAE */
|
||||
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
unsigned int ext_mgmt_frame_handling:1;
|
||||
unsigned int ext_eapol_frame_io:1;
|
||||
|
||||
struct l2_packet_data *l2_test;
|
||||
|
||||
enum wpa_alg last_gtk_alg;
|
||||
int last_gtk_key_idx;
|
||||
u8 last_gtk[WPA_GTK_MAX_LEN];
|
||||
size_t last_gtk_len;
|
||||
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
enum wpa_alg last_igtk_alg;
|
||||
int last_igtk_key_idx;
|
||||
u8 last_igtk[WPA_IGTK_MAX_LEN];
|
||||
size_t last_igtk_len;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
unsigned int mbo_assoc_disallow;
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
struct dl_list nr_db;
|
||||
|
||||
u8 beacon_req_token;
|
||||
u8 lci_req_token;
|
||||
u8 range_req_token;
|
||||
unsigned int lci_req_active:1;
|
||||
unsigned int range_req_active:1;
|
||||
|
||||
int dhcp_sock; /* UDP socket used with the DHCP server */
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
int dpp_init_done;
|
||||
struct dpp_authentication *dpp_auth;
|
||||
u8 dpp_allowed_roles;
|
||||
int dpp_qr_mutual;
|
||||
int dpp_auth_ok_on_ack;
|
||||
int dpp_in_response_listen;
|
||||
struct gas_query_ap *gas;
|
||||
struct dpp_pkex *dpp_pkex;
|
||||
struct dpp_bootstrap_info *dpp_pkex_bi;
|
||||
char *dpp_pkex_code;
|
||||
char *dpp_pkex_identifier;
|
||||
char *dpp_pkex_auth_cmd;
|
||||
char *dpp_configurator_params;
|
||||
struct os_reltime dpp_last_init;
|
||||
struct os_reltime dpp_init_iter_start;
|
||||
unsigned int dpp_init_max_tries;
|
||||
unsigned int dpp_init_retry_time;
|
||||
unsigned int dpp_resp_wait_time;
|
||||
unsigned int dpp_resp_max_tries;
|
||||
unsigned int dpp_resp_retry_time;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
char *dpp_config_obj_override;
|
||||
char *dpp_discovery_override;
|
||||
char *dpp_groups_override;
|
||||
unsigned int dpp_ignore_netaccesskey_mismatch:1;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
#ifdef CONFIG_AIRTIME_POLICY
|
||||
unsigned int num_backlogged_sta;
|
||||
unsigned int airtime_weight;
|
||||
#endif /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
u8 last_1x_eapol_key_replay_counter[8];
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
sqlite3 *rad_attr_db;
|
||||
#endif /* CONFIG_SQLITE */
|
||||
};
|
||||
|
||||
|
||||
struct hostapd_sta_info {
|
||||
struct dl_list list;
|
||||
u8 addr[ETH_ALEN];
|
||||
struct os_reltime last_seen;
|
||||
int ssi_signal;
|
||||
#ifdef CONFIG_TAXONOMY
|
||||
struct wpabuf *probe_ie_taxonomy;
|
||||
#endif /* CONFIG_TAXONOMY */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hostapd_iface - hostapd per-interface data structure
|
||||
*/
|
||||
struct hostapd_iface {
|
||||
struct hapd_interfaces *interfaces;
|
||||
void *owner;
|
||||
char *config_fname;
|
||||
struct hostapd_config *conf;
|
||||
char phy[16]; /* Name of the PHY (radio) */
|
||||
|
||||
enum hostapd_iface_state {
|
||||
HAPD_IFACE_UNINITIALIZED,
|
||||
HAPD_IFACE_DISABLED,
|
||||
HAPD_IFACE_COUNTRY_UPDATE,
|
||||
HAPD_IFACE_ACS,
|
||||
HAPD_IFACE_HT_SCAN,
|
||||
HAPD_IFACE_DFS,
|
||||
HAPD_IFACE_ENABLED
|
||||
} state;
|
||||
|
||||
#ifdef CONFIG_MESH
|
||||
struct mesh_conf *mconf;
|
||||
#endif /* CONFIG_MESH */
|
||||
|
||||
size_t num_bss;
|
||||
struct hostapd_data **bss;
|
||||
|
||||
unsigned int wait_channel_update:1;
|
||||
unsigned int cac_started:1;
|
||||
#ifdef CONFIG_FST
|
||||
struct fst_iface *fst;
|
||||
const struct wpabuf *fst_ies;
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
/*
|
||||
* When set, indicates that the driver will handle the AP
|
||||
* teardown: delete global keys, station keys, and stations.
|
||||
*/
|
||||
unsigned int driver_ap_teardown:1;
|
||||
|
||||
/*
|
||||
* When set, indicates that this interface is part of list of
|
||||
* interfaces that need to be started together (synchronously).
|
||||
*/
|
||||
unsigned int need_to_start_in_sync:1;
|
||||
|
||||
/* Ready to start but waiting for other interfaces to become ready. */
|
||||
unsigned int ready_to_start_in_sync:1;
|
||||
|
||||
int num_ap; /* number of entries in ap_list */
|
||||
struct ap_info *ap_list; /* AP info list head */
|
||||
struct ap_info *ap_hash[STA_HASH_SIZE];
|
||||
|
||||
u64 drv_flags;
|
||||
|
||||
/* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
|
||||
unsigned int smps_modes;
|
||||
|
||||
/*
|
||||
* A bitmap of supported protocols for probe response offload. See
|
||||
* struct wpa_driver_capa in driver.h
|
||||
*/
|
||||
unsigned int probe_resp_offloads;
|
||||
|
||||
/* extended capabilities supported by the driver */
|
||||
const u8 *extended_capa, *extended_capa_mask;
|
||||
unsigned int extended_capa_len;
|
||||
|
||||
unsigned int drv_max_acl_mac_addrs;
|
||||
|
||||
struct hostapd_hw_modes *hw_features;
|
||||
int num_hw_features;
|
||||
struct hostapd_hw_modes *current_mode;
|
||||
/* Rates that are currently used (i.e., filtered copy of
|
||||
* current_mode->channels */
|
||||
int num_rates;
|
||||
struct hostapd_rate_data *current_rates;
|
||||
int *basic_rates;
|
||||
int freq;
|
||||
|
||||
u16 hw_flags;
|
||||
|
||||
/* Number of associated Non-ERP stations (i.e., stations using 802.11b
|
||||
* in 802.11g BSS) */
|
||||
int num_sta_non_erp;
|
||||
|
||||
/* Number of associated stations that do not support Short Slot Time */
|
||||
int num_sta_no_short_slot_time;
|
||||
|
||||
/* Number of associated stations that do not support Short Preamble */
|
||||
int num_sta_no_short_preamble;
|
||||
|
||||
int olbc; /* Overlapping Legacy BSS Condition */
|
||||
|
||||
/* Number of HT associated stations that do not support greenfield */
|
||||
int num_sta_ht_no_gf;
|
||||
|
||||
/* Number of associated non-HT stations */
|
||||
int num_sta_no_ht;
|
||||
|
||||
/* Number of HT associated stations 20 MHz */
|
||||
int num_sta_ht_20mhz;
|
||||
|
||||
/* Number of HT40 intolerant stations */
|
||||
int num_sta_ht40_intolerant;
|
||||
|
||||
/* Overlapping BSS information */
|
||||
int olbc_ht;
|
||||
|
||||
u16 ht_op_mode;
|
||||
|
||||
/* surveying helpers */
|
||||
|
||||
/* number of channels surveyed */
|
||||
unsigned int chans_surveyed;
|
||||
|
||||
/* lowest observed noise floor in dBm */
|
||||
s8 lowest_nf;
|
||||
|
||||
/* channel utilization calculation */
|
||||
u64 last_channel_time;
|
||||
u64 last_channel_time_busy;
|
||||
u8 channel_utilization;
|
||||
|
||||
unsigned int chan_util_samples_sum;
|
||||
unsigned int chan_util_num_sample_periods;
|
||||
unsigned int chan_util_average;
|
||||
|
||||
/* eCSA IE will be added only if operating class is specified */
|
||||
u8 cs_oper_class;
|
||||
|
||||
unsigned int dfs_cac_ms;
|
||||
struct os_reltime dfs_cac_start;
|
||||
|
||||
/* Latched with the actual secondary channel information and will be
|
||||
* used while juggling between HT20 and HT40 modes. */
|
||||
int secondary_ch;
|
||||
|
||||
#ifdef CONFIG_ACS
|
||||
unsigned int acs_num_completed_scans;
|
||||
#endif /* CONFIG_ACS */
|
||||
|
||||
void (*scan_cb)(struct hostapd_iface *iface);
|
||||
int num_ht40_scan_tries;
|
||||
|
||||
struct dl_list sta_seen; /* struct hostapd_sta_info */
|
||||
unsigned int num_sta_seen;
|
||||
|
||||
u8 dfs_domain;
|
||||
#ifdef CONFIG_AIRTIME_POLICY
|
||||
unsigned int airtime_quantum;
|
||||
#endif /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
/* Previous WMM element information */
|
||||
struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
|
||||
};
|
||||
|
||||
/* hostapd.c */
|
||||
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
|
||||
int (*cb)(struct hostapd_iface *iface,
|
||||
void *ctx), void *ctx);
|
||||
int hostapd_reload_config(struct hostapd_iface *iface);
|
||||
void hostapd_reconfig_encryption(struct hostapd_data *hapd);
|
||||
struct hostapd_data *
|
||||
hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
|
||||
struct hostapd_config *conf,
|
||||
struct hostapd_bss_config *bss);
|
||||
int hostapd_setup_interface(struct hostapd_iface *iface);
|
||||
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
|
||||
void hostapd_interface_deinit(struct hostapd_iface *iface);
|
||||
void hostapd_interface_free(struct hostapd_iface *iface);
|
||||
struct hostapd_iface * hostapd_alloc_iface(void);
|
||||
struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
|
||||
const char *config_file);
|
||||
struct hostapd_iface *
|
||||
hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
|
||||
const char *config_fname, int debug);
|
||||
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int reassoc);
|
||||
void hostapd_interface_deinit_free(struct hostapd_iface *iface);
|
||||
int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
|
||||
int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
|
||||
int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
|
||||
int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
|
||||
int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
|
||||
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
|
||||
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
|
||||
const char * hostapd_state_text(enum hostapd_iface_state s);
|
||||
int hostapd_csa_in_progress(struct hostapd_iface *iface);
|
||||
void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled);
|
||||
int hostapd_switch_channel(struct hostapd_data *hapd,
|
||||
struct csa_settings *settings);
|
||||
void
|
||||
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
|
||||
const struct hostapd_freq_params *freq_params);
|
||||
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
|
||||
void hostapd_periodic_iface(struct hostapd_iface *iface);
|
||||
int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
|
||||
|
||||
/* utils.c */
|
||||
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
|
||||
int (*cb)(void *ctx, const u8 *sa,
|
||||
const u8 *da, const u8 *bssid,
|
||||
const u8 *ie, size_t ie_len,
|
||||
int ssi_signal),
|
||||
void *ctx);
|
||||
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
|
||||
|
||||
/* drv_callbacks.c (TODO: move to somewhere else?) */
|
||||
void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *ie, size_t ielen, int reassoc);
|
||||
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
|
||||
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
|
||||
void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason_code);
|
||||
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
|
||||
const u8 *bssid, const u8 *ie, size_t ie_len,
|
||||
int ssi_signal);
|
||||
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
|
||||
int offset, int width, int cf1, int cf2,
|
||||
int finished);
|
||||
struct survey_results;
|
||||
void hostapd_event_get_survey(struct hostapd_iface *iface,
|
||||
struct survey_results *survey_results);
|
||||
void hostapd_acs_channel_selected(struct hostapd_data *hapd,
|
||||
struct acs_selected_channels *acs_res);
|
||||
|
||||
const struct hostapd_eap_user *
|
||||
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
|
||||
size_t identity_len, int phase2);
|
||||
|
||||
struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
|
||||
const char *ifname);
|
||||
void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
|
||||
enum smps_mode smps_mode,
|
||||
enum chan_width chan_width, u8 rx_nss);
|
||||
|
||||
#ifdef CONFIG_FST
|
||||
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
|
||||
struct fst_wpa_obj *iface_obj);
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
#endif /* HOSTAPD_H */
|
255
hostapd-2.9/src/ap/hs20.c
Normal file
255
hostapd-2.9/src/ap/hs20.c
Normal file
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* Hotspot 2.0 AP ANQP processing
|
||||
* Copyright (c) 2009, Atheros Communications, Inc.
|
||||
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "sta_info.h"
|
||||
#include "hs20.h"
|
||||
|
||||
|
||||
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 conf;
|
||||
if (!hapd->conf->hs20)
|
||||
return eid;
|
||||
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
|
||||
WPA_PUT_BE24(eid, OUI_WFA);
|
||||
eid += 3;
|
||||
*eid++ = HS20_INDICATION_OUI_TYPE;
|
||||
conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
|
||||
if (hapd->conf->hs20_release >= 2)
|
||||
conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
|
||||
if (hapd->conf->disable_dgaf)
|
||||
conf |= HS20_DGAF_DISABLED;
|
||||
*eid++ = conf;
|
||||
if (hapd->conf->hs20_release >= 2) {
|
||||
WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
|
||||
eid += 2;
|
||||
}
|
||||
|
||||
return eid;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 *len;
|
||||
u16 capab;
|
||||
|
||||
if (!hapd->conf->osen)
|
||||
return eid;
|
||||
|
||||
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
len = eid++; /* to be filled */
|
||||
WPA_PUT_BE24(eid, OUI_WFA);
|
||||
eid += 3;
|
||||
*eid++ = HS20_OSEN_OUI_TYPE;
|
||||
|
||||
/* Group Data Cipher Suite */
|
||||
RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
|
||||
eid += RSN_SELECTOR_LEN;
|
||||
|
||||
/* Pairwise Cipher Suite Count and List */
|
||||
WPA_PUT_LE16(eid, 1);
|
||||
eid += 2;
|
||||
RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
|
||||
eid += RSN_SELECTOR_LEN;
|
||||
|
||||
/* AKM Suite Count and List */
|
||||
WPA_PUT_LE16(eid, 1);
|
||||
eid += 2;
|
||||
RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
|
||||
eid += RSN_SELECTOR_LEN;
|
||||
|
||||
/* RSN Capabilities */
|
||||
capab = 0;
|
||||
if (hapd->conf->wmm_enabled) {
|
||||
/* 4 PTKSA replay counters when using WMM */
|
||||
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
|
||||
}
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
|
||||
capab |= WPA_CAPABILITY_MFPC;
|
||||
if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
|
||||
capab |= WPA_CAPABILITY_MFPR;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
#ifdef CONFIG_OCV
|
||||
if (hapd->conf->ocv)
|
||||
capab |= WPA_CAPABILITY_OCVC;
|
||||
#endif /* CONFIG_OCV */
|
||||
WPA_PUT_LE16(eid, capab);
|
||||
eid += 2;
|
||||
|
||||
*len = eid - len - 1;
|
||||
|
||||
return eid;
|
||||
}
|
||||
|
||||
|
||||
int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 osu_method, const char *url)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
size_t len = 0;
|
||||
int ret;
|
||||
|
||||
/* TODO: should refuse to send notification if the STA is not associated
|
||||
* or if the STA did not indicate support for WNM-Notification */
|
||||
|
||||
if (url) {
|
||||
len = 1 + os_strlen(url);
|
||||
if (5 + len > 255) {
|
||||
wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
|
||||
"WNM-Notification: '%s'", url);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
buf = wpabuf_alloc(4 + 7 + len);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
||||
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
|
||||
wpabuf_put_u8(buf, 1); /* Dialog token */
|
||||
wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
|
||||
|
||||
/* Subscription Remediation subelement */
|
||||
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
||||
wpabuf_put_u8(buf, 5 + len);
|
||||
wpabuf_put_be24(buf, OUI_WFA);
|
||||
wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
|
||||
if (url) {
|
||||
wpabuf_put_u8(buf, len - 1);
|
||||
wpabuf_put_data(buf, url, len - 1);
|
||||
wpabuf_put_u8(buf, osu_method);
|
||||
} else {
|
||||
/* Server URL and Server Method fields not included */
|
||||
wpabuf_put_u8(buf, 0);
|
||||
}
|
||||
|
||||
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
|
||||
wpabuf_free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
|
||||
const u8 *addr,
|
||||
const struct wpabuf *payload)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
int ret;
|
||||
|
||||
/* TODO: should refuse to send notification if the STA is not associated
|
||||
* or if the STA did not indicate support for WNM-Notification */
|
||||
|
||||
buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
||||
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
|
||||
wpabuf_put_u8(buf, 1); /* Dialog token */
|
||||
wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
|
||||
|
||||
/* Deauthentication Imminent Notice subelement */
|
||||
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
||||
wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
|
||||
wpabuf_put_be24(buf, OUI_WFA);
|
||||
wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
|
||||
wpabuf_put_buf(buf, payload);
|
||||
|
||||
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
|
||||
wpabuf_free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
|
||||
const u8 *addr, const char *url)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
int ret;
|
||||
size_t url_len;
|
||||
|
||||
if (!url) {
|
||||
wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
url_len = os_strlen(url);
|
||||
if (5 + url_len > 255) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
|
||||
url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = wpabuf_alloc(4 + 7 + url_len);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
||||
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
|
||||
wpabuf_put_u8(buf, 1); /* Dialog token */
|
||||
wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
|
||||
|
||||
/* Terms and Conditions Acceptance subelement */
|
||||
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
||||
wpabuf_put_u8(buf, 4 + 1 + url_len);
|
||||
wpabuf_put_be24(buf, OUI_WFA);
|
||||
wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
|
||||
wpabuf_put_u8(buf, url_len);
|
||||
wpabuf_put_str(buf, url);
|
||||
|
||||
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
|
||||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
|
||||
wpabuf_free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"HS 2.0: Terms and Conditions filtering required for "
|
||||
MACSTR, MAC2STR(sta->addr));
|
||||
sta->hs20_t_c_filtering = 1;
|
||||
/* TODO: Enable firewall filtering for the STA */
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
|
||||
MAC2STR(sta->addr));
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"HS 2.0: Terms and Conditions filtering not required for "
|
||||
MACSTR, MAC2STR(sta->addr));
|
||||
sta->hs20_t_c_filtering = 0;
|
||||
/* TODO: Disable firewall filtering for the STA */
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO,
|
||||
HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
|
||||
}
|
||||
}
|
26
hostapd-2.9/src/ap/hs20.h
Normal file
26
hostapd-2.9/src/ap/hs20.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Hotspot 2.0 AP ANQP processing
|
||||
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef HS20_H
|
||||
#define HS20_H
|
||||
|
||||
struct hostapd_data;
|
||||
|
||||
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
|
||||
int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 osu_method, const char *url);
|
||||
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
|
||||
const u8 *addr,
|
||||
const struct wpabuf *payload);
|
||||
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
|
||||
const u8 *addr, const char *url);
|
||||
void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int enabled);
|
||||
|
||||
#endif /* HS20_H */
|
974
hostapd-2.9/src/ap/hw_features.c
Normal file
974
hostapd-2.9/src/ap/hw_features.c
Normal file
|
@ -0,0 +1,974 @@
|
|||
/*
|
||||
* hostapd / Hardware feature query and different modes
|
||||
* Copyright 2002-2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "common/hw_features_common.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "acs.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "beacon.h"
|
||||
#include "hw_features.h"
|
||||
|
||||
|
||||
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
||||
size_t num_hw_features)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (hw_features == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_hw_features; i++) {
|
||||
os_free(hw_features[i].channels);
|
||||
os_free(hw_features[i].rates);
|
||||
}
|
||||
|
||||
os_free(hw_features);
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_STDOUT_DEBUG
|
||||
static char * dfs_info(struct hostapd_channel_data *chan)
|
||||
{
|
||||
static char info[256];
|
||||
char *state;
|
||||
|
||||
switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
|
||||
case HOSTAPD_CHAN_DFS_UNKNOWN:
|
||||
state = "unknown";
|
||||
break;
|
||||
case HOSTAPD_CHAN_DFS_USABLE:
|
||||
state = "usable";
|
||||
break;
|
||||
case HOSTAPD_CHAN_DFS_UNAVAILABLE:
|
||||
state = "unavailable";
|
||||
break;
|
||||
case HOSTAPD_CHAN_DFS_AVAILABLE:
|
||||
state = "available";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
|
||||
info[sizeof(info) - 1] = '\0';
|
||||
|
||||
return info;
|
||||
}
|
||||
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
||||
|
||||
|
||||
int hostapd_get_hw_features(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_data *hapd = iface->bss[0];
|
||||
int i, j;
|
||||
u16 num_modes, flags;
|
||||
struct hostapd_hw_modes *modes;
|
||||
u8 dfs_domain;
|
||||
|
||||
if (hostapd_drv_none(hapd))
|
||||
return -1;
|
||||
modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
|
||||
&dfs_domain);
|
||||
if (modes == NULL) {
|
||||
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"Fetching hardware channel/rate support not "
|
||||
"supported.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
iface->hw_flags = flags;
|
||||
iface->dfs_domain = dfs_domain;
|
||||
|
||||
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
|
||||
iface->hw_features = modes;
|
||||
iface->num_hw_features = num_modes;
|
||||
|
||||
for (i = 0; i < num_modes; i++) {
|
||||
struct hostapd_hw_modes *feature = &modes[i];
|
||||
int dfs_enabled = hapd->iconf->ieee80211h &&
|
||||
(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
|
||||
|
||||
/* set flag for channels we can use in current regulatory
|
||||
* domain */
|
||||
for (j = 0; j < feature->num_channels; j++) {
|
||||
int dfs = 0;
|
||||
|
||||
/*
|
||||
* Disable all channels that are marked not to allow
|
||||
* to initiate radiation (a.k.a. passive scan and no
|
||||
* IBSS).
|
||||
* Use radar channels only if the driver supports DFS.
|
||||
*/
|
||||
if ((feature->channels[j].flag &
|
||||
HOSTAPD_CHAN_RADAR) && dfs_enabled) {
|
||||
dfs = 1;
|
||||
} else if (((feature->channels[j].flag &
|
||||
HOSTAPD_CHAN_RADAR) &&
|
||||
!(iface->drv_flags &
|
||||
WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
|
||||
(feature->channels[j].flag &
|
||||
HOSTAPD_CHAN_NO_IR)) {
|
||||
feature->channels[j].flag |=
|
||||
HOSTAPD_CHAN_DISABLED;
|
||||
}
|
||||
|
||||
if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
|
||||
wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
|
||||
"chan=%d freq=%d MHz max_tx_power=%d dBm%s",
|
||||
feature->mode,
|
||||
feature->channels[j].chan,
|
||||
feature->channels[j].freq,
|
||||
feature->channels[j].max_tx_power,
|
||||
dfs ? dfs_info(&feature->channels[j]) : "");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_prepare_rates(struct hostapd_iface *iface,
|
||||
struct hostapd_hw_modes *mode)
|
||||
{
|
||||
int i, num_basic_rates = 0;
|
||||
int basic_rates_a[] = { 60, 120, 240, -1 };
|
||||
int basic_rates_b[] = { 10, 20, -1 };
|
||||
int basic_rates_g[] = { 10, 20, 55, 110, -1 };
|
||||
int *basic_rates;
|
||||
|
||||
if (iface->conf->basic_rates)
|
||||
basic_rates = iface->conf->basic_rates;
|
||||
else switch (mode->mode) {
|
||||
case HOSTAPD_MODE_IEEE80211A:
|
||||
basic_rates = basic_rates_a;
|
||||
break;
|
||||
case HOSTAPD_MODE_IEEE80211B:
|
||||
basic_rates = basic_rates_b;
|
||||
break;
|
||||
case HOSTAPD_MODE_IEEE80211G:
|
||||
basic_rates = basic_rates_g;
|
||||
break;
|
||||
case HOSTAPD_MODE_IEEE80211AD:
|
||||
return 0; /* No basic rates for 11ad */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (basic_rates[i] >= 0)
|
||||
i++;
|
||||
if (i)
|
||||
i++; /* -1 termination */
|
||||
os_free(iface->basic_rates);
|
||||
iface->basic_rates = os_malloc(i * sizeof(int));
|
||||
if (iface->basic_rates)
|
||||
os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
|
||||
|
||||
os_free(iface->current_rates);
|
||||
iface->num_rates = 0;
|
||||
|
||||
iface->current_rates =
|
||||
os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
|
||||
if (!iface->current_rates) {
|
||||
wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
|
||||
"table.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < mode->num_rates; i++) {
|
||||
struct hostapd_rate_data *rate;
|
||||
|
||||
if (iface->conf->supported_rates &&
|
||||
!hostapd_rate_found(iface->conf->supported_rates,
|
||||
mode->rates[i]))
|
||||
continue;
|
||||
|
||||
rate = &iface->current_rates[iface->num_rates];
|
||||
rate->rate = mode->rates[i];
|
||||
if (hostapd_rate_found(basic_rates, rate->rate)) {
|
||||
rate->flags |= HOSTAPD_RATE_BASIC;
|
||||
num_basic_rates++;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
|
||||
iface->num_rates, rate->rate, rate->flags);
|
||||
iface->num_rates++;
|
||||
}
|
||||
|
||||
if ((iface->num_rates == 0 || num_basic_rates == 0) &&
|
||||
(!iface->conf->ieee80211n || !iface->conf->require_ht)) {
|
||||
wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
|
||||
"rate sets (%d,%d).",
|
||||
iface->num_rates, num_basic_rates);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
|
||||
{
|
||||
int pri_chan, sec_chan;
|
||||
|
||||
pri_chan = iface->conf->channel;
|
||||
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
||||
|
||||
return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
|
||||
sec_chan);
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
|
||||
{
|
||||
if (iface->conf->secondary_channel > 0) {
|
||||
iface->conf->channel += 4;
|
||||
iface->conf->secondary_channel = -1;
|
||||
} else {
|
||||
iface->conf->channel -= 4;
|
||||
iface->conf->secondary_channel = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
|
||||
struct wpa_scan_results *scan_res)
|
||||
{
|
||||
int pri_chan, sec_chan;
|
||||
int res;
|
||||
|
||||
pri_chan = iface->conf->channel;
|
||||
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
||||
|
||||
res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
|
||||
|
||||
if (res == 2) {
|
||||
if (iface->conf->no_pri_sec_switch) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Cannot switch PRI/SEC channels due to local constraint");
|
||||
} else {
|
||||
ieee80211n_switch_pri_sec(iface);
|
||||
}
|
||||
}
|
||||
|
||||
return !!res;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
|
||||
struct wpa_scan_results *scan_res)
|
||||
{
|
||||
int pri_chan, sec_chan;
|
||||
|
||||
pri_chan = iface->conf->channel;
|
||||
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
|
||||
|
||||
return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
|
||||
sec_chan);
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211n_check_scan(struct hostapd_iface *iface)
|
||||
{
|
||||
struct wpa_scan_results *scan_res;
|
||||
int oper40;
|
||||
int res;
|
||||
|
||||
/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
|
||||
* allowed per IEEE Std 802.11-2012, 10.15.3.2 */
|
||||
|
||||
iface->scan_cb = NULL;
|
||||
|
||||
scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
|
||||
if (scan_res == NULL) {
|
||||
hostapd_setup_interface_complete(iface, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
|
||||
oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
|
||||
else
|
||||
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
|
||||
wpa_scan_results_free(scan_res);
|
||||
|
||||
iface->secondary_ch = iface->conf->secondary_channel;
|
||||
if (!oper40) {
|
||||
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
|
||||
"channel pri=%d sec=%d based on overlapping BSSes",
|
||||
iface->conf->channel,
|
||||
iface->conf->channel +
|
||||
iface->conf->secondary_channel * 4);
|
||||
iface->conf->secondary_channel = 0;
|
||||
if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
|
||||
/*
|
||||
* TODO: Could consider scheduling another scan to check
|
||||
* if channel width can be changed if no coex reports
|
||||
* are received from associating stations.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
res = ieee80211n_allowed_ht40_channel_pair(iface);
|
||||
if (!res) {
|
||||
iface->conf->secondary_channel = 0;
|
||||
hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
|
||||
hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
|
||||
hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
|
||||
res = 1;
|
||||
wpa_printf(MSG_INFO, "Fallback to 20 MHz");
|
||||
}
|
||||
|
||||
hostapd_setup_interface_complete(iface, !res);
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
|
||||
struct wpa_driver_scan_params *params)
|
||||
{
|
||||
/* Scan only the affected frequency range */
|
||||
int pri_freq, sec_freq;
|
||||
int affected_start, affected_end;
|
||||
int i, pos;
|
||||
struct hostapd_hw_modes *mode;
|
||||
|
||||
if (iface->current_mode == NULL)
|
||||
return;
|
||||
|
||||
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
|
||||
if (iface->conf->secondary_channel > 0)
|
||||
sec_freq = pri_freq + 20;
|
||||
else
|
||||
sec_freq = pri_freq - 20;
|
||||
/*
|
||||
* Note: Need to find the PRI channel also in cases where the affected
|
||||
* channel is the SEC channel of a 40 MHz BSS, so need to include the
|
||||
* scanning coverage here to be 40 MHz from the center frequency.
|
||||
*/
|
||||
affected_start = (pri_freq + sec_freq) / 2 - 40;
|
||||
affected_end = (pri_freq + sec_freq) / 2 + 40;
|
||||
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
|
||||
affected_start, affected_end);
|
||||
|
||||
mode = iface->current_mode;
|
||||
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
|
||||
if (params->freqs == NULL)
|
||||
return;
|
||||
pos = 0;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
if (chan->freq < affected_start ||
|
||||
chan->freq > affected_end)
|
||||
continue;
|
||||
params->freqs[pos++] = chan->freq;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
|
||||
struct wpa_driver_scan_params *params)
|
||||
{
|
||||
/* Scan only the affected frequency range */
|
||||
int pri_freq;
|
||||
int affected_start, affected_end;
|
||||
int i, pos;
|
||||
struct hostapd_hw_modes *mode;
|
||||
|
||||
if (iface->current_mode == NULL)
|
||||
return;
|
||||
|
||||
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
|
||||
if (iface->conf->secondary_channel > 0) {
|
||||
affected_start = pri_freq - 10;
|
||||
affected_end = pri_freq + 30;
|
||||
} else {
|
||||
affected_start = pri_freq - 30;
|
||||
affected_end = pri_freq + 10;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
|
||||
affected_start, affected_end);
|
||||
|
||||
mode = iface->current_mode;
|
||||
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
|
||||
if (params->freqs == NULL)
|
||||
return;
|
||||
pos = 0;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
if (chan->freq < affected_start ||
|
||||
chan->freq > affected_end)
|
||||
continue;
|
||||
params->freqs[pos++] = chan->freq;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
|
||||
{
|
||||
#define HT2040_COEX_SCAN_RETRY 15
|
||||
struct hostapd_iface *iface = eloop_data;
|
||||
struct wpa_driver_scan_params params;
|
||||
int ret;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
|
||||
ieee80211n_scan_channels_2g4(iface, ¶ms);
|
||||
else
|
||||
ieee80211n_scan_channels_5g(iface, ¶ms);
|
||||
|
||||
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
|
||||
iface->num_ht40_scan_tries++;
|
||||
os_free(params.freqs);
|
||||
|
||||
if (ret == -EBUSY &&
|
||||
iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
|
||||
ret, strerror(-ret), iface->num_ht40_scan_tries);
|
||||
eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
iface->scan_cb = ieee80211n_check_scan;
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Failed to request a scan in device, bringing up in HT20 mode");
|
||||
iface->conf->secondary_channel = 0;
|
||||
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
|
||||
hostapd_setup_interface_complete(iface, 0);
|
||||
}
|
||||
|
||||
|
||||
void hostapd_stop_setup_timers(struct hostapd_iface *iface)
|
||||
{
|
||||
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
|
||||
{
|
||||
struct wpa_driver_scan_params params;
|
||||
int ret;
|
||||
|
||||
/* Check that HT40 is used and PRI / SEC switch is allowed */
|
||||
if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
|
||||
return 0;
|
||||
|
||||
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
|
||||
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
|
||||
"40 MHz channel");
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
|
||||
ieee80211n_scan_channels_2g4(iface, ¶ms);
|
||||
else
|
||||
ieee80211n_scan_channels_5g(iface, ¶ms);
|
||||
|
||||
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
|
||||
os_free(params.freqs);
|
||||
|
||||
if (ret == -EBUSY) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
|
||||
ret, strerror(-ret));
|
||||
iface->num_ht40_scan_tries = 1;
|
||||
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
|
||||
eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
|
||||
|
||||
//DRIVER_RTW Modify
|
||||
//return -1;
|
||||
return 0;//ignore this error
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Failed to request a scan of neighboring BSSes ret=%d (%s)",
|
||||
ret, strerror(-ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
iface->scan_cb = ieee80211n_check_scan;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
|
||||
{
|
||||
u16 hw = iface->current_mode->ht_capab;
|
||||
u16 conf = iface->conf->ht_capab;
|
||||
|
||||
if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
|
||||
!(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [LDPC]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Driver ACS chosen channel may not be HT40 due to internal driver
|
||||
* restrictions.
|
||||
*/
|
||||
if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
|
||||
!(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [HT40*]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (conf & HT_CAP_INFO_SMPS_MASK) {
|
||||
case HT_CAP_INFO_SMPS_STATIC:
|
||||
if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Driver does not support configured HT capability [SMPS-STATIC]");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case HT_CAP_INFO_SMPS_DYNAMIC:
|
||||
if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Driver does not support configured HT capability [SMPS-DYNAMIC]");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case HT_CAP_INFO_SMPS_DISABLED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
|
||||
!(hw & HT_CAP_INFO_GREEN_FIELD)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [GF]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
|
||||
!(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [SHORT-GI-20]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
|
||||
!(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [SHORT-GI-40]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [TX-STBC]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
|
||||
(hw & HT_CAP_INFO_RX_STBC_MASK)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [RX-STBC*]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_DELAYED_BA) &&
|
||||
!(hw & HT_CAP_INFO_DELAYED_BA)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [DELAYED-BA]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
|
||||
!(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [MAX-AMSDU-7935]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
|
||||
!(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [DSSS_CCK-40]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
|
||||
!(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
|
||||
wpa_printf(MSG_ERROR, "Driver does not support configured "
|
||||
"HT capability [LSIG-TXOP-PROT]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211AC
|
||||
static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_hw_modes *mode = iface->current_mode;
|
||||
u32 hw = mode->vht_capab;
|
||||
u32 conf = iface->conf->vht_capab;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
|
||||
hw, conf);
|
||||
|
||||
if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
|
||||
iface->conf->bss[0]->vendor_vht &&
|
||||
mode->vht_capab == 0 && iface->hw_features) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iface->num_hw_features; i++) {
|
||||
if (iface->hw_features[i].mode ==
|
||||
HOSTAPD_MODE_IEEE80211A) {
|
||||
mode = &iface->hw_features[i];
|
||||
hw = mode->vht_capab;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"update hw vht capab based on 5 GHz band: 0x%x",
|
||||
hw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ieee80211ac_cap_check(hw, conf);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211AC */
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211AX
|
||||
static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211AX */
|
||||
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
|
||||
int hostapd_check_ht_capab(struct hostapd_iface *iface)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
int ret;
|
||||
if (!iface->conf->ieee80211n)
|
||||
return 0;
|
||||
|
||||
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
|
||||
iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
|
||||
(iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Disable HT capability [DSSS_CCK-40] on 5 GHz band");
|
||||
iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
|
||||
}
|
||||
|
||||
if (!ieee80211n_supported_ht_capab(iface))
|
||||
return -1;
|
||||
#ifdef CONFIG_IEEE80211AX
|
||||
if (iface->conf->ieee80211ax &&
|
||||
!ieee80211ax_supported_he_capab(iface))
|
||||
return -1;
|
||||
#endif /* CONFIG_IEEE80211AX */
|
||||
#ifdef CONFIG_IEEE80211AC
|
||||
if (iface->conf->ieee80211ac &&
|
||||
!ieee80211ac_supported_vht_capab(iface))
|
||||
return -1;
|
||||
#endif /* CONFIG_IEEE80211AC */
|
||||
ret = ieee80211n_check_40mhz(iface);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!ieee80211n_allowed_ht40_channel_pair(iface))
|
||||
return -1;
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
|
||||
int channel, int primary)
|
||||
{
|
||||
struct hostapd_channel_data *chan;
|
||||
|
||||
if (!iface->current_mode)
|
||||
return 0;
|
||||
|
||||
chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
|
||||
if (!chan)
|
||||
return 0;
|
||||
|
||||
if ((primary && chan_pri_allowed(chan)) ||
|
||||
(!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
|
||||
return 1;
|
||||
|
||||
wpa_printf(MSG_INFO,
|
||||
"Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
|
||||
channel, primary ? "primary" : "secondary",
|
||||
chan->flag,
|
||||
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
|
||||
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
|
||||
{
|
||||
int secondary_chan;
|
||||
struct hostapd_channel_data *pri_chan;
|
||||
|
||||
pri_chan = hw_get_channel_chan(iface->current_mode,
|
||||
iface->conf->channel, NULL);
|
||||
if (!pri_chan)
|
||||
return 0;
|
||||
|
||||
if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
|
||||
return 0;
|
||||
|
||||
if (!iface->conf->secondary_channel)
|
||||
return 1;
|
||||
|
||||
if (!iface->conf->ht40_plus_minus_allowed)
|
||||
return hostapd_is_usable_chan(
|
||||
iface, iface->conf->channel +
|
||||
iface->conf->secondary_channel * 4, 0);
|
||||
|
||||
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
|
||||
secondary_chan = iface->conf->channel + 4;
|
||||
if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
|
||||
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
|
||||
iface->conf->secondary_channel = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
secondary_chan = iface->conf->channel - 4;
|
||||
if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
|
||||
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
|
||||
iface->conf->secondary_channel = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static enum hostapd_chan_status
|
||||
hostapd_check_chans(struct hostapd_iface *iface)
|
||||
{
|
||||
if (iface->conf->channel) {
|
||||
if (hostapd_is_usable_chans(iface))
|
||||
return HOSTAPD_CHAN_VALID;
|
||||
else
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* The user set channel=0 or channel=acs_survey
|
||||
* which is used to trigger ACS.
|
||||
*/
|
||||
|
||||
switch (acs_init(iface)) {
|
||||
case HOSTAPD_CHAN_ACS:
|
||||
return HOSTAPD_CHAN_ACS;
|
||||
case HOSTAPD_CHAN_VALID:
|
||||
case HOSTAPD_CHAN_INVALID:
|
||||
default:
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
|
||||
{
|
||||
if (!iface->current_mode) {
|
||||
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_WARNING,
|
||||
"Hardware does not support configured mode");
|
||||
return;
|
||||
}
|
||||
hostapd_logger(iface->bss[0], NULL,
|
||||
HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_WARNING,
|
||||
"Configured channel (%d) not found from the "
|
||||
"channel list of current mode (%d) %s",
|
||||
iface->conf->channel,
|
||||
iface->current_mode->mode,
|
||||
hostapd_hw_mode_txt(iface->current_mode->mode));
|
||||
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_WARNING,
|
||||
"Hardware does not support configured channel");
|
||||
}
|
||||
|
||||
|
||||
int hostapd_acs_completed(struct hostapd_iface *iface, int err)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
switch (hostapd_check_chans(iface)) {
|
||||
case HOSTAPD_CHAN_VALID:
|
||||
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
|
||||
ACS_EVENT_COMPLETED "freq=%d channel=%d",
|
||||
hostapd_hw_get_freq(iface->bss[0],
|
||||
iface->conf->channel),
|
||||
iface->conf->channel);
|
||||
break;
|
||||
case HOSTAPD_CHAN_ACS:
|
||||
wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
|
||||
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
|
||||
hostapd_notify_bad_chans(iface);
|
||||
goto out;
|
||||
case HOSTAPD_CHAN_INVALID:
|
||||
default:
|
||||
wpa_printf(MSG_ERROR, "ACS picked unusable channels");
|
||||
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
|
||||
hostapd_notify_bad_chans(iface);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = hostapd_check_ht_capab(iface);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret == 1) {
|
||||
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return hostapd_setup_interface_complete(iface, ret);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_select_hw_mode - Select the hardware mode
|
||||
* @iface: Pointer to interface data.
|
||||
* Returns: 0 on success, < 0 on failure
|
||||
*
|
||||
* Sets up the hardware mode, channel, rates, and passive scanning
|
||||
* based on the configuration.
|
||||
*/
|
||||
int hostapd_select_hw_mode(struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (iface->num_hw_features < 1)
|
||||
return -1;
|
||||
|
||||
if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
|
||||
iface->conf->ieee80211n || iface->conf->ieee80211ac ||
|
||||
iface->conf->ieee80211ax) &&
|
||||
iface->conf->channel == 14) {
|
||||
wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
|
||||
iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
|
||||
iface->conf->ieee80211n = 0;
|
||||
iface->conf->ieee80211ac = 0;
|
||||
iface->conf->ieee80211ax = 0;
|
||||
}
|
||||
|
||||
iface->current_mode = NULL;
|
||||
for (i = 0; i < iface->num_hw_features; i++) {
|
||||
struct hostapd_hw_modes *mode = &iface->hw_features[i];
|
||||
if (mode->mode == iface->conf->hw_mode) {
|
||||
iface->current_mode = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iface->current_mode == NULL) {
|
||||
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
|
||||
!(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
|
||||
{
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Hardware does not support configured mode");
|
||||
hostapd_logger(iface->bss[0], NULL,
|
||||
HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_WARNING,
|
||||
"Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
|
||||
(int) iface->conf->hw_mode);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
switch (hostapd_check_chans(iface)) {
|
||||
case HOSTAPD_CHAN_VALID:
|
||||
return 0;
|
||||
case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
|
||||
return 1;
|
||||
case HOSTAPD_CHAN_INVALID:
|
||||
default:
|
||||
hostapd_notify_bad_chans(iface);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char * hostapd_hw_mode_txt(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case HOSTAPD_MODE_IEEE80211A:
|
||||
return "IEEE 802.11a";
|
||||
case HOSTAPD_MODE_IEEE80211B:
|
||||
return "IEEE 802.11b";
|
||||
case HOSTAPD_MODE_IEEE80211G:
|
||||
return "IEEE 802.11g";
|
||||
case HOSTAPD_MODE_IEEE80211AD:
|
||||
return "IEEE 802.11ad";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
|
||||
{
|
||||
return hw_get_freq(hapd->iface->current_mode, chan);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
|
||||
{
|
||||
int i, channel;
|
||||
struct hostapd_hw_modes *mode;
|
||||
|
||||
if (hapd->iface->current_mode) {
|
||||
channel = hw_get_chan(hapd->iface->current_mode, freq);
|
||||
if (channel)
|
||||
return channel;
|
||||
}
|
||||
|
||||
/* Check other available modes since the channel list for the current
|
||||
* mode did not include the specified frequency. */
|
||||
if (!hapd->iface->hw_features)
|
||||
return 0;
|
||||
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||
mode = &hapd->iface->hw_features[i];
|
||||
channel = hw_get_chan(mode, freq);
|
||||
if (channel)
|
||||
return channel;
|
||||
}
|
||||
return 0;
|
||||
}
|
76
hostapd-2.9/src/ap/hw_features.h
Normal file
76
hostapd-2.9/src/ap/hw_features.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* hostapd / Hardware feature query and different modes
|
||||
* Copyright 2002-2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef HW_FEATURES_H
|
||||
#define HW_FEATURES_H
|
||||
|
||||
#ifdef NEED_AP_MLME
|
||||
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
||||
size_t num_hw_features);
|
||||
int hostapd_get_hw_features(struct hostapd_iface *iface);
|
||||
int hostapd_acs_completed(struct hostapd_iface *iface, int err);
|
||||
int hostapd_select_hw_mode(struct hostapd_iface *iface);
|
||||
const char * hostapd_hw_mode_txt(int mode);
|
||||
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
|
||||
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
|
||||
int hostapd_check_ht_capab(struct hostapd_iface *iface);
|
||||
int hostapd_prepare_rates(struct hostapd_iface *iface,
|
||||
struct hostapd_hw_modes *mode);
|
||||
void hostapd_stop_setup_timers(struct hostapd_iface *iface);
|
||||
#else /* NEED_AP_MLME */
|
||||
static inline void
|
||||
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
||||
size_t num_hw_features)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
|
||||
{
|
||||
return -100;
|
||||
}
|
||||
|
||||
static inline const char * hostapd_hw_mode_txt(int mode)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
|
||||
struct hostapd_hw_modes *mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* NEED_AP_MLME */
|
||||
|
||||
#endif /* HW_FEATURES_H */
|
542
hostapd-2.9/src/ap/iapp.c
Normal file
542
hostapd-2.9/src/ap/iapp.c
Normal file
|
@ -0,0 +1,542 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
|
||||
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*
|
||||
* Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
|
||||
* and IEEE has withdrawn it. In other words, it is likely better to look at
|
||||
* using some other mechanism for AP-to-AP communication than extending the
|
||||
* implementation here.
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
* Level 1: no administrative or security support
|
||||
* (e.g., static BSSID to IP address mapping in each AP)
|
||||
* Level 2: support for dynamic mapping of BSSID to IP address
|
||||
* Level 3: support for encryption and authentication of IAPP messages
|
||||
* - add support for MOVE-notify and MOVE-response (this requires support for
|
||||
* finding out IP address for previous AP using RADIUS)
|
||||
* - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
|
||||
* reassociation to another AP
|
||||
* - implement counters etc. for IAPP MIB
|
||||
* - verify endianness of fields in IAPP messages; are they big-endian as
|
||||
* used here?
|
||||
* - RADIUS connection for AP registration and BSSID to IP address mapping
|
||||
* - TCP connection for IAPP MOVE, CACHE
|
||||
* - broadcast ESP for IAPP ADD-notify
|
||||
* - ESP for IAPP MOVE messages
|
||||
* - security block sending/processing
|
||||
* - IEEE 802.11 context transfer
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netpacket/packet.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "iapp.h"
|
||||
|
||||
|
||||
#define IAPP_MULTICAST "224.0.1.178"
|
||||
#define IAPP_UDP_PORT 3517
|
||||
#define IAPP_TCP_PORT 3517
|
||||
|
||||
struct iapp_hdr {
|
||||
u8 version;
|
||||
u8 command;
|
||||
be16 identifier;
|
||||
be16 length;
|
||||
/* followed by length-6 octets of data */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IAPP_VERSION 0
|
||||
|
||||
enum IAPP_COMMAND {
|
||||
IAPP_CMD_ADD_notify = 0,
|
||||
IAPP_CMD_MOVE_notify = 1,
|
||||
IAPP_CMD_MOVE_response = 2,
|
||||
IAPP_CMD_Send_Security_Block = 3,
|
||||
IAPP_CMD_ACK_Security_Block = 4,
|
||||
IAPP_CMD_CACHE_notify = 5,
|
||||
IAPP_CMD_CACHE_response = 6,
|
||||
};
|
||||
|
||||
|
||||
/* ADD-notify - multicast UDP on the local LAN */
|
||||
struct iapp_add_notify {
|
||||
u8 addr_len; /* ETH_ALEN */
|
||||
u8 reserved;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
be16 seq_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
|
||||
struct iapp_layer2_update {
|
||||
u8 da[ETH_ALEN]; /* broadcast */
|
||||
u8 sa[ETH_ALEN]; /* STA addr */
|
||||
be16 len; /* 6 */
|
||||
u8 dsap; /* null DSAP address */
|
||||
u8 ssap; /* null SSAP address, CR=Response */
|
||||
u8 control;
|
||||
u8 xid_info[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* MOVE-notify - unicast TCP */
|
||||
struct iapp_move_notify {
|
||||
u8 addr_len; /* ETH_ALEN */
|
||||
u8 reserved;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 seq_num;
|
||||
u16 ctx_block_len;
|
||||
/* followed by ctx_block_len bytes */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* MOVE-response - unicast TCP */
|
||||
struct iapp_move_response {
|
||||
u8 addr_len; /* ETH_ALEN */
|
||||
u8 status;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 seq_num;
|
||||
u16 ctx_block_len;
|
||||
/* followed by ctx_block_len bytes */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum {
|
||||
IAPP_MOVE_SUCCESSFUL = 0,
|
||||
IAPP_MOVE_DENIED = 1,
|
||||
IAPP_MOVE_STALE_MOVE = 2,
|
||||
};
|
||||
|
||||
|
||||
/* CACHE-notify */
|
||||
struct iapp_cache_notify {
|
||||
u8 addr_len; /* ETH_ALEN */
|
||||
u8 reserved;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 seq_num;
|
||||
u8 current_ap[ETH_ALEN];
|
||||
u16 ctx_block_len;
|
||||
/* ctx_block_len bytes of context block followed by 16-bit context
|
||||
* timeout */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* CACHE-response - unicast TCP */
|
||||
struct iapp_cache_response {
|
||||
u8 addr_len; /* ETH_ALEN */
|
||||
u8 status;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 seq_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum {
|
||||
IAPP_CACHE_SUCCESSFUL = 0,
|
||||
IAPP_CACHE_STALE_CACHE = 1,
|
||||
};
|
||||
|
||||
|
||||
/* Send-Security-Block - unicast TCP */
|
||||
struct iapp_send_security_block {
|
||||
u8 iv[8];
|
||||
u16 sec_block_len;
|
||||
/* followed by sec_block_len bytes of security block */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* ACK-Security-Block - unicast TCP */
|
||||
struct iapp_ack_security_block {
|
||||
u8 iv[8];
|
||||
u8 new_ap_ack_authenticator[48];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct iapp_data {
|
||||
struct hostapd_data *hapd;
|
||||
u16 identifier; /* next IAPP identifier */
|
||||
struct in_addr own, multicast;
|
||||
int udp_sock;
|
||||
int packet_sock;
|
||||
};
|
||||
|
||||
|
||||
static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
|
||||
{
|
||||
char buf[128];
|
||||
struct iapp_hdr *hdr;
|
||||
struct iapp_add_notify *add;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
/* Send IAPP ADD-notify to remove possible association from other APs
|
||||
*/
|
||||
|
||||
hdr = (struct iapp_hdr *) buf;
|
||||
hdr->version = IAPP_VERSION;
|
||||
hdr->command = IAPP_CMD_ADD_notify;
|
||||
hdr->identifier = host_to_be16(iapp->identifier++);
|
||||
hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
|
||||
|
||||
add = (struct iapp_add_notify *) (hdr + 1);
|
||||
add->addr_len = ETH_ALEN;
|
||||
add->reserved = 0;
|
||||
os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
|
||||
|
||||
add->seq_num = host_to_be16(seq_num);
|
||||
|
||||
os_memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = iapp->multicast.s_addr;
|
||||
addr.sin_port = htons(IAPP_UDP_PORT);
|
||||
if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
|
||||
(struct sockaddr *) &addr, sizeof(addr)) < 0)
|
||||
wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
|
||||
{
|
||||
struct iapp_layer2_update msg;
|
||||
|
||||
/* Send Level 2 Update Frame to update forwarding tables in layer 2
|
||||
* bridge devices */
|
||||
|
||||
/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
|
||||
* Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
|
||||
|
||||
os_memset(msg.da, 0xff, ETH_ALEN);
|
||||
os_memcpy(msg.sa, addr, ETH_ALEN);
|
||||
msg.len = host_to_be16(6);
|
||||
msg.dsap = 0; /* NULL DSAP address */
|
||||
msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
|
||||
msg.control = 0xaf; /* XID response lsb.1111F101.
|
||||
* F=0 (no poll command; unsolicited frame) */
|
||||
msg.xid_info[0] = 0x81; /* XID format identifier */
|
||||
msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
|
||||
msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
|
||||
* FIX: what is correct RW with 802.11? */
|
||||
|
||||
if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
|
||||
wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iapp_new_station - IAPP processing for a new STA
|
||||
* @iapp: IAPP data
|
||||
* @sta: The associated station
|
||||
*/
|
||||
void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
|
||||
{
|
||||
u16 seq = 0; /* TODO */
|
||||
|
||||
if (iapp == NULL)
|
||||
return;
|
||||
|
||||
/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
|
||||
hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
|
||||
HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
|
||||
iapp_send_layer2_update(iapp, sta->addr);
|
||||
iapp_send_add(iapp, sta->addr, seq);
|
||||
|
||||
/* TODO: If this was reassociation:
|
||||
* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
|
||||
* Context Block, Timeout)
|
||||
* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
|
||||
* IP address */
|
||||
}
|
||||
|
||||
|
||||
static void iapp_process_add_notify(struct iapp_data *iapp,
|
||||
struct sockaddr_in *from,
|
||||
struct iapp_hdr *hdr, int len)
|
||||
{
|
||||
struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
|
||||
struct sta_info *sta;
|
||||
|
||||
if (len != sizeof(*add)) {
|
||||
wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
|
||||
len, (unsigned long) sizeof(*add));
|
||||
return;
|
||||
}
|
||||
|
||||
sta = ap_get_sta(iapp->hapd, add->mac_addr);
|
||||
|
||||
/* IAPP-ADD.indication(MAC Address, Sequence Number) */
|
||||
hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Received IAPP ADD-notify (seq# %d) from %s:%d%s",
|
||||
be_to_host16(add->seq_num),
|
||||
inet_ntoa(from->sin_addr), ntohs(from->sin_port),
|
||||
sta ? "" : " (STA not found)");
|
||||
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
/* TODO: could use seq_num to try to determine whether last association
|
||||
* to this AP is newer than the one advertised in IAPP-ADD. Although,
|
||||
* this is not really a reliable verification. */
|
||||
|
||||
hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"Removing STA due to IAPP ADD-notify");
|
||||
ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iapp_receive_udp - Process IAPP UDP frames
|
||||
* @sock: File descriptor for the socket
|
||||
* @eloop_ctx: IAPP data (struct iapp_data *)
|
||||
* @sock_ctx: Not used
|
||||
*/
|
||||
static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
{
|
||||
struct iapp_data *iapp = eloop_ctx;
|
||||
int len, hlen;
|
||||
unsigned char buf[128];
|
||||
struct sockaddr_in from;
|
||||
socklen_t fromlen;
|
||||
struct iapp_hdr *hdr;
|
||||
|
||||
/* Handle incoming IAPP frames (over UDP/IP) */
|
||||
|
||||
fromlen = sizeof(from);
|
||||
len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *) &from, &fromlen);
|
||||
if (len < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.sin_addr.s_addr == iapp->own.s_addr)
|
||||
return; /* ignore own IAPP messages */
|
||||
|
||||
hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"Received %d byte IAPP frame from %s%s\n",
|
||||
len, inet_ntoa(from.sin_addr),
|
||||
len < (int) sizeof(*hdr) ? " (too short)" : "");
|
||||
|
||||
if (len < (int) sizeof(*hdr))
|
||||
return;
|
||||
|
||||
hdr = (struct iapp_hdr *) buf;
|
||||
hlen = be_to_host16(hdr->length);
|
||||
hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"RX: version=%d command=%d id=%d len=%d\n",
|
||||
hdr->version, hdr->command,
|
||||
be_to_host16(hdr->identifier), hlen);
|
||||
if (hdr->version != IAPP_VERSION) {
|
||||
wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
|
||||
hdr->version);
|
||||
return;
|
||||
}
|
||||
if (hlen > len) {
|
||||
wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
|
||||
hlen, len);
|
||||
return;
|
||||
}
|
||||
if (hlen < len) {
|
||||
wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
|
||||
len - hlen);
|
||||
len = hlen;
|
||||
}
|
||||
|
||||
switch (hdr->command) {
|
||||
case IAPP_CMD_ADD_notify:
|
||||
iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
|
||||
break;
|
||||
case IAPP_CMD_MOVE_notify:
|
||||
/* TODO: MOVE is using TCP; so move this to TCP handler once it
|
||||
* is implemented.. */
|
||||
/* IAPP-MOVE.indication(MAC Address, New BSSID,
|
||||
* Sequence Number, AP Address, Context Block) */
|
||||
/* TODO: process */
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_ll addr;
|
||||
int ifindex;
|
||||
struct sockaddr_in *paddr, uaddr;
|
||||
struct iapp_data *iapp;
|
||||
struct ip_mreqn mreq;
|
||||
int reuseaddr = 1;
|
||||
|
||||
iapp = os_zalloc(sizeof(*iapp));
|
||||
if (iapp == NULL)
|
||||
return NULL;
|
||||
iapp->hapd = hapd;
|
||||
iapp->udp_sock = iapp->packet_sock = -1;
|
||||
|
||||
/* TODO:
|
||||
* open socket for sending and receiving IAPP frames over TCP
|
||||
*/
|
||||
|
||||
iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (iapp->udp_sock < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_memset(&ifr, 0, sizeof(ifr));
|
||||
os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
|
||||
if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
ifindex = ifr.ifr_ifindex;
|
||||
|
||||
if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
paddr = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
if (paddr->sin_family != AF_INET) {
|
||||
wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
|
||||
paddr->sin_family);
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
iapp->own.s_addr = paddr->sin_addr.s_addr;
|
||||
|
||||
if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
paddr = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
if (paddr->sin_family != AF_INET) {
|
||||
wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
|
||||
paddr->sin_family);
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
inet_aton(IAPP_MULTICAST, &iapp->multicast);
|
||||
|
||||
os_memset(&uaddr, 0, sizeof(uaddr));
|
||||
uaddr.sin_family = AF_INET;
|
||||
uaddr.sin_port = htons(IAPP_UDP_PORT);
|
||||
|
||||
if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
|
||||
sizeof(reuseaddr)) < 0) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s",
|
||||
strerror(errno));
|
||||
/*
|
||||
* Ignore this and try to continue. This is fine for single
|
||||
* BSS cases, but may fail if multiple BSSes enable IAPP.
|
||||
*/
|
||||
}
|
||||
|
||||
if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
|
||||
sizeof(uaddr)) < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_memset(&mreq, 0, sizeof(mreq));
|
||||
mreq.imr_multiaddr = iapp->multicast;
|
||||
mreq.imr_address.s_addr = INADDR_ANY;
|
||||
mreq.imr_ifindex = 0;
|
||||
if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
|
||||
sizeof(mreq)) < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (iapp->packet_sock < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_memset(&addr, 0, sizeof(addr));
|
||||
addr.sll_family = AF_PACKET;
|
||||
addr.sll_ifindex = ifindex;
|
||||
if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
|
||||
sizeof(addr)) < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
|
||||
strerror(errno));
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
|
||||
iapp, NULL)) {
|
||||
wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
|
||||
iapp_deinit(iapp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
|
||||
|
||||
/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
|
||||
* RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
|
||||
* be openned only after receiving Initiate-Accept. If Initiate-Reject
|
||||
* is received, IAPP is not started. */
|
||||
|
||||
return iapp;
|
||||
}
|
||||
|
||||
|
||||
void iapp_deinit(struct iapp_data *iapp)
|
||||
{
|
||||
struct ip_mreqn mreq;
|
||||
|
||||
if (iapp == NULL)
|
||||
return;
|
||||
|
||||
if (iapp->udp_sock >= 0) {
|
||||
os_memset(&mreq, 0, sizeof(mreq));
|
||||
mreq.imr_multiaddr = iapp->multicast;
|
||||
mreq.imr_address.s_addr = INADDR_ANY;
|
||||
mreq.imr_ifindex = 0;
|
||||
if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
|
||||
&mreq, sizeof(mreq)) < 0) {
|
||||
wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
eloop_unregister_read_sock(iapp->udp_sock);
|
||||
close(iapp->udp_sock);
|
||||
}
|
||||
if (iapp->packet_sock >= 0) {
|
||||
eloop_unregister_read_sock(iapp->packet_sock);
|
||||
close(iapp->packet_sock);
|
||||
}
|
||||
os_free(iapp);
|
||||
}
|
39
hostapd-2.9/src/ap/iapp.h
Normal file
39
hostapd-2.9/src/ap/iapp.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
|
||||
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef IAPP_H
|
||||
#define IAPP_H
|
||||
|
||||
struct iapp_data;
|
||||
|
||||
#ifdef CONFIG_IAPP
|
||||
|
||||
void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
|
||||
struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
|
||||
void iapp_deinit(struct iapp_data *iapp);
|
||||
|
||||
#else /* CONFIG_IAPP */
|
||||
|
||||
static inline void iapp_new_station(struct iapp_data *iapp,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
|
||||
const char *iface)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void iapp_deinit(struct iapp_data *iapp)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IAPP */
|
||||
|
||||
#endif /* IAPP_H */
|
5278
hostapd-2.9/src/ap/ieee802_11.c
Normal file
5278
hostapd-2.9/src/ap/ieee802_11.c
Normal file
File diff suppressed because it is too large
Load diff
196
hostapd-2.9/src/ap/ieee802_11.h
Normal file
196
hostapd-2.9/src/ap/ieee802_11.h
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11 Management
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef IEEE802_11_H
|
||||
#define IEEE802_11_H
|
||||
|
||||
struct hostapd_iface;
|
||||
struct hostapd_data;
|
||||
struct sta_info;
|
||||
struct hostapd_frame_info;
|
||||
struct ieee80211_ht_capabilities;
|
||||
struct ieee80211_vht_capabilities;
|
||||
struct ieee80211_mgmt;
|
||||
struct vlan_description;
|
||||
struct hostapd_sta_wpa_psk_short;
|
||||
enum ieee80211_op_mode;
|
||||
|
||||
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
||||
struct hostapd_frame_info *fi);
|
||||
void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
||||
u16 stype, int ok);
|
||||
void hostapd_2040_coex_action(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len);
|
||||
#ifdef NEED_AP_MLME
|
||||
int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
|
||||
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
char *buf, size_t buflen);
|
||||
#else /* NEED_AP_MLME */
|
||||
static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* NEED_AP_MLME */
|
||||
u16 hostapd_own_capab_info(struct hostapd_data *hapd);
|
||||
void ap_ht2040_timeout(void *eloop_data, void *user_data);
|
||||
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
|
||||
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
|
||||
enum ieee80211_op_mode opmode);
|
||||
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
|
||||
|
||||
int hostapd_ht_operation_update(struct hostapd_iface *iface);
|
||||
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *trans_id);
|
||||
void hostapd_get_ht_capab(struct hostapd_data *hapd,
|
||||
struct ieee80211_ht_capabilities *ht_cap,
|
||||
struct ieee80211_ht_capabilities *neg_ht_cap);
|
||||
void hostapd_get_vht_capab(struct hostapd_data *hapd,
|
||||
struct ieee80211_vht_capabilities *vht_cap,
|
||||
struct ieee80211_vht_capabilities *neg_vht_cap);
|
||||
void hostapd_get_he_capab(struct hostapd_data *hapd,
|
||||
const struct ieee80211_he_capabilities *he_cap,
|
||||
struct ieee80211_he_capabilities *neg_he_cap,
|
||||
size_t he_capab_len);
|
||||
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *ht_capab);
|
||||
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *ie, size_t len);
|
||||
|
||||
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
|
||||
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
|
||||
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_capab);
|
||||
u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_oper);
|
||||
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_opmode);
|
||||
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
enum ieee80211_op_mode opmode, const u8 *he_capab,
|
||||
size_t he_capab_len);
|
||||
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *buf, size_t len, int ack);
|
||||
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
|
||||
const u8 *data, size_t len, int ack);
|
||||
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
|
||||
int wds);
|
||||
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, u8 *eid);
|
||||
void ieee802_11_sa_query_action(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len);
|
||||
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
|
||||
int hostapd_update_time_adv(struct hostapd_data *hapd);
|
||||
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
|
||||
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
|
||||
|
||||
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
#ifdef CONFIG_SAE
|
||||
void sae_clear_retransmit_timer(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
#else /* CONFIG_SAE */
|
||||
static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SAE */
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
|
||||
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
|
||||
|
||||
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
|
||||
|
||||
u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
|
||||
size_t len, int delta);
|
||||
|
||||
#else /* CONFIG_MBO */
|
||||
|
||||
static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
|
||||
size_t len)
|
||||
{
|
||||
return eid;
|
||||
}
|
||||
|
||||
static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
|
||||
const u8 *supp_op_classes,
|
||||
size_t supp_op_classes_len);
|
||||
|
||||
u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
|
||||
void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int success,
|
||||
struct wpabuf *erp_resp,
|
||||
const u8 *msk, size_t msk_len);
|
||||
u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *owe_dh, u8 owe_dh_len,
|
||||
u8 *owe_buf, size_t owe_buf_len, u16 *reason);
|
||||
u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *rsn_ie, size_t rsn_ie_len,
|
||||
const u8 *owe_dh, size_t owe_dh_len);
|
||||
u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
|
||||
const u8 *rsn_ie, size_t rsn_ie_len,
|
||||
const u8 *owe_dh, size_t owe_dh_len);
|
||||
void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
|
||||
void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *pos, size_t len, u16 auth_alg,
|
||||
u16 auth_transaction, u16 status_code,
|
||||
void (*cb)(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
u16 resp, struct wpabuf *data, int pub));
|
||||
|
||||
size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
|
||||
u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
|
||||
int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *msg, size_t len, u32 *session_timeout,
|
||||
u32 *acct_interim_interval,
|
||||
struct vlan_description *vlan_id,
|
||||
struct hostapd_sta_wpa_psk_short **psk,
|
||||
char **identity, char **radius_cui,
|
||||
int is_probe_req);
|
||||
|
||||
int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
|
||||
int ap_seg1_idx, int *bandwidth, int *seg1_idx);
|
||||
|
||||
void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
|
||||
|
||||
#endif /* IEEE802_11_H */
|
701
hostapd-2.9/src/ap/ieee802_11_auth.c
Normal file
701
hostapd-2.9/src/ap/ieee802_11_auth.c
Normal file
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11 authentication (ACL)
|
||||
* Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*
|
||||
* Access control list for IEEE 802.11 authentication can uses statically
|
||||
* configured ACL from configuration files or an external RADIUS server.
|
||||
* Results from external RADIUS queries are cached to allow faster
|
||||
* authentication frame processing.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "radius/radius.h"
|
||||
#include "radius/radius_client.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "ieee802_1x.h"
|
||||
#include "ieee802_11_auth.h"
|
||||
|
||||
#define RADIUS_ACL_TIMEOUT 30
|
||||
|
||||
|
||||
struct hostapd_cached_radius_acl {
|
||||
struct os_reltime timestamp;
|
||||
macaddr addr;
|
||||
int accepted; /* HOSTAPD_ACL_* */
|
||||
struct hostapd_cached_radius_acl *next;
|
||||
u32 session_timeout;
|
||||
u32 acct_interim_interval;
|
||||
struct vlan_description vlan_id;
|
||||
struct hostapd_sta_wpa_psk_short *psk;
|
||||
char *identity;
|
||||
char *radius_cui;
|
||||
};
|
||||
|
||||
|
||||
struct hostapd_acl_query_data {
|
||||
struct os_reltime timestamp;
|
||||
u8 radius_id;
|
||||
macaddr addr;
|
||||
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
|
||||
size_t auth_msg_len;
|
||||
struct hostapd_acl_query_data *next;
|
||||
};
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
|
||||
{
|
||||
os_free(e->identity);
|
||||
os_free(e->radius_cui);
|
||||
hostapd_free_psk_list(e->psk);
|
||||
os_free(e);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
|
||||
{
|
||||
struct hostapd_cached_radius_acl *prev;
|
||||
|
||||
while (acl_cache) {
|
||||
prev = acl_cache;
|
||||
acl_cache = acl_cache->next;
|
||||
hostapd_acl_cache_free_entry(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
|
||||
struct hostapd_sta_wpa_psk_short *src)
|
||||
{
|
||||
if (!psk)
|
||||
return;
|
||||
|
||||
if (src)
|
||||
src->ref++;
|
||||
|
||||
*psk = src;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
|
||||
u32 *session_timeout,
|
||||
u32 *acct_interim_interval,
|
||||
struct vlan_description *vlan_id,
|
||||
struct hostapd_sta_wpa_psk_short **psk,
|
||||
char **identity, char **radius_cui)
|
||||
{
|
||||
struct hostapd_cached_radius_acl *entry;
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
for (entry = hapd->acl_cache; entry; entry = entry->next) {
|
||||
if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
|
||||
continue;
|
||||
|
||||
if (os_reltime_expired(&now, &entry->timestamp,
|
||||
RADIUS_ACL_TIMEOUT))
|
||||
return -1; /* entry has expired */
|
||||
if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
|
||||
if (session_timeout)
|
||||
*session_timeout = entry->session_timeout;
|
||||
if (acct_interim_interval)
|
||||
*acct_interim_interval =
|
||||
entry->acct_interim_interval;
|
||||
if (vlan_id)
|
||||
*vlan_id = entry->vlan_id;
|
||||
copy_psk_list(psk, entry->psk);
|
||||
if (identity) {
|
||||
if (entry->identity)
|
||||
*identity = os_strdup(entry->identity);
|
||||
else
|
||||
*identity = NULL;
|
||||
}
|
||||
if (radius_cui) {
|
||||
if (entry->radius_cui)
|
||||
*radius_cui = os_strdup(entry->radius_cui);
|
||||
else
|
||||
*radius_cui = NULL;
|
||||
}
|
||||
return entry->accepted;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
|
||||
static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
|
||||
{
|
||||
if (query == NULL)
|
||||
return;
|
||||
os_free(query->auth_msg);
|
||||
os_free(query);
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
|
||||
struct hostapd_acl_query_data *query)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
char buf[128];
|
||||
|
||||
query->radius_id = radius_client_get_id(hapd->radius);
|
||||
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
|
||||
if (msg == NULL)
|
||||
return -1;
|
||||
|
||||
if (radius_msg_make_authenticator(msg) < 0) {
|
||||
wpa_printf(MSG_INFO, "Could not make Request Authenticator");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
|
||||
os_strlen(buf))) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add User-Name");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!radius_msg_add_attr_user_password(
|
||||
msg, (u8 *) buf, os_strlen(buf),
|
||||
hapd->conf->radius->auth_server->shared_secret,
|
||||
hapd->conf->radius->auth_server->shared_secret_len)) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add User-Password");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
|
||||
NULL, msg) < 0)
|
||||
goto fail;
|
||||
|
||||
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
|
||||
MAC2STR(addr));
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
|
||||
(u8 *) buf, os_strlen(buf))) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
|
||||
(u8 *) buf, os_strlen(buf))) {
|
||||
wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
|
||||
goto fail;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
radius_msg_free(msg);
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_check_acl - Check a specified STA against accept/deny ACLs
|
||||
* @hapd: hostapd BSS data
|
||||
* @addr: MAC address of the STA
|
||||
* @vlan_id: Buffer for returning VLAN ID
|
||||
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
|
||||
*/
|
||||
int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
|
||||
struct vlan_description *vlan_id)
|
||||
{
|
||||
if (hostapd_maclist_found(hapd->conf->accept_mac,
|
||||
hapd->conf->num_accept_mac, addr, vlan_id))
|
||||
return HOSTAPD_ACL_ACCEPT;
|
||||
|
||||
if (hostapd_maclist_found(hapd->conf->deny_mac,
|
||||
hapd->conf->num_deny_mac, addr, vlan_id))
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
|
||||
if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
|
||||
return HOSTAPD_ACL_ACCEPT;
|
||||
if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
|
||||
return HOSTAPD_ACL_PENDING;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_allowed_address - Check whether a specified STA can be authenticated
|
||||
* @hapd: hostapd BSS data
|
||||
* @addr: MAC address of the STA
|
||||
* @msg: Authentication message
|
||||
* @len: Length of msg in octets
|
||||
* @session_timeout: Buffer for returning session timeout (from RADIUS)
|
||||
* @acct_interim_interval: Buffer for returning account interval (from RADIUS)
|
||||
* @vlan_id: Buffer for returning VLAN ID
|
||||
* @psk: Linked list buffer for returning WPA PSK
|
||||
* @identity: Buffer for returning identity (from RADIUS)
|
||||
* @radius_cui: Buffer for returning CUI (from RADIUS)
|
||||
* @is_probe_req: Whether this query for a Probe Request frame
|
||||
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
|
||||
*
|
||||
* The caller is responsible for freeing the returned *identity and *radius_cui
|
||||
* values with os_free().
|
||||
*/
|
||||
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *msg, size_t len, u32 *session_timeout,
|
||||
u32 *acct_interim_interval,
|
||||
struct vlan_description *vlan_id,
|
||||
struct hostapd_sta_wpa_psk_short **psk,
|
||||
char **identity, char **radius_cui,
|
||||
int is_probe_req)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (session_timeout)
|
||||
*session_timeout = 0;
|
||||
if (acct_interim_interval)
|
||||
*acct_interim_interval = 0;
|
||||
if (vlan_id)
|
||||
os_memset(vlan_id, 0, sizeof(*vlan_id));
|
||||
if (psk)
|
||||
*psk = NULL;
|
||||
if (identity)
|
||||
*identity = NULL;
|
||||
if (radius_cui)
|
||||
*radius_cui = NULL;
|
||||
|
||||
res = hostapd_check_acl(hapd, addr, vlan_id);
|
||||
if (res != HOSTAPD_ACL_PENDING)
|
||||
return res;
|
||||
|
||||
if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
|
||||
#ifdef CONFIG_NO_RADIUS
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
#else /* CONFIG_NO_RADIUS */
|
||||
struct hostapd_acl_query_data *query;
|
||||
|
||||
if (is_probe_req) {
|
||||
/* Skip RADIUS queries for Probe Request frames to avoid
|
||||
* excessive load on the authentication server. */
|
||||
return HOSTAPD_ACL_ACCEPT;
|
||||
};
|
||||
|
||||
if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
|
||||
vlan_id = NULL;
|
||||
|
||||
/* Check whether ACL cache has an entry for this station */
|
||||
res = hostapd_acl_cache_get(hapd, addr, session_timeout,
|
||||
acct_interim_interval, vlan_id, psk,
|
||||
identity, radius_cui);
|
||||
if (res == HOSTAPD_ACL_ACCEPT ||
|
||||
res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
|
||||
return res;
|
||||
if (res == HOSTAPD_ACL_REJECT)
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
|
||||
query = hapd->acl_queries;
|
||||
while (query) {
|
||||
if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
|
||||
/* pending query in RADIUS retransmit queue;
|
||||
* do not generate a new one */
|
||||
if (identity) {
|
||||
os_free(*identity);
|
||||
*identity = NULL;
|
||||
}
|
||||
if (radius_cui) {
|
||||
os_free(*radius_cui);
|
||||
*radius_cui = NULL;
|
||||
}
|
||||
return HOSTAPD_ACL_PENDING;
|
||||
}
|
||||
query = query->next;
|
||||
}
|
||||
|
||||
if (!hapd->conf->radius->auth_server)
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
|
||||
/* No entry in the cache - query external RADIUS server */
|
||||
query = os_zalloc(sizeof(*query));
|
||||
if (query == NULL) {
|
||||
wpa_printf(MSG_ERROR, "malloc for query data failed");
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
}
|
||||
os_get_reltime(&query->timestamp);
|
||||
os_memcpy(query->addr, addr, ETH_ALEN);
|
||||
if (hostapd_radius_acl_query(hapd, addr, query)) {
|
||||
wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
|
||||
"for ACL query.");
|
||||
hostapd_acl_query_free(query);
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
}
|
||||
|
||||
query->auth_msg = os_memdup(msg, len);
|
||||
if (query->auth_msg == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
|
||||
"auth frame.");
|
||||
hostapd_acl_query_free(query);
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
}
|
||||
query->auth_msg_len = len;
|
||||
query->next = hapd->acl_queries;
|
||||
hapd->acl_queries = query;
|
||||
|
||||
/* Queued data will be processed in hostapd_acl_recv_radius()
|
||||
* when RADIUS server replies to the sent Access-Request. */
|
||||
return HOSTAPD_ACL_PENDING;
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
}
|
||||
|
||||
return HOSTAPD_ACL_REJECT;
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
|
||||
struct os_reltime *now)
|
||||
{
|
||||
struct hostapd_cached_radius_acl *prev, *entry, *tmp;
|
||||
|
||||
prev = NULL;
|
||||
entry = hapd->acl_cache;
|
||||
|
||||
while (entry) {
|
||||
if (os_reltime_expired(now, &entry->timestamp,
|
||||
RADIUS_ACL_TIMEOUT)) {
|
||||
wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
|
||||
" has expired.", MAC2STR(entry->addr));
|
||||
if (prev)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
hapd->acl_cache = entry->next;
|
||||
hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
|
||||
tmp = entry;
|
||||
entry = entry->next;
|
||||
hostapd_acl_cache_free_entry(tmp);
|
||||
continue;
|
||||
}
|
||||
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
|
||||
struct os_reltime *now)
|
||||
{
|
||||
struct hostapd_acl_query_data *prev, *entry, *tmp;
|
||||
|
||||
prev = NULL;
|
||||
entry = hapd->acl_queries;
|
||||
|
||||
while (entry) {
|
||||
if (os_reltime_expired(now, &entry->timestamp,
|
||||
RADIUS_ACL_TIMEOUT)) {
|
||||
wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
|
||||
" has expired.", MAC2STR(entry->addr));
|
||||
if (prev)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
hapd->acl_queries = entry->next;
|
||||
|
||||
tmp = entry;
|
||||
entry = entry->next;
|
||||
hostapd_acl_query_free(tmp);
|
||||
continue;
|
||||
}
|
||||
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_acl_expire - ACL cache expiration callback
|
||||
* @hapd: struct hostapd_data *
|
||||
*/
|
||||
void hostapd_acl_expire(struct hostapd_data *hapd)
|
||||
{
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
hostapd_acl_expire_cache(hapd, &now);
|
||||
hostapd_acl_expire_queries(hapd, &now);
|
||||
}
|
||||
|
||||
|
||||
static void decode_tunnel_passwords(struct hostapd_data *hapd,
|
||||
const u8 *shared_secret,
|
||||
size_t shared_secret_len,
|
||||
struct radius_msg *msg,
|
||||
struct radius_msg *req,
|
||||
struct hostapd_cached_radius_acl *cache)
|
||||
{
|
||||
int passphraselen;
|
||||
char *passphrase;
|
||||
size_t i;
|
||||
struct hostapd_sta_wpa_psk_short *psk;
|
||||
|
||||
/*
|
||||
* Decode all tunnel passwords as PSK and save them into a linked list.
|
||||
*/
|
||||
for (i = 0; ; i++) {
|
||||
passphrase = radius_msg_get_tunnel_password(
|
||||
msg, &passphraselen, shared_secret, shared_secret_len,
|
||||
req, i);
|
||||
/*
|
||||
* Passphrase is NULL iff there is no i-th Tunnel-Password
|
||||
* attribute in msg.
|
||||
*/
|
||||
if (passphrase == NULL)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Passphase should be 8..63 chars (to be hashed with SSID)
|
||||
* or 64 chars hex string (no separate hashing with SSID).
|
||||
*/
|
||||
|
||||
if (passphraselen < MIN_PASSPHRASE_LEN ||
|
||||
passphraselen > MAX_PASSPHRASE_LEN + 1)
|
||||
goto free_pass;
|
||||
|
||||
/*
|
||||
* passphrase does not contain the NULL termination.
|
||||
* Add it here as pbkdf2_sha1() requires it.
|
||||
*/
|
||||
psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
|
||||
if (psk) {
|
||||
if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
|
||||
(hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
|
||||
hostapd_logger(hapd, cache->addr,
|
||||
HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_WARNING,
|
||||
"invalid hex string (%d chars) in Tunnel-Password",
|
||||
passphraselen);
|
||||
goto skip;
|
||||
} else if (passphraselen <= MAX_PASSPHRASE_LEN) {
|
||||
os_memcpy(psk->passphrase, passphrase,
|
||||
passphraselen);
|
||||
psk->is_passphrase = 1;
|
||||
}
|
||||
psk->next = cache->psk;
|
||||
cache->psk = psk;
|
||||
psk = NULL;
|
||||
}
|
||||
skip:
|
||||
os_free(psk);
|
||||
free_pass:
|
||||
os_free(passphrase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
|
||||
* @msg: RADIUS response message
|
||||
* @req: RADIUS request message
|
||||
* @shared_secret: RADIUS shared secret
|
||||
* @shared_secret_len: Length of shared_secret in octets
|
||||
* @data: Context data (struct hostapd_data *)
|
||||
* Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
|
||||
* was processed here) or RADIUS_RX_UNKNOWN if not.
|
||||
*/
|
||||
static RadiusRxResult
|
||||
hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
|
||||
const u8 *shared_secret, size_t shared_secret_len,
|
||||
void *data)
|
||||
{
|
||||
struct hostapd_data *hapd = data;
|
||||
struct hostapd_acl_query_data *query, *prev;
|
||||
struct hostapd_cached_radius_acl *cache;
|
||||
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
|
||||
|
||||
query = hapd->acl_queries;
|
||||
prev = NULL;
|
||||
while (query) {
|
||||
if (query->radius_id == hdr->identifier)
|
||||
break;
|
||||
prev = query;
|
||||
query = query->next;
|
||||
}
|
||||
if (query == NULL)
|
||||
return RADIUS_RX_UNKNOWN;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
|
||||
"message (id=%d)", query->radius_id);
|
||||
|
||||
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
|
||||
wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
|
||||
"correct authenticator - dropped\n");
|
||||
return RADIUS_RX_INVALID_AUTHENTICATOR;
|
||||
}
|
||||
|
||||
if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
|
||||
hdr->code != RADIUS_CODE_ACCESS_REJECT) {
|
||||
wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
|
||||
"query", hdr->code);
|
||||
return RADIUS_RX_UNKNOWN;
|
||||
}
|
||||
|
||||
/* Insert Accept/Reject info into ACL cache */
|
||||
cache = os_zalloc(sizeof(*cache));
|
||||
if (cache == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
|
||||
goto done;
|
||||
}
|
||||
os_get_reltime(&cache->timestamp);
|
||||
os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
|
||||
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
|
||||
u8 *buf;
|
||||
size_t len;
|
||||
|
||||
if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
|
||||
&cache->session_timeout) == 0)
|
||||
cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
|
||||
else
|
||||
cache->accepted = HOSTAPD_ACL_ACCEPT;
|
||||
|
||||
if (radius_msg_get_attr_int32(
|
||||
msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
|
||||
&cache->acct_interim_interval) == 0 &&
|
||||
cache->acct_interim_interval < 60) {
|
||||
wpa_printf(MSG_DEBUG, "Ignored too small "
|
||||
"Acct-Interim-Interval %d for STA " MACSTR,
|
||||
cache->acct_interim_interval,
|
||||
MAC2STR(query->addr));
|
||||
cache->acct_interim_interval = 0;
|
||||
}
|
||||
|
||||
if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
|
||||
cache->vlan_id.notempty = !!radius_msg_get_vlanid(
|
||||
msg, &cache->vlan_id.untagged,
|
||||
MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged);
|
||||
|
||||
decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
|
||||
msg, req, cache);
|
||||
|
||||
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
|
||||
&buf, &len, NULL) == 0) {
|
||||
cache->identity = os_zalloc(len + 1);
|
||||
if (cache->identity)
|
||||
os_memcpy(cache->identity, buf, len);
|
||||
}
|
||||
if (radius_msg_get_attr_ptr(
|
||||
msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||
&buf, &len, NULL) == 0) {
|
||||
cache->radius_cui = os_zalloc(len + 1);
|
||||
if (cache->radius_cui)
|
||||
os_memcpy(cache->radius_cui, buf, len);
|
||||
}
|
||||
|
||||
if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
|
||||
!cache->psk)
|
||||
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||
|
||||
if (cache->vlan_id.notempty &&
|
||||
!hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) {
|
||||
hostapd_logger(hapd, query->addr,
|
||||
HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Invalid VLAN %d%s received from RADIUS server",
|
||||
cache->vlan_id.untagged,
|
||||
cache->vlan_id.tagged[0] ? "+" : "");
|
||||
os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id));
|
||||
}
|
||||
if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
|
||||
!cache->vlan_id.notempty)
|
||||
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||
} else
|
||||
cache->accepted = HOSTAPD_ACL_REJECT;
|
||||
cache->next = hapd->acl_cache;
|
||||
hapd->acl_cache = cache;
|
||||
|
||||
#ifdef CONFIG_DRIVER_RADIUS_ACL
|
||||
hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
|
||||
cache->session_timeout);
|
||||
#else /* CONFIG_DRIVER_RADIUS_ACL */
|
||||
#ifdef NEED_AP_MLME
|
||||
/* Re-send original authentication frame for 802.11 processing */
|
||||
wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
|
||||
"successful RADIUS ACL query");
|
||||
ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
|
||||
#endif /* NEED_AP_MLME */
|
||||
#endif /* CONFIG_DRIVER_RADIUS_ACL */
|
||||
|
||||
done:
|
||||
if (prev == NULL)
|
||||
hapd->acl_queries = query->next;
|
||||
else
|
||||
prev->next = query->next;
|
||||
|
||||
hostapd_acl_query_free(query);
|
||||
|
||||
return RADIUS_RX_PROCESSED;
|
||||
}
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_acl_init: Initialize IEEE 802.11 ACL
|
||||
* @hapd: hostapd BSS data
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int hostapd_acl_init(struct hostapd_data *hapd)
|
||||
{
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
if (radius_client_register(hapd->radius, RADIUS_AUTH,
|
||||
hostapd_acl_recv_radius, hapd))
|
||||
return -1;
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
|
||||
* @hapd: hostapd BSS data
|
||||
*/
|
||||
void hostapd_acl_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
struct hostapd_acl_query_data *query, *prev;
|
||||
|
||||
#ifndef CONFIG_NO_RADIUS
|
||||
hostapd_acl_cache_free(hapd->acl_cache);
|
||||
hapd->acl_cache = NULL;
|
||||
#endif /* CONFIG_NO_RADIUS */
|
||||
|
||||
query = hapd->acl_queries;
|
||||
hapd->acl_queries = NULL;
|
||||
while (query) {
|
||||
prev = query;
|
||||
query = query->next;
|
||||
hostapd_acl_query_free(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
|
||||
{
|
||||
if (psk && psk->ref) {
|
||||
/* This will be freed when the last reference is dropped. */
|
||||
psk->ref--;
|
||||
return;
|
||||
}
|
||||
|
||||
while (psk) {
|
||||
struct hostapd_sta_wpa_psk_short *prev = psk;
|
||||
psk = psk->next;
|
||||
os_free(prev);
|
||||
}
|
||||
}
|
33
hostapd-2.9/src/ap/ieee802_11_auth.h
Normal file
33
hostapd-2.9/src/ap/ieee802_11_auth.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11 authentication (ACL)
|
||||
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef IEEE802_11_AUTH_H
|
||||
#define IEEE802_11_AUTH_H
|
||||
|
||||
enum {
|
||||
HOSTAPD_ACL_REJECT = 0,
|
||||
HOSTAPD_ACL_ACCEPT = 1,
|
||||
HOSTAPD_ACL_PENDING = 2,
|
||||
HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
|
||||
};
|
||||
|
||||
int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
|
||||
struct vlan_description *vlan_id);
|
||||
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *msg, size_t len, u32 *session_timeout,
|
||||
u32 *acct_interim_interval,
|
||||
struct vlan_description *vlan_id,
|
||||
struct hostapd_sta_wpa_psk_short **psk,
|
||||
char **identity, char **radius_cui,
|
||||
int is_probe_req);
|
||||
int hostapd_acl_init(struct hostapd_data *hapd);
|
||||
void hostapd_acl_deinit(struct hostapd_data *hapd);
|
||||
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
|
||||
void hostapd_acl_expire(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* IEEE802_11_AUTH_H */
|
348
hostapd-2.9/src/ap/ieee802_11_he.c
Normal file
348
hostapd-2.9/src/ap/ieee802_11_he.c
Normal file
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11ax HE
|
||||
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
|
||||
* Copyright (c) 2019 John Crispin <john@phrozen.org>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "beacon.h"
|
||||
#include "sta_info.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "dfs.h"
|
||||
|
||||
static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
|
||||
{
|
||||
u8 sz = 0, ru;
|
||||
|
||||
if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
|
||||
HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
|
||||
return 0;
|
||||
|
||||
ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
|
||||
HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
|
||||
while (ru) {
|
||||
if (ru & 0x1)
|
||||
sz++;
|
||||
ru >>= 1;
|
||||
}
|
||||
|
||||
sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
|
||||
sz = (sz * 6) + 7;
|
||||
if (sz % 8)
|
||||
sz += 8;
|
||||
sz /= 8;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
|
||||
enum ieee80211_op_mode opmode)
|
||||
{
|
||||
struct ieee80211_he_capabilities *cap;
|
||||
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
|
||||
u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
|
||||
u8 *pos = eid;
|
||||
u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
|
||||
|
||||
if (!mode)
|
||||
return eid;
|
||||
|
||||
ie_size = sizeof(struct ieee80211_he_capabilities);
|
||||
ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
|
||||
mode->he_capab[opmode].phy_cap);
|
||||
|
||||
switch (hapd->iface->conf->he_oper_chwidth) {
|
||||
case CHANWIDTH_80P80MHZ:
|
||||
he_oper_chwidth |=
|
||||
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
|
||||
mcs_nss_size += 4;
|
||||
/* fall through */
|
||||
case CHANWIDTH_160MHZ:
|
||||
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||
mcs_nss_size += 4;
|
||||
/* fall through */
|
||||
case CHANWIDTH_80MHZ:
|
||||
case CHANWIDTH_USE_HT:
|
||||
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
|
||||
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
|
||||
mcs_nss_size += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
ie_size += mcs_nss_size + ppet_size;
|
||||
|
||||
*pos++ = WLAN_EID_EXTENSION;
|
||||
*pos++ = 1 + ie_size;
|
||||
*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
|
||||
|
||||
cap = (struct ieee80211_he_capabilities *) pos;
|
||||
os_memset(cap, 0, sizeof(*cap));
|
||||
|
||||
os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
|
||||
HE_MAX_MAC_CAPAB_SIZE);
|
||||
os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
|
||||
HE_MAX_PHY_CAPAB_SIZE);
|
||||
os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
|
||||
if (ppet_size)
|
||||
os_memcpy(&cap->optional[mcs_nss_size],
|
||||
mode->he_capab[opmode].ppet, ppet_size);
|
||||
|
||||
if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
|
||||
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
|
||||
HE_PHYCAP_SU_BEAMFORMER_CAPAB;
|
||||
else
|
||||
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
|
||||
~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
|
||||
|
||||
if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
|
||||
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
|
||||
HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
|
||||
else
|
||||
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
|
||||
~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
|
||||
|
||||
if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
|
||||
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
|
||||
HE_PHYCAP_MU_BEAMFORMER_CAPAB;
|
||||
else
|
||||
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
|
||||
~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
|
||||
|
||||
cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
|
||||
he_oper_chwidth;
|
||||
|
||||
pos += ie_size;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct ieee80211_he_operation *oper;
|
||||
u8 *pos = eid;
|
||||
int oper_size = 6;
|
||||
u32 params = 0;
|
||||
|
||||
if (!hapd->iface->current_mode)
|
||||
return eid;
|
||||
|
||||
*pos++ = WLAN_EID_EXTENSION;
|
||||
*pos++ = 1 + oper_size;
|
||||
*pos++ = WLAN_EID_EXT_HE_OPERATION;
|
||||
|
||||
oper = (struct ieee80211_he_operation *) pos;
|
||||
os_memset(oper, 0, sizeof(*oper));
|
||||
|
||||
if (hapd->iface->conf->he_op.he_default_pe_duration)
|
||||
params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
|
||||
HE_OPERATION_DFLT_PE_DURATION_OFFSET);
|
||||
|
||||
if (hapd->iface->conf->he_op.he_twt_required)
|
||||
params |= HE_OPERATION_TWT_REQUIRED;
|
||||
|
||||
if (hapd->iface->conf->he_op.he_rts_threshold)
|
||||
params |= (hapd->iface->conf->he_op.he_rts_threshold <<
|
||||
HE_OPERATION_RTS_THRESHOLD_OFFSET);
|
||||
|
||||
if (hapd->iface->conf->he_op.he_bss_color)
|
||||
params |= (hapd->iface->conf->he_op.he_bss_color <<
|
||||
HE_OPERATION_BSS_COLOR_OFFSET);
|
||||
|
||||
/* HE minimum required basic MCS and NSS for STAs */
|
||||
oper->he_mcs_nss_set =
|
||||
host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
|
||||
|
||||
/* TODO: conditional MaxBSSID Indicator subfield */
|
||||
|
||||
oper->he_oper_params = host_to_le32(params);
|
||||
|
||||
pos += oper_size;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct ieee80211_he_mu_edca_parameter_set *edca;
|
||||
u8 *pos;
|
||||
size_t i;
|
||||
|
||||
pos = (u8 *) &hapd->iface->conf->he_mu_edca;
|
||||
for (i = 0; i < sizeof(*edca); i++) {
|
||||
if (pos[i])
|
||||
break;
|
||||
}
|
||||
if (i == sizeof(*edca))
|
||||
return eid; /* no MU EDCA Parameters configured */
|
||||
|
||||
pos = eid;
|
||||
*pos++ = WLAN_EID_EXTENSION;
|
||||
*pos++ = 1 + sizeof(*edca);
|
||||
*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
|
||||
|
||||
edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
|
||||
os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
|
||||
pos, sizeof(*edca));
|
||||
|
||||
pos += sizeof(*edca);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct ieee80211_spatial_reuse *spr;
|
||||
u8 *pos = eid, *spr_param;
|
||||
u8 sz = 1;
|
||||
|
||||
if (!hapd->iface->conf->spr.sr_control)
|
||||
return eid;
|
||||
|
||||
if (hapd->iface->conf->spr.sr_control &
|
||||
SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
|
||||
sz++;
|
||||
|
||||
if (hapd->iface->conf->spr.sr_control &
|
||||
SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
|
||||
sz += 18;
|
||||
|
||||
*pos++ = WLAN_EID_EXTENSION;
|
||||
*pos++ = 1 + sz;
|
||||
*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
|
||||
|
||||
spr = (struct ieee80211_spatial_reuse *) pos;
|
||||
os_memset(spr, 0, sizeof(*spr));
|
||||
|
||||
spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
|
||||
pos++;
|
||||
spr_param = spr->params;
|
||||
if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
|
||||
*spr_param++ =
|
||||
hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
|
||||
pos++;
|
||||
}
|
||||
if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
|
||||
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
|
||||
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
|
||||
pos += 18;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_get_he_capab(struct hostapd_data *hapd,
|
||||
const struct ieee80211_he_capabilities *he_cap,
|
||||
struct ieee80211_he_capabilities *neg_he_cap,
|
||||
size_t he_capab_len)
|
||||
{
|
||||
if (!he_cap)
|
||||
return;
|
||||
|
||||
if (he_capab_len > sizeof(*neg_he_cap))
|
||||
he_capab_len = sizeof(*neg_he_cap);
|
||||
/* TODO: mask out unsupported features */
|
||||
|
||||
os_memcpy(neg_he_cap, he_cap, he_capab_len);
|
||||
}
|
||||
|
||||
|
||||
static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
|
||||
enum ieee80211_op_mode opmode)
|
||||
{
|
||||
u16 sta_rx_mcs_set, ap_tx_mcs_set;
|
||||
u8 mcs_count = 0;
|
||||
const u16 *ap_mcs_set, *sta_mcs_set;
|
||||
int i;
|
||||
|
||||
if (!hapd->iface->current_mode)
|
||||
return 1;
|
||||
ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
|
||||
sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
|
||||
sta_he_capab)->optional;
|
||||
|
||||
/*
|
||||
* Disable HE capabilities for STAs for which there is not even a single
|
||||
* allowed MCS in any supported number of streams, i.e., STA is
|
||||
* advertising 3 (not supported) as HE MCS rates for all supported
|
||||
* band/stream cases.
|
||||
*/
|
||||
switch (hapd->iface->conf->he_oper_chwidth) {
|
||||
case CHANWIDTH_80P80MHZ:
|
||||
mcs_count = 3;
|
||||
break;
|
||||
case CHANWIDTH_160MHZ:
|
||||
mcs_count = 2;
|
||||
break;
|
||||
default:
|
||||
mcs_count = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < mcs_count; i++) {
|
||||
int j;
|
||||
|
||||
/* AP Tx MCS map vs. STA Rx MCS map */
|
||||
sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
|
||||
ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
|
||||
&ap_mcs_set[(i * 2) + 1]);
|
||||
|
||||
for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
|
||||
if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
|
||||
continue;
|
||||
|
||||
if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
|
||||
continue;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"No matching HE MCS found between AP TX and STA RX");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
enum ieee80211_op_mode opmode, const u8 *he_capab,
|
||||
size_t he_capab_len)
|
||||
{
|
||||
if (!he_capab || !hapd->iconf->ieee80211ax ||
|
||||
!check_valid_he_mcs(hapd, he_capab, opmode) ||
|
||||
he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
|
||||
sta->flags &= ~WLAN_STA_HE;
|
||||
os_free(sta->he_capab);
|
||||
sta->he_capab = NULL;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!sta->he_capab) {
|
||||
sta->he_capab =
|
||||
os_zalloc(sizeof(struct ieee80211_he_capabilities));
|
||||
if (!sta->he_capab)
|
||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
sta->flags |= WLAN_STA_HE;
|
||||
os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
|
||||
os_memcpy(sta->he_capab, he_capab, he_capab_len);
|
||||
sta->he_capab_len = he_capab_len;
|
||||
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
557
hostapd-2.9/src/ap/ieee802_11_ht.c
Normal file
557
hostapd-2.9/src/ap/ieee802_11_ht.c
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11n HT
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2007-2008, Intel Corporation
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "sta_info.h"
|
||||
#include "beacon.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "hw_features.h"
|
||||
#include "ap_drv_ops.h"
|
||||
|
||||
|
||||
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct ieee80211_ht_capabilities *cap;
|
||||
u8 *pos = eid;
|
||||
|
||||
if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
|
||||
hapd->conf->disable_11n)
|
||||
return eid;
|
||||
|
||||
*pos++ = WLAN_EID_HT_CAP;
|
||||
*pos++ = sizeof(*cap);
|
||||
|
||||
cap = (struct ieee80211_ht_capabilities *) pos;
|
||||
os_memset(cap, 0, sizeof(*cap));
|
||||
cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
|
||||
cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
|
||||
os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
|
||||
16);
|
||||
|
||||
/* TODO: ht_extended_capabilities (now fully disabled) */
|
||||
/* TODO: tx_bf_capability_info (now fully disabled) */
|
||||
/* TODO: asel_capabilities (now fully disabled) */
|
||||
|
||||
pos += sizeof(*cap);
|
||||
|
||||
if (hapd->iconf->obss_interval) {
|
||||
struct ieee80211_obss_scan_parameters *scan_params;
|
||||
|
||||
*pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
|
||||
*pos++ = sizeof(*scan_params);
|
||||
|
||||
scan_params = (struct ieee80211_obss_scan_parameters *) pos;
|
||||
os_memset(scan_params, 0, sizeof(*scan_params));
|
||||
scan_params->width_trigger_scan_interval =
|
||||
host_to_le16(hapd->iconf->obss_interval);
|
||||
|
||||
/* Fill in default values for remaining parameters
|
||||
* (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
|
||||
scan_params->scan_passive_dwell =
|
||||
host_to_le16(20);
|
||||
scan_params->scan_active_dwell =
|
||||
host_to_le16(10);
|
||||
scan_params->scan_passive_total_per_channel =
|
||||
host_to_le16(200);
|
||||
scan_params->scan_active_total_per_channel =
|
||||
host_to_le16(20);
|
||||
scan_params->channel_transition_delay_factor =
|
||||
host_to_le16(5);
|
||||
scan_params->scan_activity_threshold =
|
||||
host_to_le16(25);
|
||||
|
||||
pos += sizeof(*scan_params);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct ieee80211_ht_operation *oper;
|
||||
u8 *pos = eid;
|
||||
|
||||
if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
|
||||
return eid;
|
||||
|
||||
*pos++ = WLAN_EID_HT_OPERATION;
|
||||
*pos++ = sizeof(*oper);
|
||||
|
||||
oper = (struct ieee80211_ht_operation *) pos;
|
||||
os_memset(oper, 0, sizeof(*oper));
|
||||
|
||||
oper->primary_chan = hapd->iconf->channel;
|
||||
oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
|
||||
if (hapd->iconf->secondary_channel == 1)
|
||||
oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
|
||||
HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
|
||||
if (hapd->iconf->secondary_channel == -1)
|
||||
oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
|
||||
HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
|
||||
|
||||
pos += sizeof(*oper);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 sec_ch;
|
||||
|
||||
if (!hapd->cs_freq_params.channel ||
|
||||
!hapd->cs_freq_params.sec_channel_offset)
|
||||
return eid;
|
||||
|
||||
if (hapd->cs_freq_params.sec_channel_offset == -1)
|
||||
sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
|
||||
else if (hapd->cs_freq_params.sec_channel_offset == 1)
|
||||
sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
|
||||
else
|
||||
return eid;
|
||||
|
||||
*eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
|
||||
*eid++ = 1;
|
||||
*eid++ = sec_ch;
|
||||
|
||||
return eid;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
op_mode
|
||||
Set to 0 (HT pure) under the followign conditions
|
||||
- all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
|
||||
- all STAs in the BSS are 20 MHz HT in 20 MHz BSS
|
||||
Set to 1 (HT non-member protection) if there may be non-HT STAs
|
||||
in both the primary and the secondary channel
|
||||
Set to 2 if only HT STAs are associated in BSS,
|
||||
however and at least one 20 MHz HT STA is associated
|
||||
Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
|
||||
*/
|
||||
int hostapd_ht_operation_update(struct hostapd_iface *iface)
|
||||
{
|
||||
u16 cur_op_mode, new_op_mode;
|
||||
int op_mode_changes = 0;
|
||||
|
||||
if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
|
||||
return 0;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
|
||||
__func__, iface->ht_op_mode);
|
||||
|
||||
if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
|
||||
&& iface->num_sta_ht_no_gf) {
|
||||
iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
|
||||
op_mode_changes++;
|
||||
} else if ((iface->ht_op_mode &
|
||||
HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
|
||||
iface->num_sta_ht_no_gf == 0) {
|
||||
iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
|
||||
op_mode_changes++;
|
||||
}
|
||||
|
||||
if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
|
||||
(iface->num_sta_no_ht || iface->olbc_ht)) {
|
||||
iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
|
||||
op_mode_changes++;
|
||||
} else if ((iface->ht_op_mode &
|
||||
HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
|
||||
(iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
|
||||
iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
|
||||
op_mode_changes++;
|
||||
}
|
||||
|
||||
if (iface->num_sta_no_ht)
|
||||
new_op_mode = HT_PROT_NON_HT_MIXED;
|
||||
else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
|
||||
new_op_mode = HT_PROT_20MHZ_PROTECTION;
|
||||
else if (iface->olbc_ht)
|
||||
new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
|
||||
else
|
||||
new_op_mode = HT_PROT_NO_PROTECTION;
|
||||
|
||||
cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
|
||||
if (cur_op_mode != new_op_mode) {
|
||||
iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
|
||||
iface->ht_op_mode |= new_op_mode;
|
||||
op_mode_changes++;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
|
||||
__func__, iface->ht_op_mode, op_mode_changes);
|
||||
|
||||
return op_mode_changes;
|
||||
}
|
||||
|
||||
|
||||
static int is_40_allowed(struct hostapd_iface *iface, int channel)
|
||||
{
|
||||
int pri_freq, sec_freq;
|
||||
int affected_start, affected_end;
|
||||
int pri = 2407 + 5 * channel;
|
||||
|
||||
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
||||
return 1;
|
||||
|
||||
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
|
||||
|
||||
if (iface->conf->secondary_channel > 0)
|
||||
sec_freq = pri_freq + 20;
|
||||
else
|
||||
sec_freq = pri_freq - 20;
|
||||
|
||||
affected_start = (pri_freq + sec_freq) / 2 - 25;
|
||||
affected_end = (pri_freq + sec_freq) / 2 + 25;
|
||||
if ((pri < affected_start || pri > affected_end))
|
||||
return 1; /* not within affected channel range */
|
||||
|
||||
wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
|
||||
affected_start, affected_end);
|
||||
wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_2040_coex_action(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct hostapd_iface *iface = hapd->iface;
|
||||
struct ieee80211_2040_bss_coex_ie *bc_ie;
|
||||
struct ieee80211_2040_intol_chan_report *ic_report;
|
||||
int is_ht40_allowed = 1;
|
||||
int i;
|
||||
const u8 *start = (const u8 *) mgmt;
|
||||
const u8 *data = start + IEEE80211_HDRLEN + 2;
|
||||
struct sta_info *sta;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"HT: Received 20/40 BSS Coexistence Management frame from "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
|
||||
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
|
||||
mgmt->u.action.u.public_action.action);
|
||||
|
||||
if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Ignore too short 20/40 BSS Coexistence Management frame");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 20/40 BSS Coexistence element */
|
||||
bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
|
||||
if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
|
||||
bc_ie->length < 1) {
|
||||
wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
|
||||
bc_ie->element_id, bc_ie->length);
|
||||
return;
|
||||
}
|
||||
if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Truncated 20/40 BSS Coexistence element");
|
||||
return;
|
||||
}
|
||||
data += 2 + bc_ie->length;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
|
||||
bc_ie->coex_param,
|
||||
(bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
|
||||
(bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
|
||||
(bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
|
||||
(bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
|
||||
(bc_ie->coex_param & BIT(4)) ?
|
||||
"[OBSSScanExemptionGrant]" : "",
|
||||
(bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
|
||||
"[Reserved]" : "");
|
||||
|
||||
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
|
||||
/* Intra-BSS communication prohibiting 20/40 MHz BSS operation
|
||||
*/
|
||||
sta = ap_get_sta(hapd, mgmt->sa);
|
||||
if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
|
||||
return;
|
||||
}
|
||||
|
||||
hostapd_logger(hapd, mgmt->sa,
|
||||
HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"20 MHz BSS width request bit is set in BSS coexistence information field");
|
||||
is_ht40_allowed = 0;
|
||||
}
|
||||
|
||||
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
|
||||
/* Inter-BSS communication prohibiting 20/40 MHz BSS operation
|
||||
*/
|
||||
hostapd_logger(hapd, mgmt->sa,
|
||||
HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"40 MHz intolerant bit is set in BSS coexistence information field");
|
||||
is_ht40_allowed = 0;
|
||||
}
|
||||
|
||||
/* 20/40 BSS Intolerant Channel Report element (zero or more times) */
|
||||
while (start + len - data >= 3 &&
|
||||
data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
|
||||
u8 ielen = data[1];
|
||||
|
||||
if (ielen > start + len - data - 2) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Truncated 20/40 BSS Intolerant Channel Report element");
|
||||
return;
|
||||
}
|
||||
ic_report = (struct ieee80211_2040_intol_chan_report *) data;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"20/40 BSS Intolerant Channel Report: Operating Class %u",
|
||||
ic_report->op_class);
|
||||
|
||||
/* Go through the channel report to find any BSS there in the
|
||||
* affected channel range */
|
||||
for (i = 0; i < ielen - 1; i++) {
|
||||
u8 chan = ic_report->variable[i];
|
||||
|
||||
if (chan == iface->conf->channel)
|
||||
continue; /* matching own primary channel */
|
||||
if (is_40_allowed(iface, chan))
|
||||
continue; /* not within affected channels */
|
||||
hostapd_logger(hapd, mgmt->sa,
|
||||
HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"20_40_INTOLERANT channel %d reported",
|
||||
chan);
|
||||
is_ht40_allowed = 0;
|
||||
}
|
||||
|
||||
data += 2 + ielen;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
|
||||
is_ht40_allowed, iface->num_sta_ht40_intolerant);
|
||||
|
||||
if (!is_ht40_allowed &&
|
||||
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||
if (iface->conf->secondary_channel) {
|
||||
hostapd_logger(hapd, mgmt->sa,
|
||||
HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Switching to 20 MHz operation");
|
||||
iface->conf->secondary_channel = 0;
|
||||
ieee802_11_set_beacons(iface);
|
||||
}
|
||||
if (!iface->num_sta_ht40_intolerant &&
|
||||
iface->conf->obss_interval) {
|
||||
unsigned int delay_time;
|
||||
delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
|
||||
iface->conf->obss_interval;
|
||||
eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
|
||||
NULL);
|
||||
eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
|
||||
hapd->iface, NULL);
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Reschedule HT 20/40 timeout to occur in %u seconds",
|
||||
delay_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *ht_capab)
|
||||
{
|
||||
/*
|
||||
* Disable HT caps for STAs associated to no-HT BSSes, or for stations
|
||||
* that did not specify a valid WMM IE in the (Re)Association Request
|
||||
* frame.
|
||||
*/
|
||||
if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
|
||||
!hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
|
||||
sta->flags &= ~WLAN_STA_HT;
|
||||
os_free(sta->ht_capabilities);
|
||||
sta->ht_capabilities = NULL;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (sta->ht_capabilities == NULL) {
|
||||
sta->ht_capabilities =
|
||||
os_zalloc(sizeof(struct ieee80211_ht_capabilities));
|
||||
if (sta->ht_capabilities == NULL)
|
||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
sta->flags |= WLAN_STA_HT;
|
||||
os_memcpy(sta->ht_capabilities, ht_capab,
|
||||
sizeof(struct ieee80211_ht_capabilities));
|
||||
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
|
||||
{
|
||||
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
||||
return;
|
||||
|
||||
wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
|
||||
" in Association Request", MAC2STR(sta->addr));
|
||||
|
||||
if (sta->ht40_intolerant_set)
|
||||
return;
|
||||
|
||||
sta->ht40_intolerant_set = 1;
|
||||
iface->num_sta_ht40_intolerant++;
|
||||
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||
|
||||
if (iface->conf->secondary_channel &&
|
||||
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||
iface->conf->secondary_channel = 0;
|
||||
ieee802_11_set_beacons(iface);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
|
||||
{
|
||||
if (!sta->ht40_intolerant_set)
|
||||
return;
|
||||
|
||||
sta->ht40_intolerant_set = 0;
|
||||
iface->num_sta_ht40_intolerant--;
|
||||
|
||||
if (iface->num_sta_ht40_intolerant == 0 &&
|
||||
(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
|
||||
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||
unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
|
||||
iface->conf->obss_interval;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"HT: Start 20->40 MHz transition timer (%d seconds)",
|
||||
delay_time);
|
||||
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||
eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
|
||||
iface, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
u16 ht_capab;
|
||||
|
||||
ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
|
||||
wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
|
||||
"0x%04x", MAC2STR(sta->addr), ht_capab);
|
||||
if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
|
||||
if (!sta->no_ht_gf_set) {
|
||||
sta->no_ht_gf_set = 1;
|
||||
hapd->iface->num_sta_ht_no_gf++;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
|
||||
"of non-gf stations %d",
|
||||
__func__, MAC2STR(sta->addr),
|
||||
hapd->iface->num_sta_ht_no_gf);
|
||||
}
|
||||
if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
|
||||
if (!sta->ht_20mhz_set) {
|
||||
sta->ht_20mhz_set = 1;
|
||||
hapd->iface->num_sta_ht_20mhz++;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
|
||||
"20MHz HT STAs %d",
|
||||
__func__, MAC2STR(sta->addr),
|
||||
hapd->iface->num_sta_ht_20mhz);
|
||||
}
|
||||
|
||||
if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
|
||||
ht40_intolerant_add(hapd->iface, sta);
|
||||
}
|
||||
|
||||
|
||||
static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
if (!sta->no_ht_set) {
|
||||
sta->no_ht_set = 1;
|
||||
hapd->iface->num_sta_no_ht++;
|
||||
}
|
||||
if (hapd->iconf->ieee80211n) {
|
||||
wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
|
||||
"non-HT stations %d",
|
||||
__func__, MAC2STR(sta->addr),
|
||||
hapd->iface->num_sta_no_ht);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
|
||||
update_sta_ht(hapd, sta);
|
||||
else
|
||||
update_sta_no_ht(hapd, sta);
|
||||
|
||||
if (hostapd_ht_operation_update(hapd->iface) > 0)
|
||||
ieee802_11_set_beacons(hapd->iface);
|
||||
}
|
||||
|
||||
|
||||
void hostapd_get_ht_capab(struct hostapd_data *hapd,
|
||||
struct ieee80211_ht_capabilities *ht_cap,
|
||||
struct ieee80211_ht_capabilities *neg_ht_cap)
|
||||
{
|
||||
u16 cap;
|
||||
|
||||
if (ht_cap == NULL)
|
||||
return;
|
||||
os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
|
||||
cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
|
||||
|
||||
/*
|
||||
* Mask out HT features we don't support, but don't overwrite
|
||||
* non-symmetric features like STBC and SMPS. Just because
|
||||
* we're not in dynamic SMPS mode the STA might still be.
|
||||
*/
|
||||
cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
|
||||
HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
|
||||
|
||||
/*
|
||||
* STBC needs to be handled specially
|
||||
* if we don't support RX STBC, mask out TX STBC in the STA's HT caps
|
||||
* if we don't support TX STBC, mask out RX STBC in the STA's HT caps
|
||||
*/
|
||||
if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
|
||||
cap &= ~HT_CAP_INFO_TX_STBC;
|
||||
if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
|
||||
cap &= ~HT_CAP_INFO_RX_STBC_MASK;
|
||||
|
||||
neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
|
||||
}
|
||||
|
||||
|
||||
void ap_ht2040_timeout(void *eloop_data, void *user_data)
|
||||
{
|
||||
struct hostapd_iface *iface = eloop_data;
|
||||
|
||||
wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
|
||||
|
||||
iface->conf->secondary_channel = iface->secondary_ch;
|
||||
ieee802_11_set_beacons(iface);
|
||||
}
|
1002
hostapd-2.9/src/ap/ieee802_11_shared.c
Normal file
1002
hostapd-2.9/src/ap/ieee802_11_shared.c
Normal file
File diff suppressed because it is too large
Load diff
517
hostapd-2.9/src/ap/ieee802_11_vht.c
Normal file
517
hostapd-2.9/src/ap/ieee802_11_vht.c
Normal file
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.11ac VHT
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of BSD license
|
||||
*
|
||||
* See README and COPYING for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "sta_info.h"
|
||||
#include "beacon.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "dfs.h"
|
||||
|
||||
|
||||
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
|
||||
{
|
||||
struct ieee80211_vht_capabilities *cap;
|
||||
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
|
||||
u8 *pos = eid;
|
||||
|
||||
if (!mode)
|
||||
return eid;
|
||||
|
||||
if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
|
||||
mode->vht_capab == 0 && hapd->iface->hw_features) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||
if (hapd->iface->hw_features[i].mode ==
|
||||
HOSTAPD_MODE_IEEE80211A) {
|
||||
mode = &hapd->iface->hw_features[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*pos++ = WLAN_EID_VHT_CAP;
|
||||
*pos++ = sizeof(*cap);
|
||||
|
||||
cap = (struct ieee80211_vht_capabilities *) pos;
|
||||
os_memset(cap, 0, sizeof(*cap));
|
||||
cap->vht_capabilities_info = host_to_le32(
|
||||
hapd->iface->conf->vht_capab);
|
||||
|
||||
if (nsts != 0) {
|
||||
u32 hapd_nsts;
|
||||
|
||||
hapd_nsts = le_to_host32(cap->vht_capabilities_info);
|
||||
hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
|
||||
cap->vht_capabilities_info &=
|
||||
~(host_to_le32(hapd_nsts <<
|
||||
VHT_CAP_BEAMFORMEE_STS_OFFSET));
|
||||
cap->vht_capabilities_info |=
|
||||
host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
|
||||
}
|
||||
|
||||
/* Supported MCS set comes from hw */
|
||||
os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
|
||||
|
||||
pos += sizeof(*cap);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct ieee80211_vht_operation *oper;
|
||||
u8 *pos = eid;
|
||||
|
||||
*pos++ = WLAN_EID_VHT_OPERATION;
|
||||
*pos++ = sizeof(*oper);
|
||||
|
||||
oper = (struct ieee80211_vht_operation *) pos;
|
||||
os_memset(oper, 0, sizeof(*oper));
|
||||
|
||||
/*
|
||||
* center freq = 5 GHz + (5 * index)
|
||||
* So index 42 gives center freq 5.210 GHz
|
||||
* which is channel 42 in 5G band
|
||||
*/
|
||||
oper->vht_op_info_chan_center_freq_seg0_idx =
|
||||
hapd->iconf->vht_oper_centr_freq_seg0_idx;
|
||||
oper->vht_op_info_chan_center_freq_seg1_idx =
|
||||
hapd->iconf->vht_oper_centr_freq_seg1_idx;
|
||||
|
||||
oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
|
||||
if (hapd->iconf->vht_oper_chwidth == 2) {
|
||||
/*
|
||||
* Convert 160 MHz channel width to new style as interop
|
||||
* workaround.
|
||||
*/
|
||||
oper->vht_op_info_chwidth = 1;
|
||||
oper->vht_op_info_chan_center_freq_seg1_idx =
|
||||
oper->vht_op_info_chan_center_freq_seg0_idx;
|
||||
if (hapd->iconf->channel <
|
||||
hapd->iconf->vht_oper_centr_freq_seg0_idx)
|
||||
oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
|
||||
else
|
||||
oper->vht_op_info_chan_center_freq_seg0_idx += 8;
|
||||
} else if (hapd->iconf->vht_oper_chwidth == 3) {
|
||||
/*
|
||||
* Convert 80+80 MHz channel width to new style as interop
|
||||
* workaround.
|
||||
*/
|
||||
oper->vht_op_info_chwidth = 1;
|
||||
}
|
||||
|
||||
/* VHT Basic MCS set comes from hw */
|
||||
/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
|
||||
oper->vht_basic_mcs_set = host_to_le16(0xfffc);
|
||||
pos += sizeof(*oper);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
|
||||
const u8 *sta_vht_capab)
|
||||
{
|
||||
const struct ieee80211_vht_capabilities *vht_cap;
|
||||
struct ieee80211_vht_capabilities ap_vht_cap;
|
||||
u16 sta_rx_mcs_set, ap_tx_mcs_set;
|
||||
int i;
|
||||
|
||||
if (!mode)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Disable VHT caps for STAs for which there is not even a single
|
||||
* allowed MCS in any supported number of streams, i.e., STA is
|
||||
* advertising 3 (not supported) as VHT MCS rates for all supported
|
||||
* stream cases.
|
||||
*/
|
||||
os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
|
||||
sizeof(ap_vht_cap.vht_supported_mcs_set));
|
||||
vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
|
||||
|
||||
/* AP Tx MCS map vs. STA Rx MCS map */
|
||||
sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
|
||||
ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
|
||||
|
||||
for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
|
||||
if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
|
||||
continue;
|
||||
|
||||
if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
|
||||
continue;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"No matching VHT MCS found between AP TX and STA RX");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 bw, chan1, chan2 = 0;
|
||||
int freq1;
|
||||
|
||||
if (!hapd->cs_freq_params.channel ||
|
||||
!hapd->cs_freq_params.vht_enabled)
|
||||
return eid;
|
||||
|
||||
/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
|
||||
switch (hapd->cs_freq_params.bandwidth) {
|
||||
case 40:
|
||||
bw = 0;
|
||||
break;
|
||||
case 80:
|
||||
/* check if it's 80+80 */
|
||||
if (!hapd->cs_freq_params.center_freq2)
|
||||
bw = 1;
|
||||
else
|
||||
bw = 3;
|
||||
break;
|
||||
case 160:
|
||||
bw = 2;
|
||||
break;
|
||||
default:
|
||||
/* not valid VHT bandwidth or not in CSA */
|
||||
return eid;
|
||||
}
|
||||
|
||||
freq1 = hapd->cs_freq_params.center_freq1 ?
|
||||
hapd->cs_freq_params.center_freq1 :
|
||||
hapd->cs_freq_params.freq;
|
||||
if (ieee80211_freq_to_chan(freq1, &chan1) !=
|
||||
HOSTAPD_MODE_IEEE80211A)
|
||||
return eid;
|
||||
|
||||
if (hapd->cs_freq_params.center_freq2 &&
|
||||
ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
|
||||
&chan2) != HOSTAPD_MODE_IEEE80211A)
|
||||
return eid;
|
||||
|
||||
*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
|
||||
*eid++ = 5; /* Length of Channel Switch Wrapper */
|
||||
*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
|
||||
*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
|
||||
*eid++ = bw; /* New Channel Width */
|
||||
*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
|
||||
*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
|
||||
|
||||
return eid;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
struct hostapd_iface *iface = hapd->iface;
|
||||
struct hostapd_config *iconf = iface->conf;
|
||||
struct hostapd_hw_modes *mode = iface->current_mode;
|
||||
struct hostapd_channel_data *chan;
|
||||
int dfs, i;
|
||||
u8 channel, tx_pwr_count, local_pwr_constraint;
|
||||
int max_tx_power;
|
||||
u8 tx_pwr;
|
||||
|
||||
if (!mode)
|
||||
return eid;
|
||||
|
||||
if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
|
||||
return eid;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
if (mode->channels[i].freq == iface->freq)
|
||||
break;
|
||||
}
|
||||
if (i == mode->num_channels)
|
||||
return eid;
|
||||
|
||||
switch (iface->conf->vht_oper_chwidth) {
|
||||
case CHANWIDTH_USE_HT:
|
||||
if (iconf->secondary_channel == 0) {
|
||||
/* Max Transmit Power count = 0 (20 MHz) */
|
||||
tx_pwr_count = 0;
|
||||
} else {
|
||||
/* Max Transmit Power count = 1 (20, 40 MHz) */
|
||||
tx_pwr_count = 1;
|
||||
}
|
||||
break;
|
||||
case CHANWIDTH_80MHZ:
|
||||
/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
|
||||
tx_pwr_count = 2;
|
||||
break;
|
||||
case CHANWIDTH_80P80MHZ:
|
||||
case CHANWIDTH_160MHZ:
|
||||
/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
|
||||
tx_pwr_count = 3;
|
||||
break;
|
||||
default:
|
||||
return eid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below local_pwr_constraint logic is referred from
|
||||
* hostapd_eid_pwr_constraint.
|
||||
*
|
||||
* Check if DFS is required by regulatory.
|
||||
*/
|
||||
dfs = hostapd_is_dfs_required(hapd->iface);
|
||||
if (dfs < 0)
|
||||
dfs = 0;
|
||||
|
||||
/*
|
||||
* In order to meet regulations when TPC is not implemented using
|
||||
* a transmit power that is below the legal maximum (including any
|
||||
* mitigation factor) should help. In this case, indicate 3 dB below
|
||||
* maximum allowed transmit power.
|
||||
*/
|
||||
if (hapd->iconf->local_pwr_constraint == -1)
|
||||
local_pwr_constraint = (dfs == 0) ? 0 : 3;
|
||||
else
|
||||
local_pwr_constraint = hapd->iconf->local_pwr_constraint;
|
||||
|
||||
/*
|
||||
* A STA that is not an AP shall use a transmit power less than or
|
||||
* equal to the local maximum transmit power level for the channel.
|
||||
* The local maximum transmit power can be calculated from the formula:
|
||||
* local max TX pwr = max TX pwr - local pwr constraint
|
||||
* Where max TX pwr is maximum transmit power level specified for
|
||||
* channel in Country element and local pwr constraint is specified
|
||||
* for channel in this Power Constraint element.
|
||||
*/
|
||||
chan = &mode->channels[i];
|
||||
max_tx_power = chan->max_tx_power - local_pwr_constraint;
|
||||
|
||||
/*
|
||||
* Local Maximum Transmit power is encoded as two's complement
|
||||
* with a 0.5 dB step.
|
||||
*/
|
||||
max_tx_power *= 2; /* in 0.5 dB steps */
|
||||
if (max_tx_power > 127) {
|
||||
/* 63.5 has special meaning of 63.5 dBm or higher */
|
||||
max_tx_power = 127;
|
||||
}
|
||||
if (max_tx_power < -128)
|
||||
max_tx_power = -128;
|
||||
if (max_tx_power < 0)
|
||||
tx_pwr = 0x80 + max_tx_power + 128;
|
||||
else
|
||||
tx_pwr = max_tx_power;
|
||||
|
||||
*eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE;
|
||||
*eid++ = 2 + tx_pwr_count;
|
||||
|
||||
/*
|
||||
* Max Transmit Power count and
|
||||
* Max Transmit Power units = 0 (EIRP)
|
||||
*/
|
||||
*eid++ = tx_pwr_count;
|
||||
|
||||
for (i = 0; i <= tx_pwr_count; i++)
|
||||
*eid++ = tx_pwr;
|
||||
|
||||
return eid;
|
||||
}
|
||||
|
||||
|
||||
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_capab)
|
||||
{
|
||||
/* Disable VHT caps for STAs associated to no-VHT BSSes. */
|
||||
if (!vht_capab ||
|
||||
!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
|
||||
!check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
|
||||
sta->flags &= ~WLAN_STA_VHT;
|
||||
os_free(sta->vht_capabilities);
|
||||
sta->vht_capabilities = NULL;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (sta->vht_capabilities == NULL) {
|
||||
sta->vht_capabilities =
|
||||
os_zalloc(sizeof(struct ieee80211_vht_capabilities));
|
||||
if (sta->vht_capabilities == NULL)
|
||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
sta->flags |= WLAN_STA_VHT;
|
||||
os_memcpy(sta->vht_capabilities, vht_capab,
|
||||
sizeof(struct ieee80211_vht_capabilities));
|
||||
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_oper)
|
||||
{
|
||||
if (!vht_oper) {
|
||||
os_free(sta->vht_operation);
|
||||
sta->vht_operation = NULL;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!sta->vht_operation) {
|
||||
sta->vht_operation =
|
||||
os_zalloc(sizeof(struct ieee80211_vht_operation));
|
||||
if (!sta->vht_operation)
|
||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
os_memcpy(sta->vht_operation, vht_oper,
|
||||
sizeof(struct ieee80211_vht_operation));
|
||||
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *ie, size_t len)
|
||||
{
|
||||
const u8 *vht_capab;
|
||||
unsigned int vht_capab_len;
|
||||
|
||||
if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
|
||||
hapd->conf->disable_11ac)
|
||||
goto no_capab;
|
||||
|
||||
/* The VHT Capabilities element embedded in vendor VHT */
|
||||
vht_capab = ie + 5;
|
||||
if (vht_capab[0] != WLAN_EID_VHT_CAP)
|
||||
goto no_capab;
|
||||
vht_capab_len = vht_capab[1];
|
||||
if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
|
||||
(int) vht_capab_len > ie + len - vht_capab - 2)
|
||||
goto no_capab;
|
||||
vht_capab += 2;
|
||||
|
||||
if (sta->vht_capabilities == NULL) {
|
||||
sta->vht_capabilities =
|
||||
os_zalloc(sizeof(struct ieee80211_vht_capabilities));
|
||||
if (sta->vht_capabilities == NULL)
|
||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
|
||||
os_memcpy(sta->vht_capabilities, vht_capab,
|
||||
sizeof(struct ieee80211_vht_capabilities));
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
|
||||
no_capab:
|
||||
sta->flags &= ~WLAN_STA_VENDOR_VHT;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 *pos = eid;
|
||||
|
||||
if (!hapd->iface->current_mode)
|
||||
return eid;
|
||||
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = (5 + /* The Vendor OUI, type and subtype */
|
||||
2 + sizeof(struct ieee80211_vht_capabilities) +
|
||||
2 + sizeof(struct ieee80211_vht_operation));
|
||||
|
||||
WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
|
||||
pos += 4;
|
||||
*pos++ = VENDOR_VHT_SUBTYPE;
|
||||
pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
|
||||
pos = hostapd_eid_vht_operation(hapd, pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_oper_notif)
|
||||
{
|
||||
if (!vht_oper_notif) {
|
||||
sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
|
||||
sta->vht_opmode = *vht_oper_notif;
|
||||
return WLAN_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_get_vht_capab(struct hostapd_data *hapd,
|
||||
struct ieee80211_vht_capabilities *vht_cap,
|
||||
struct ieee80211_vht_capabilities *neg_vht_cap)
|
||||
{
|
||||
u32 cap, own_cap, sym_caps;
|
||||
|
||||
if (vht_cap == NULL)
|
||||
return;
|
||||
os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
|
||||
|
||||
cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
|
||||
own_cap = hapd->iconf->vht_capab;
|
||||
|
||||
/* mask out symmetric VHT capabilities we don't support */
|
||||
sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
|
||||
cap &= ~sym_caps | (own_cap & sym_caps);
|
||||
|
||||
/* mask out beamformer/beamformee caps if not supported */
|
||||
if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
|
||||
cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||
VHT_CAP_BEAMFORMEE_STS_MAX);
|
||||
|
||||
if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
|
||||
cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
|
||||
VHT_CAP_SOUNDING_DIMENSION_MAX);
|
||||
|
||||
if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
|
||||
cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
||||
|
||||
if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
|
||||
cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
||||
|
||||
/* mask channel widths we don't support */
|
||||
switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
|
||||
case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
|
||||
break;
|
||||
case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
|
||||
if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
|
||||
cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
|
||||
cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
|
||||
cap &= ~VHT_CAP_SHORT_GI_160;
|
||||
|
||||
/*
|
||||
* if we don't support RX STBC, mask out TX STBC in the STA's HT caps
|
||||
* if we don't support TX STBC, mask out RX STBC in the STA's HT caps
|
||||
*/
|
||||
if (!(own_cap & VHT_CAP_RXSTBC_MASK))
|
||||
cap &= ~VHT_CAP_TXSTBC;
|
||||
if (!(own_cap & VHT_CAP_TXSTBC))
|
||||
cap &= ~VHT_CAP_RXSTBC_MASK;
|
||||
|
||||
neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
|
||||
}
|
2997
hostapd-2.9/src/ap/ieee802_1x.c
Normal file
2997
hostapd-2.9/src/ap/ieee802_1x.c
Normal file
File diff suppressed because it is too large
Load diff
70
hostapd-2.9/src/ap/ieee802_1x.h
Normal file
70
hostapd-2.9/src/ap/ieee802_1x.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* hostapd / IEEE 802.1X-2004 Authenticator
|
||||
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef IEEE802_1X_H
|
||||
#define IEEE802_1X_H
|
||||
|
||||
struct hostapd_data;
|
||||
struct sta_info;
|
||||
struct eapol_state_machine;
|
||||
struct hostapd_config;
|
||||
struct hostapd_bss_config;
|
||||
struct hostapd_radius_attr;
|
||||
struct radius_msg;
|
||||
|
||||
|
||||
void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
|
||||
size_t len);
|
||||
void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized);
|
||||
void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
|
||||
int ieee802_1x_init(struct hostapd_data *hapd);
|
||||
void ieee802_1x_erp_flush(struct hostapd_data *hapd);
|
||||
void ieee802_1x_deinit(struct hostapd_data *hapd);
|
||||
int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *buf, size_t len, int ack);
|
||||
int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *data, int len, int ack);
|
||||
u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
|
||||
u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
|
||||
int idx);
|
||||
struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
|
||||
const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
|
||||
const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
|
||||
size_t *len);
|
||||
void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
|
||||
int enabled);
|
||||
void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
|
||||
int valid);
|
||||
void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
|
||||
int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
|
||||
int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
char *buf, size_t buflen);
|
||||
void hostapd_get_ntp_timestamp(u8 *buf);
|
||||
char *eap_type_text(u8 type);
|
||||
|
||||
const char *radius_mode_txt(struct hostapd_data *hapd);
|
||||
int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
int add_common_radius_attr(struct hostapd_data *hapd,
|
||||
struct hostapd_radius_attr *req_attr,
|
||||
struct sta_info *sta,
|
||||
struct radius_msg *msg);
|
||||
int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct radius_msg *msg, int acct);
|
||||
void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *eap, size_t len);
|
||||
struct eapol_state_machine *
|
||||
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
#endif /* IEEE802_1X_H */
|
244
hostapd-2.9/src/ap/mbo_ap.c
Normal file
244
hostapd-2.9/src/ap/mbo_ap.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* hostapd - MBO
|
||||
* Copyright (c) 2016, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "mbo_ap.h"
|
||||
|
||||
|
||||
void mbo_ap_sta_free(struct sta_info *sta)
|
||||
{
|
||||
struct mbo_non_pref_chan_info *info, *prev;
|
||||
|
||||
info = sta->non_pref_chan;
|
||||
sta->non_pref_chan = NULL;
|
||||
while (info) {
|
||||
prev = info;
|
||||
info = info->next;
|
||||
os_free(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct mbo_non_pref_chan_info *info, *tmp;
|
||||
char channels[200], *pos, *end;
|
||||
size_t num_chan, i;
|
||||
int ret;
|
||||
|
||||
if (len <= 3)
|
||||
return; /* Not enough room for any channels */
|
||||
|
||||
num_chan = len - 3;
|
||||
info = os_zalloc(sizeof(*info) + num_chan);
|
||||
if (!info)
|
||||
return;
|
||||
info->op_class = buf[0];
|
||||
info->pref = buf[len - 2];
|
||||
info->reason_code = buf[len - 1];
|
||||
info->num_channels = num_chan;
|
||||
buf++;
|
||||
os_memcpy(info->channels, buf, num_chan);
|
||||
if (!sta->non_pref_chan) {
|
||||
sta->non_pref_chan = info;
|
||||
} else {
|
||||
tmp = sta->non_pref_chan;
|
||||
while (tmp->next)
|
||||
tmp = tmp->next;
|
||||
tmp->next = info;
|
||||
}
|
||||
|
||||
pos = channels;
|
||||
end = pos + sizeof(channels);
|
||||
*pos = '\0';
|
||||
for (i = 0; i < num_chan; i++) {
|
||||
ret = os_snprintf(pos, end - pos, "%s%u",
|
||||
i == 0 ? "" : " ", buf[i]);
|
||||
if (os_snprintf_error(end - pos, ret)) {
|
||||
*pos = '\0';
|
||||
break;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
|
||||
" non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
|
||||
MAC2STR(sta->addr), info->op_class, info->pref,
|
||||
info->reason_code, channels);
|
||||
}
|
||||
|
||||
|
||||
void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
const u8 *pos, *attr, *end;
|
||||
size_t len;
|
||||
|
||||
if (!hapd->conf->mbo_enabled || !elems->mbo)
|
||||
return;
|
||||
|
||||
pos = elems->mbo + 4;
|
||||
len = elems->mbo_len - 4;
|
||||
wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
|
||||
|
||||
attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
|
||||
if (attr && attr[1] >= 1)
|
||||
sta->cell_capa = attr[2];
|
||||
|
||||
mbo_ap_sta_free(sta);
|
||||
end = pos + len;
|
||||
while (end - pos > 1) {
|
||||
u8 ie_len = pos[1];
|
||||
|
||||
if (2 + ie_len > end - pos)
|
||||
break;
|
||||
|
||||
if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
|
||||
mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
|
||||
{
|
||||
char *pos = buf, *end = buf + buflen;
|
||||
int ret;
|
||||
struct mbo_non_pref_chan_info *info;
|
||||
u8 i;
|
||||
unsigned int count = 0;
|
||||
|
||||
if (!sta->cell_capa)
|
||||
return 0;
|
||||
|
||||
ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return pos - buf;
|
||||
pos += ret;
|
||||
|
||||
for (info = sta->non_pref_chan; info; info = info->next) {
|
||||
char *pos2 = pos;
|
||||
|
||||
ret = os_snprintf(pos2, end - pos2,
|
||||
"non_pref_chan[%u]=%u:%u:%u:",
|
||||
count, info->op_class, info->pref,
|
||||
info->reason_code);
|
||||
count++;
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
|
||||
for (i = 0; i < info->num_channels; i++) {
|
||||
ret = os_snprintf(pos2, end - pos2, "%u%s",
|
||||
info->channels[i],
|
||||
i + 1 < info->num_channels ?
|
||||
"," : "");
|
||||
if (os_snprintf_error(end - pos2, ret)) {
|
||||
pos2 = NULL;
|
||||
break;
|
||||
}
|
||||
pos2 += ret;
|
||||
}
|
||||
|
||||
if (!pos2)
|
||||
break;
|
||||
ret = os_snprintf(pos2, end - pos2, "\n");
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
pos = pos2;
|
||||
}
|
||||
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
if (len < 1)
|
||||
return;
|
||||
wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
|
||||
" updated cellular data capability: %u",
|
||||
MAC2STR(sta->addr), buf[0]);
|
||||
sta->cell_capa = buf[0];
|
||||
}
|
||||
|
||||
|
||||
static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
|
||||
const u8 *buf, size_t len,
|
||||
int *first_non_pref_chan)
|
||||
{
|
||||
switch (type) {
|
||||
case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
|
||||
if (*first_non_pref_chan) {
|
||||
/*
|
||||
* Need to free the previously stored entries now to
|
||||
* allow the update to replace all entries.
|
||||
*/
|
||||
*first_non_pref_chan = 0;
|
||||
mbo_ap_sta_free(sta);
|
||||
}
|
||||
mbo_ap_parse_non_pref_chan(sta, buf, len);
|
||||
break;
|
||||
case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
|
||||
mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MBO: Ignore unknown WNM Notification WFA subelement %u",
|
||||
type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u8 ie_len;
|
||||
struct sta_info *sta;
|
||||
int first_non_pref_chan = 1;
|
||||
|
||||
if (!hapd->conf->mbo_enabled)
|
||||
return;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
pos = buf;
|
||||
end = buf + len;
|
||||
|
||||
while (end - pos > 1) {
|
||||
ie_len = pos[1];
|
||||
|
||||
if (2 + ie_len > end - pos)
|
||||
break;
|
||||
|
||||
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
|
||||
ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
|
||||
mbo_ap_wnm_notif_req_elem(sta, pos[5],
|
||||
pos + 6, ie_len - 4,
|
||||
&first_non_pref_chan);
|
||||
else
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MBO: Ignore unknown WNM Notification element %u (len=%u)",
|
||||
pos[0], pos[1]);
|
||||
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
}
|
51
hostapd-2.9/src/ap/mbo_ap.h
Normal file
51
hostapd-2.9/src/ap/mbo_ap.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* MBO related functions and structures
|
||||
* Copyright (c) 2016, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef MBO_AP_H
|
||||
#define MBO_AP_H
|
||||
|
||||
struct hostapd_data;
|
||||
struct sta_info;
|
||||
struct ieee802_11_elems;
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
|
||||
void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems);
|
||||
int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
|
||||
void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
|
||||
const u8 *buf, size_t len);
|
||||
void mbo_ap_sta_free(struct sta_info *sta);
|
||||
|
||||
#else /* CONFIG_MBO */
|
||||
|
||||
static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
|
||||
const u8 *addr,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mbo_ap_sta_free(struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
#endif /* MBO_AP_H */
|
186
hostapd-2.9/src/ap/ndisc_snoop.c
Normal file
186
hostapd-2.9/src/ap/ndisc_snoop.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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);
|
||||
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;
|
||||
case ROUTER_ADVERTISEMENT:
|
||||
if (hapd->conf->disable_dgaf)
|
||||
ucast_to_stas(hapd, buf, len);
|
||||
break;
|
||||
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
hostapd-2.9/src/ap/ndisc_snoop.h
Normal file
36
hostapd-2.9/src/ap/ndisc_snoop.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Neighbor Discovery snooping for Proxy ARP
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef NDISC_SNOOP_H
|
||||
#define NDISC_SNOOP_H
|
||||
|
||||
#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
|
||||
|
||||
int ndisc_snoop_init(struct hostapd_data *hapd);
|
||||
void ndisc_snoop_deinit(struct hostapd_data *hapd);
|
||||
void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
|
||||
|
||||
static inline int ndisc_snoop_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void sta_ip6addr_del(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
|
||||
|
||||
#endif /* NDISC_SNOOP_H */
|
262
hostapd-2.9/src/ap/neighbor_db.c
Normal file
262
hostapd-2.9/src/ap/neighbor_db.c
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* 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 "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 (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
|
||||
(!ssid ||
|
||||
(ssid->ssid_len == nr->ssid.ssid_len &&
|
||||
os_memcmp(ssid->ssid, nr->ssid.ssid,
|
||||
ssid->ssid_len) == 0)))
|
||||
return nr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
nr->stationary = 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)
|
||||
{
|
||||
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->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;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
hostapd_neighbor_remove(hapd, bssid, ssid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
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_clear_entry(nr);
|
||||
dl_list_del(&nr->list);
|
||||
os_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_clear_entry(nr);
|
||||
dl_list_del(&nr->list);
|
||||
os_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)
|
||||
{
|
||||
u8 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 == CHANWIDTH_USE_HT)
|
||||
return NR_CHAN_WIDTH_40;
|
||||
if (oper_chwidth == CHANWIDTH_80MHZ)
|
||||
return NR_CHAN_WIDTH_80;
|
||||
if (oper_chwidth == CHANWIDTH_160MHZ)
|
||||
return NR_CHAN_WIDTH_160;
|
||||
if (oper_chwidth == CHANWIDTH_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;
|
||||
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;
|
||||
}
|
||||
|
||||
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
|
||||
|
||||
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
|
||||
hapd->iconf->secondary_channel,
|
||||
hostapd_get_oper_chwidth(hapd->iconf),
|
||||
&op_class, &channel) ==
|
||||
NUM_HOSTAPD_MODES)
|
||||
return;
|
||||
width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
|
||||
if (vht) {
|
||||
center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
|
||||
hapd->iconf);
|
||||
if (width == NR_CHAN_WIDTH_80P80)
|
||||
center_freq2_idx =
|
||||
hostapd_get_oper_centr_freq_seg1_idx(
|
||||
hapd->iconf);
|
||||
} else if (ht) {
|
||||
ieee80211_freq_to_chan(hapd->iface->freq +
|
||||
10 * hapd->iconf->secondary_channel,
|
||||
¢er_freq1_idx);
|
||||
}
|
||||
|
||||
ssid.ssid_len = hapd->conf->ssid.ssid_len;
|
||||
os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
|
||||
|
||||
/*
|
||||
* Neighbor Report element size = BSSID + BSSID info + op_class + chan +
|
||||
* phy type + wide bandwidth channel subelement.
|
||||
*/
|
||||
nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
|
||||
if (!nr)
|
||||
return;
|
||||
|
||||
wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
|
||||
wpabuf_put_le32(nr, bssid_info);
|
||||
wpabuf_put_u8(nr, op_class);
|
||||
wpabuf_put_u8(nr, channel);
|
||||
wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
|
||||
|
||||
/*
|
||||
* Wide Bandwidth Channel subelement may be needed to allow the
|
||||
* receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
|
||||
* Figure 9-301.
|
||||
*/
|
||||
wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
|
||||
wpabuf_put_u8(nr, 3);
|
||||
wpabuf_put_u8(nr, width);
|
||||
wpabuf_put_u8(nr, center_freq1_idx);
|
||||
wpabuf_put_u8(nr, center_freq2_idx);
|
||||
|
||||
hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
|
||||
hapd->iconf->civic, hapd->iconf->stationary_ap);
|
||||
|
||||
wpabuf_free(nr);
|
||||
#endif /* NEED_AP_MLME */
|
||||
}
|
25
hostapd-2.9/src/ap/neighbor_db.h
Normal file
25
hostapd-2.9/src/ap/neighbor_db.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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_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);
|
||||
void hostapd_neighbor_set_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
hostapd-2.9/src/ap/p2p_hostapd.c
Normal file
113
hostapd-2.9/src/ap/p2p_hostapd.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* hostapd / P2P integration
|
||||
* Copyright (c) 2009-2010, Atheros Communications
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "p2p/p2p.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "sta_info.h"
|
||||
#include "p2p_hostapd.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
|
||||
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
if (sta->p2p_ie == NULL)
|
||||
return 0;
|
||||
|
||||
return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
|
||||
"duration=%d", count, start, duration);
|
||||
|
||||
if (count == 0) {
|
||||
hapd->noa_enabled = 0;
|
||||
hapd->noa_start = 0;
|
||||
hapd->noa_duration = 0;
|
||||
}
|
||||
|
||||
if (count != 255) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
|
||||
"NoA parameters");
|
||||
return hostapd_driver_set_noa(hapd, count, start, duration);
|
||||
}
|
||||
|
||||
hapd->noa_enabled = 1;
|
||||
hapd->noa_start = start;
|
||||
hapd->noa_duration = duration;
|
||||
|
||||
if (hapd->num_sta_no_p2p == 0) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
|
||||
"periodic NoA parameters");
|
||||
return hostapd_driver_set_noa(hapd, count, start, duration);
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
|
||||
"periodic NoA");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
|
||||
|
||||
if (hapd->noa_enabled) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
|
||||
hostapd_driver_set_noa(hapd, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
|
||||
|
||||
if (hapd->noa_enabled) {
|
||||
wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
|
||||
hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
|
||||
hapd->noa_duration);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 bitmap;
|
||||
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*eid++ = 4 + 3 + 1;
|
||||
WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
|
||||
eid += 4;
|
||||
|
||||
*eid++ = P2P_ATTR_MANAGEABILITY;
|
||||
WPA_PUT_LE16(eid, 1);
|
||||
eid += 2;
|
||||
bitmap = P2P_MAN_DEVICE_MANAGEMENT;
|
||||
if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
|
||||
bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
|
||||
bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
|
||||
*eid++ = bitmap;
|
||||
|
||||
return eid;
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
35
hostapd-2.9/src/ap/p2p_hostapd.h
Normal file
35
hostapd-2.9/src/ap/p2p_hostapd.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* hostapd / P2P integration
|
||||
* Copyright (c) 2009-2010, Atheros Communications
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef P2P_HOSTAPD_H
|
||||
#define P2P_HOSTAPD_H
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
|
||||
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
char *buf, size_t buflen);
|
||||
int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration);
|
||||
void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
|
||||
void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
|
||||
|
||||
|
||||
#else /* CONFIG_P2P */
|
||||
|
||||
static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
|
||||
|
||||
#endif /* P2P_HOSTAPD_H */
|
729
hostapd-2.9/src/ap/pmksa_cache_auth.c
Normal file
729
hostapd-2.9/src/ap/pmksa_cache_auth.c
Normal file
|
@ -0,0 +1,729 @@
|
|||
/*
|
||||
* 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);
|
||||
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--;
|
||||
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 (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 ||
|
||||
os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
|
||||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
|
||||
return entry;
|
||||
}
|
||||
} else {
|
||||
for (entry = pmksa->pmksa; entry; entry = entry->next) {
|
||||
if (spa == NULL ||
|
||||
os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
|
||||
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 (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
|
||||
continue;
|
||||
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 (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
|
||||
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 && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
|
||||
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 */
|
79
hostapd-2.9/src/ap/pmksa_cache_auth.h
Normal file
79
hostapd-2.9/src/ap/pmksa_cache_auth.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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;
|
||||
os_time_t expiration;
|
||||
int akmp; /* WPA_KEY_MGMT_* */
|
||||
u8 spa[ETH_ALEN];
|
||||
|
||||
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
hostapd-2.9/src/ap/preauth_auth.c
Normal file
273
hostapd-2.9/src/ap/preauth_auth.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
|
||||
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#ifdef CONFIG_RSN_PREAUTH
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "l2_packet/l2_packet.h"
|
||||
#include "common/wpa_common.h"
|
||||
#include "eapol_auth/eapol_auth_sm.h"
|
||||
#include "eapol_auth/eapol_auth_sm_i.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ieee802_1x.h"
|
||||
#include "sta_info.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "preauth_auth.h"
|
||||
|
||||
#ifndef ETH_P_PREAUTH
|
||||
#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
|
||||
#endif /* ETH_P_PREAUTH */
|
||||
|
||||
static const int dot11RSNAConfigPMKLifetime = 43200;
|
||||
|
||||
struct rsn_preauth_interface {
|
||||
struct rsn_preauth_interface *next;
|
||||
struct hostapd_data *hapd;
|
||||
struct l2_packet_data *l2;
|
||||
char *ifname;
|
||||
int ifindex;
|
||||
};
|
||||
|
||||
|
||||
static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct rsn_preauth_interface *piface = ctx;
|
||||
struct hostapd_data *hapd = piface->hapd;
|
||||
struct ieee802_1x_hdr *hdr;
|
||||
struct sta_info *sta;
|
||||
struct l2_ethhdr *ethhdr;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
|
||||
"from interface '%s'", piface->ifname);
|
||||
if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
|
||||
wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
|
||||
"(len=%lu)", (unsigned long) len);
|
||||
return;
|
||||
}
|
||||
|
||||
ethhdr = (struct l2_ethhdr *) buf;
|
||||
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
|
||||
|
||||
if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
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
hostapd-2.9/src/ap/preauth_auth.h
Normal file
52
hostapd-2.9/src/ap/preauth_auth.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
|
||||
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef PREAUTH_H
|
||||
#define PREAUTH_H
|
||||
|
||||
#ifdef CONFIG_RSN_PREAUTH
|
||||
|
||||
int rsn_preauth_iface_init(struct hostapd_data *hapd);
|
||||
void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
|
||||
void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int success);
|
||||
void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 *buf, size_t len);
|
||||
void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
|
||||
#else /* CONFIG_RSN_PREAUTH */
|
||||
|
||||
static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_finished(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
int success)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_send(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
u8 *buf, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_RSN_PREAUTH */
|
||||
|
||||
#endif /* PREAUTH_H */
|
674
hostapd-2.9/src/ap/rrm.c
Normal file
674
hostapd-2.9/src/ap/rrm.c
Normal file
|
@ -0,0 +1,674 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
33
hostapd-2.9/src/ap/rrm.h
Normal file
33
hostapd-2.9/src/ap/rrm.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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);
|
||||
|
||||
#endif /* RRM_H */
|
1488
hostapd-2.9/src/ap/sta_info.c
Normal file
1488
hostapd-2.9/src/ap/sta_info.c
Normal file
File diff suppressed because it is too large
Load diff
362
hostapd-2.9/src/ap/sta_info.h
Normal file
362
hostapd-2.9/src/ap/sta_info.h
Normal file
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
/* 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_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;
|
||||
};
|
||||
|
||||
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 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;
|
||||
|
||||
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;
|
||||
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
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;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
|
||||
#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;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
#ifdef CONFIG_AIRTIME_POLICY
|
||||
unsigned int airtime_weight;
|
||||
struct os_reltime backlogged_until;
|
||||
#endif /* CONFIG_AIRTIME_POLICY */
|
||||
};
|
||||
|
||||
|
||||
/* 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);
|
||||
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *addr, u16 reason);
|
||||
|
||||
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);
|
||||
int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
#endif /* STA_INFO_H */
|
292
hostapd-2.9/src/ap/taxonomy.c
Normal file
292
hostapd-2.9/src/ap/taxonomy.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* hostapd / Client taxonomy
|
||||
* Copyright (c) 2015 Google, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*
|
||||
* Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
|
||||
* and render them to a descriptive string. The tag number of standard options
|
||||
* is written to the string, while the vendor ID and subtag are written for
|
||||
* vendor options.
|
||||
*
|
||||
* Example strings:
|
||||
* 0,1,50,45,221(00904c,51)
|
||||
* 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "taxonomy.h"
|
||||
|
||||
|
||||
/* Copy a string with no funny schtuff allowed; only alphanumerics. */
|
||||
static void no_mischief_strncpy(char *dst, const char *src, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned char s = src[i];
|
||||
int is_lower = s >= 'a' && s <= 'z';
|
||||
int is_upper = s >= 'A' && s <= 'Z';
|
||||
int is_digit = s >= '0' && s <= '9';
|
||||
|
||||
if (is_lower || is_upper || is_digit) {
|
||||
/* TODO: if any manufacturer uses Unicode within the
|
||||
* WPS header, it will get mangled here. */
|
||||
dst[i] = s;
|
||||
} else {
|
||||
/* Note that even spaces will be transformed to
|
||||
* underscores, so 'Nexus 7' will turn into 'Nexus_7'.
|
||||
* This is deliberate, to make the string easier to
|
||||
* parse. */
|
||||
dst[i] = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int get_wps_name(char *name, size_t name_len,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
/* Inside the WPS IE are a series of attributes, using two byte IDs
|
||||
* and two byte lengths. We're looking for the model name, if
|
||||
* present. */
|
||||
while (data_len >= 4) {
|
||||
u16 id, elen;
|
||||
|
||||
id = WPA_GET_BE16(data);
|
||||
elen = WPA_GET_BE16(data + 2);
|
||||
data += 4;
|
||||
data_len -= 4;
|
||||
|
||||
if (elen > data_len)
|
||||
return 0;
|
||||
|
||||
if (id == 0x1023) {
|
||||
/* Model name, like 'Nexus 7' */
|
||||
size_t n = (elen < name_len) ? elen : name_len;
|
||||
no_mischief_strncpy(name, (const char *) data, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
data += elen;
|
||||
data_len -= elen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
|
||||
{
|
||||
char *fpos = fstr;
|
||||
char *fend = fstr + fstr_len;
|
||||
char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
|
||||
char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
|
||||
char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
|
||||
char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
|
||||
char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
|
||||
char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
|
||||
#define MAX_EXTCAP 254
|
||||
char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
|
||||
*/
|
||||
char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
|
||||
#define WPS_NAME_LEN 32
|
||||
char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
|
||||
* NUL */
|
||||
int num = 0;
|
||||
const u8 *ie;
|
||||
size_t ie_len;
|
||||
int ret;
|
||||
|
||||
os_memset(htcap, 0, sizeof(htcap));
|
||||
os_memset(htagg, 0, sizeof(htagg));
|
||||
os_memset(htmcs, 0, sizeof(htmcs));
|
||||
os_memset(vhtcap, 0, sizeof(vhtcap));
|
||||
os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
|
||||
os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
|
||||
os_memset(extcap, 0, sizeof(extcap));
|
||||
os_memset(txpow, 0, sizeof(txpow));
|
||||
os_memset(wps, 0, sizeof(wps));
|
||||
*fpos = '\0';
|
||||
|
||||
if (!ies)
|
||||
return;
|
||||
ie = wpabuf_head(ies);
|
||||
ie_len = wpabuf_len(ies);
|
||||
|
||||
while (ie_len >= 2) {
|
||||
u8 id, elen;
|
||||
char *sep = (num++ == 0) ? "" : ",";
|
||||
|
||||
id = *ie++;
|
||||
elen = *ie++;
|
||||
ie_len -= 2;
|
||||
|
||||
if (elen > ie_len)
|
||||
break;
|
||||
|
||||
if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
|
||||
/* Vendor specific */
|
||||
if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
|
||||
/* WPS */
|
||||
char model_name[WPS_NAME_LEN + 1];
|
||||
const u8 *data = &ie[4];
|
||||
size_t data_len = elen - 4;
|
||||
|
||||
os_memset(model_name, 0, sizeof(model_name));
|
||||
if (get_wps_name(model_name, WPS_NAME_LEN, data,
|
||||
data_len)) {
|
||||
os_snprintf(wps, sizeof(wps),
|
||||
",wps:%s", model_name);
|
||||
}
|
||||
}
|
||||
|
||||
ret = os_snprintf(fpos, fend - fpos,
|
||||
"%s%d(%02x%02x%02x,%d)",
|
||||
sep, id, ie[0], ie[1], ie[2], ie[3]);
|
||||
} else {
|
||||
if (id == WLAN_EID_HT_CAP && elen >= 2) {
|
||||
/* HT Capabilities (802.11n) */
|
||||
os_snprintf(htcap, sizeof(htcap),
|
||||
",htcap:%04hx",
|
||||
WPA_GET_LE16(ie));
|
||||
}
|
||||
if (id == WLAN_EID_HT_CAP && elen >= 3) {
|
||||
/* HT Capabilities (802.11n), A-MPDU information
|
||||
*/
|
||||
os_snprintf(htagg, sizeof(htagg),
|
||||
",htagg:%02hx", (u16) ie[2]);
|
||||
}
|
||||
if (id == WLAN_EID_HT_CAP && elen >= 7) {
|
||||
/* HT Capabilities (802.11n), MCS information */
|
||||
os_snprintf(htmcs, sizeof(htmcs),
|
||||
",htmcs:%08hx",
|
||||
(u16) WPA_GET_LE32(ie + 3));
|
||||
}
|
||||
if (id == WLAN_EID_VHT_CAP && elen >= 4) {
|
||||
/* VHT Capabilities (802.11ac) */
|
||||
os_snprintf(vhtcap, sizeof(vhtcap),
|
||||
",vhtcap:%08x",
|
||||
WPA_GET_LE32(ie));
|
||||
}
|
||||
if (id == WLAN_EID_VHT_CAP && elen >= 8) {
|
||||
/* VHT Capabilities (802.11ac), RX MCS
|
||||
* information */
|
||||
os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
|
||||
",vhtrxmcs:%08x",
|
||||
WPA_GET_LE32(ie + 4));
|
||||
}
|
||||
if (id == WLAN_EID_VHT_CAP && elen >= 12) {
|
||||
/* VHT Capabilities (802.11ac), TX MCS
|
||||
* information */
|
||||
os_snprintf(vhttxmcs, sizeof(vhttxmcs),
|
||||
",vhttxmcs:%08x",
|
||||
WPA_GET_LE32(ie + 8));
|
||||
}
|
||||
if (id == WLAN_EID_EXT_CAPAB) {
|
||||
/* Extended Capabilities */
|
||||
int i;
|
||||
int len = (elen < MAX_EXTCAP) ? elen :
|
||||
MAX_EXTCAP;
|
||||
char *p = extcap;
|
||||
|
||||
p += os_snprintf(extcap, sizeof(extcap),
|
||||
",extcap:");
|
||||
for (i = 0; i < len; i++) {
|
||||
int lim;
|
||||
|
||||
lim = sizeof(extcap) -
|
||||
os_strlen(extcap);
|
||||
if (lim <= 0)
|
||||
break;
|
||||
p += os_snprintf(p, lim, "%02x",
|
||||
*(ie + i));
|
||||
}
|
||||
}
|
||||
if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
|
||||
/* TX Power */
|
||||
os_snprintf(txpow, sizeof(txpow),
|
||||
",txpow:%04hx",
|
||||
WPA_GET_LE16(ie));
|
||||
}
|
||||
|
||||
ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
|
||||
}
|
||||
if (os_snprintf_error(fend - fpos, ret))
|
||||
goto fail;
|
||||
fpos += ret;
|
||||
|
||||
ie += elen;
|
||||
ie_len -= elen;
|
||||
}
|
||||
|
||||
ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
|
||||
htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
|
||||
txpow, extcap, wps);
|
||||
if (os_snprintf_error(fend - fpos, ret)) {
|
||||
fail:
|
||||
fstr[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta, char *buf, size_t buflen)
|
||||
{
|
||||
int ret;
|
||||
char *pos, *end;
|
||||
|
||||
if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
|
||||
return 0;
|
||||
|
||||
ret = os_snprintf(buf, buflen, "wifi4|probe:");
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
return 0;
|
||||
pos = buf + ret;
|
||||
end = buf + buflen;
|
||||
|
||||
ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
|
||||
pos = os_strchr(pos, '\0');
|
||||
if (pos >= end)
|
||||
return 0;
|
||||
ret = os_snprintf(pos, end - pos, "|assoc:");
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return 0;
|
||||
pos += ret;
|
||||
ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
|
||||
pos = os_strchr(pos, '\0');
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
wpabuf_free(sta->probe_ie_taxonomy);
|
||||
sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
|
||||
}
|
||||
|
||||
|
||||
void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct hostapd_sta_info *info,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
wpabuf_free(info->probe_ie_taxonomy);
|
||||
info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
|
||||
}
|
||||
|
||||
|
||||
void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
wpabuf_free(sta->assoc_ie_taxonomy);
|
||||
sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
|
||||
}
|
24
hostapd-2.9/src/ap/taxonomy.h
Normal file
24
hostapd-2.9/src/ap/taxonomy.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* hostapd / Station client taxonomy
|
||||
* Copyright (c) 2015 Google, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef TAXONOMY_H
|
||||
#define TAXONOMY_H
|
||||
|
||||
void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len);
|
||||
void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
|
||||
struct hostapd_sta_info *sta,
|
||||
const u8 *ie, size_t ie_len);
|
||||
void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
const u8 *ie, size_t ie_len);
|
||||
int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
|
||||
struct sta_info *sta, char *buf, size_t buflen);
|
||||
|
||||
#endif /* TAXONOMY_H */
|
110
hostapd-2.9/src/ap/tkip_countermeasures.c
Normal file
110
hostapd-2.9/src/ap/tkip_countermeasures.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* hostapd / TKIP countermeasures
|
||||
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "radius/radius.h"
|
||||
#include "hostapd.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_mlme.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "tkip_countermeasures.h"
|
||||
|
||||
|
||||
static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
|
||||
void *timeout_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
hapd->tkip_countermeasures = 0;
|
||||
hostapd_drv_set_countermeasures(hapd, 0);
|
||||
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
|
||||
|
||||
wpa_auth_countermeasures_start(hapd->wpa_auth);
|
||||
hapd->tkip_countermeasures = 1;
|
||||
hostapd_drv_set_countermeasures(hapd, 1);
|
||||
wpa_gtk_rekey(hapd->wpa_auth);
|
||||
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
|
||||
eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
|
||||
hapd, NULL);
|
||||
while ((sta = hapd->sta_list)) {
|
||||
sta->acct_terminate_cause =
|
||||
RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
|
||||
if (sta->flags & WLAN_STA_AUTH) {
|
||||
mlme_deauthenticate_indication(
|
||||
hapd, sta,
|
||||
WLAN_REASON_MICHAEL_MIC_FAILURE);
|
||||
}
|
||||
hostapd_drv_sta_deauth(hapd, sta->addr,
|
||||
WLAN_REASON_MICHAEL_MIC_FAILURE);
|
||||
ap_free_sta(hapd, sta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
|
||||
}
|
||||
|
||||
|
||||
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
|
||||
{
|
||||
struct os_reltime now;
|
||||
int ret = 0;
|
||||
|
||||
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Michael MIC failure detected in received frame%s",
|
||||
local ? " (local)" : "");
|
||||
|
||||
if (addr && local) {
|
||||
struct sta_info *sta = ap_get_sta(hapd, addr);
|
||||
if (sta != NULL) {
|
||||
wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
|
||||
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"Michael MIC failure detected in "
|
||||
"received frame");
|
||||
mlme_michaelmicfailure_indication(hapd, addr);
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"MLME-MICHAELMICFAILURE.indication "
|
||||
"for not associated STA (" MACSTR
|
||||
") ignored", MAC2STR(addr));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
os_get_reltime(&now);
|
||||
if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
|
||||
hapd->michael_mic_failures = 1;
|
||||
} else {
|
||||
hapd->michael_mic_failures++;
|
||||
if (hapd->michael_mic_failures > 1) {
|
||||
ieee80211_tkip_countermeasures_start(hapd);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
hapd->michael_mic_failure = now;
|
||||
|
||||
return ret;
|
||||
}
|
15
hostapd-2.9/src/ap/tkip_countermeasures.h
Normal file
15
hostapd-2.9/src/ap/tkip_countermeasures.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* hostapd / TKIP countermeasures
|
||||
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef TKIP_COUNTERMEASURES_H
|
||||
#define TKIP_COUNTERMEASURES_H
|
||||
|
||||
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
|
||||
void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* TKIP_COUNTERMEASURES_H */
|
96
hostapd-2.9/src/ap/utils.c
Normal file
96
hostapd-2.9/src/ap/utils.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
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_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;
|
||||
|
||||
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
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
struct prune_data data;
|
||||
data.hapd = hapd;
|
||||
data.addr = addr;
|
||||
if (hapd->iface->interfaces &&
|
||||
hapd->iface->interfaces->for_each_interface)
|
||||
hapd->iface->interfaces->for_each_interface(
|
||||
hapd->iface->interfaces, prune_associations, &data);
|
||||
}
|
34
hostapd-2.9/src/ap/vlan.c
Normal file
34
hostapd-2.9/src/ap/vlan.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* hostapd / VLAN definition
|
||||
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "ap/vlan.h"
|
||||
|
||||
/* compare the two arguments, NULL is treated as empty
|
||||
* return zero iff they are equal
|
||||
*/
|
||||
int vlan_compare(struct vlan_description *a, struct vlan_description *b)
|
||||
{
|
||||
int i;
|
||||
const int a_empty = !a || !a->notempty;
|
||||
const int b_empty = !b || !b->notempty;
|
||||
|
||||
if (a_empty && b_empty)
|
||||
return 0;
|
||||
if (a_empty || b_empty)
|
||||
return 1;
|
||||
if (a->untagged != b->untagged)
|
||||
return 1;
|
||||
for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
|
||||
if (a->tagged[i] != b->tagged[i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
30
hostapd-2.9/src/ap/vlan.h
Normal file
30
hostapd-2.9/src/ap/vlan.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* hostapd / VLAN definition
|
||||
* Copyright (c) 2015, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef VLAN_H
|
||||
#define VLAN_H
|
||||
|
||||
#define MAX_NUM_TAGGED_VLAN 32
|
||||
|
||||
struct vlan_description {
|
||||
int notempty; /* 0 : no vlan information present, 1: else */
|
||||
int untagged; /* >0 802.1q vid */
|
||||
int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
|
||||
};
|
||||
|
||||
#ifndef CONFIG_NO_VLAN
|
||||
int vlan_compare(struct vlan_description *a, struct vlan_description *b);
|
||||
#else /* CONFIG_NO_VLAN */
|
||||
static inline int
|
||||
vlan_compare(struct vlan_description *a, struct vlan_description *b)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NO_VLAN */
|
||||
|
||||
#endif /* VLAN_H */
|
801
hostapd-2.9/src/ap/vlan_full.c
Normal file
801
hostapd-2.9/src/ap/vlan_full.c
Normal file
|
@ -0,0 +1,801 @@
|
|||
/*
|
||||
* hostapd / VLAN initialization - full dynamic VLAN
|
||||
* Copyright 2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include <net/if.h>
|
||||
/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
|
||||
#undef if_type
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "drivers/priv_netlink.h"
|
||||
#include "drivers/linux_ioctl.h"
|
||||
#include "common/linux_bridge.h"
|
||||
#include "common/linux_vlan.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "vlan_init.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
|
||||
struct full_dynamic_vlan {
|
||||
int s; /* socket on which to listen for new/removed interfaces. */
|
||||
};
|
||||
|
||||
#define DVLAN_CLEAN_BR 0x1
|
||||
#define DVLAN_CLEAN_VLAN 0x2
|
||||
#define DVLAN_CLEAN_VLAN_PORT 0x4
|
||||
|
||||
struct dynamic_iface {
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
int usage;
|
||||
int clean;
|
||||
struct dynamic_iface *next;
|
||||
};
|
||||
|
||||
|
||||
/* Increment ref counter for ifname and add clean flag.
|
||||
* If not in list, add it only if some flags are given.
|
||||
*/
|
||||
static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
|
||||
int clean)
|
||||
{
|
||||
struct dynamic_iface *next, **dynamic_ifaces;
|
||||
struct hapd_interfaces *interfaces;
|
||||
|
||||
interfaces = hapd->iface->interfaces;
|
||||
dynamic_ifaces = &interfaces->vlan_priv;
|
||||
|
||||
for (next = *dynamic_ifaces; next; next = next->next) {
|
||||
if (os_strcmp(ifname, next->ifname) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
next->usage++;
|
||||
next->clean |= clean;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clean)
|
||||
return;
|
||||
|
||||
next = os_zalloc(sizeof(*next));
|
||||
if (!next)
|
||||
return;
|
||||
os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
|
||||
next->usage = 1;
|
||||
next->clean = clean;
|
||||
next->next = *dynamic_ifaces;
|
||||
*dynamic_ifaces = next;
|
||||
}
|
||||
|
||||
|
||||
/* Decrement reference counter for given ifname.
|
||||
* Return clean flag iff reference counter was decreased to zero, else zero
|
||||
*/
|
||||
static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
|
||||
struct hapd_interfaces *interfaces;
|
||||
int clean;
|
||||
|
||||
interfaces = hapd->iface->interfaces;
|
||||
dynamic_ifaces = &interfaces->vlan_priv;
|
||||
|
||||
for (next = *dynamic_ifaces; next; next = next->next) {
|
||||
if (os_strcmp(ifname, next->ifname) == 0)
|
||||
break;
|
||||
prev = next;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return 0;
|
||||
|
||||
next->usage--;
|
||||
if (next->usage)
|
||||
return 0;
|
||||
|
||||
if (prev)
|
||||
prev->next = next->next;
|
||||
else
|
||||
*dynamic_ifaces = next->next;
|
||||
clean = next->clean;
|
||||
os_free(next);
|
||||
|
||||
return clean;
|
||||
}
|
||||
|
||||
|
||||
static int ifconfig_down(const char *if_name)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
|
||||
return ifconfig_helper(if_name, 0);
|
||||
}
|
||||
|
||||
|
||||
/* This value should be 256 ONLY. If it is something else, then hostapd
|
||||
* might crash!, as this value has been hard-coded in 2.4.x kernel
|
||||
* bridging code.
|
||||
*/
|
||||
#define MAX_BR_PORTS 256
|
||||
|
||||
static int br_delif(const char *br_name, const char *if_name)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
unsigned long args[2];
|
||||
int if_index;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_del_if(fd, br_name, if_name) == 0)
|
||||
goto done;
|
||||
|
||||
if_index = if_nametoindex(if_name);
|
||||
|
||||
if (if_index == 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
|
||||
"interface index for '%s'",
|
||||
__func__, if_name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args[0] = BRCTL_DEL_IF;
|
||||
args[1] = if_index;
|
||||
|
||||
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (void *) args;
|
||||
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
|
||||
/* No error if interface already removed. */
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
|
||||
"BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
|
||||
"%s", __func__, br_name, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add interface 'if_name' to the bridge 'br_name'
|
||||
|
||||
returns -1 on error
|
||||
returns 1 if the interface is already part of the bridge
|
||||
returns 0 otherwise
|
||||
*/
|
||||
static int br_addif(const char *br_name, const char *if_name)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
unsigned long args[2];
|
||||
int if_index;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_add_if(fd, br_name, if_name) == 0)
|
||||
goto done;
|
||||
if (errno == EBUSY) {
|
||||
/* The interface is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if_index = if_nametoindex(if_name);
|
||||
|
||||
if (if_index == 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
|
||||
"interface index for '%s'",
|
||||
__func__, if_name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args[0] = BRCTL_ADD_IF;
|
||||
args[1] = if_index;
|
||||
|
||||
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (void *) args;
|
||||
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
||||
if (errno == EBUSY) {
|
||||
/* The interface is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
|
||||
"BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
|
||||
"%s", __func__, br_name, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int br_delbr(const char *br_name)
|
||||
{
|
||||
int fd;
|
||||
unsigned long arg[2];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_del(fd, br_name) == 0)
|
||||
goto done;
|
||||
|
||||
arg[0] = BRCTL_DEL_BRIDGE;
|
||||
arg[1] = (unsigned long) br_name;
|
||||
|
||||
if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
|
||||
/* No error if bridge already removed. */
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
|
||||
"%s: %s", __func__, br_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add a bridge with the name 'br_name'.
|
||||
|
||||
returns -1 on error
|
||||
returns 1 if the bridge already exists
|
||||
returns 0 otherwise
|
||||
*/
|
||||
static int br_addbr(const char *br_name)
|
||||
{
|
||||
int fd;
|
||||
unsigned long arg[4];
|
||||
struct ifreq ifr;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (linux_br_add(fd, br_name) == 0)
|
||||
goto done;
|
||||
if (errno == EEXIST) {
|
||||
/* The bridge is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
arg[0] = BRCTL_ADD_BRIDGE;
|
||||
arg[1] = (unsigned long) br_name;
|
||||
|
||||
if (ioctl(fd, SIOCGIFBR, arg) < 0) {
|
||||
if (errno == EEXIST) {
|
||||
/* The bridge is already added. */
|
||||
close(fd);
|
||||
return 1;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
|
||||
"failed for %s: %s",
|
||||
__func__, br_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
/* Decrease forwarding delay to avoid EAPOL timeouts. */
|
||||
os_memset(&ifr, 0, sizeof(ifr));
|
||||
os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
|
||||
arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
|
||||
arg[1] = 1;
|
||||
arg[2] = 0;
|
||||
arg[3] = 0;
|
||||
ifr.ifr_data = (char *) &arg;
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: "
|
||||
"BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
|
||||
"%s: %s", __func__, br_name, strerror(errno));
|
||||
/* Continue anyway */
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int br_getnumports(const char *br_name)
|
||||
{
|
||||
int fd;
|
||||
int i;
|
||||
int port_cnt = 0;
|
||||
unsigned long arg[4];
|
||||
int ifindices[MAX_BR_PORTS];
|
||||
struct ifreq ifr;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
arg[0] = BRCTL_GET_PORT_LIST;
|
||||
arg[1] = (unsigned long) ifindices;
|
||||
arg[2] = MAX_BR_PORTS;
|
||||
arg[3] = 0;
|
||||
|
||||
os_memset(ifindices, 0, sizeof(ifindices));
|
||||
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (void *) arg;
|
||||
|
||||
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
|
||||
"failed for %s: %s",
|
||||
__func__, br_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 1; i < MAX_BR_PORTS; i++) {
|
||||
if (ifindices[i] > 0) {
|
||||
port_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return port_cnt;
|
||||
}
|
||||
|
||||
|
||||
static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
|
||||
const char *br_name, int vid,
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
char vlan_ifname[IFNAMSIZ];
|
||||
int clean;
|
||||
int ret;
|
||||
|
||||
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
|
||||
tagged_interface, vid);
|
||||
else
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
|
||||
vid);
|
||||
if (ret >= (int) sizeof(vlan_ifname))
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
vlan_ifname);
|
||||
|
||||
clean = 0;
|
||||
ifconfig_up(tagged_interface);
|
||||
if (!vlan_add(tagged_interface, vid, vlan_ifname))
|
||||
clean |= DVLAN_CLEAN_VLAN;
|
||||
|
||||
if (!br_addif(br_name, vlan_ifname))
|
||||
clean |= DVLAN_CLEAN_VLAN_PORT;
|
||||
|
||||
dyn_iface_get(hapd, vlan_ifname, clean);
|
||||
|
||||
ifconfig_up(vlan_ifname);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
|
||||
struct hostapd_vlan *vlan, int vid)
|
||||
{
|
||||
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
||||
int ret;
|
||||
|
||||
if (vlan->bridge[0]) {
|
||||
os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
|
||||
ret = 0;
|
||||
} else if (hapd->conf->vlan_bridge[0]) {
|
||||
ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
|
||||
hapd->conf->vlan_bridge, vid);
|
||||
} else if (tagged_interface) {
|
||||
ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
|
||||
tagged_interface, vid);
|
||||
} else {
|
||||
ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
|
||||
}
|
||||
if (ret >= IFNAMSIZ)
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
br_name);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
|
||||
int vid)
|
||||
{
|
||||
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
||||
int vlan_naming = hapd->conf->ssid.vlan_naming;
|
||||
|
||||
dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
|
||||
|
||||
ifconfig_up(br_name);
|
||||
|
||||
if (tagged_interface)
|
||||
vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
|
||||
vid, hapd);
|
||||
}
|
||||
|
||||
|
||||
void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
|
||||
{
|
||||
char br_name[IFNAMSIZ];
|
||||
struct hostapd_vlan *vlan;
|
||||
int untagged, *tagged, i, notempty;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
|
||||
|
||||
for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
|
||||
if (vlan->configured ||
|
||||
os_strcmp(ifname, vlan->ifname) != 0)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (!vlan)
|
||||
return;
|
||||
|
||||
vlan->configured = 1;
|
||||
|
||||
notempty = vlan->vlan_desc.notempty;
|
||||
untagged = vlan->vlan_desc.untagged;
|
||||
tagged = vlan->vlan_desc.tagged;
|
||||
|
||||
if (!notempty) {
|
||||
/* Non-VLAN STA */
|
||||
if (hapd->conf->bridge[0] &&
|
||||
!br_addif(hapd->conf->bridge, ifname))
|
||||
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
|
||||
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
|
||||
vlan_bridge_name(br_name, hapd, vlan, untagged);
|
||||
|
||||
vlan_get_bridge(br_name, hapd, untagged);
|
||||
|
||||
if (!br_addif(br_name, ifname))
|
||||
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
|
||||
if (tagged[i] == untagged ||
|
||||
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
|
||||
(i > 0 && tagged[i] == tagged[i - 1]))
|
||||
continue;
|
||||
vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
|
||||
vlan_get_bridge(br_name, hapd, tagged[i]);
|
||||
vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
|
||||
ifname, br_name, tagged[i], hapd);
|
||||
}
|
||||
|
||||
ifconfig_up(ifname);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
|
||||
const char *br_name, int vid,
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
char vlan_ifname[IFNAMSIZ];
|
||||
int clean;
|
||||
int ret;
|
||||
|
||||
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
|
||||
tagged_interface, vid);
|
||||
else
|
||||
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
|
||||
vid);
|
||||
if (ret >= (int) sizeof(vlan_ifname))
|
||||
wpa_printf(MSG_WARNING,
|
||||
"VLAN: Interface name was truncated to %s",
|
||||
vlan_ifname);
|
||||
|
||||
|
||||
clean = dyn_iface_put(hapd, vlan_ifname);
|
||||
|
||||
if (clean & DVLAN_CLEAN_VLAN_PORT)
|
||||
br_delif(br_name, vlan_ifname);
|
||||
|
||||
if (clean & DVLAN_CLEAN_VLAN) {
|
||||
ifconfig_down(vlan_ifname);
|
||||
vlan_rem(vlan_ifname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
|
||||
int vid)
|
||||
{
|
||||
int clean;
|
||||
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
||||
int vlan_naming = hapd->conf->ssid.vlan_naming;
|
||||
|
||||
if (tagged_interface)
|
||||
vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
|
||||
vid, hapd);
|
||||
|
||||
clean = dyn_iface_put(hapd, br_name);
|
||||
if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
|
||||
ifconfig_down(br_name);
|
||||
br_delbr(br_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
|
||||
{
|
||||
struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
|
||||
|
||||
first = prev = vlan;
|
||||
|
||||
while (vlan) {
|
||||
if (os_strcmp(ifname, vlan->ifname) != 0) {
|
||||
prev = vlan;
|
||||
vlan = vlan->next;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!vlan)
|
||||
return;
|
||||
|
||||
if (vlan->configured) {
|
||||
int notempty = vlan->vlan_desc.notempty;
|
||||
int untagged = vlan->vlan_desc.untagged;
|
||||
int *tagged = vlan->vlan_desc.tagged;
|
||||
char br_name[IFNAMSIZ];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
|
||||
if (tagged[i] == untagged ||
|
||||
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
|
||||
(i > 0 && tagged[i] == tagged[i - 1]))
|
||||
continue;
|
||||
vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
|
||||
vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
|
||||
ifname, br_name, tagged[i], hapd);
|
||||
vlan_put_bridge(br_name, hapd, tagged[i]);
|
||||
}
|
||||
|
||||
if (!notempty) {
|
||||
/* Non-VLAN STA */
|
||||
if (hapd->conf->bridge[0] &&
|
||||
(vlan->clean & DVLAN_CLEAN_WLAN_PORT))
|
||||
br_delif(hapd->conf->bridge, ifname);
|
||||
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
|
||||
vlan_bridge_name(br_name, hapd, vlan, untagged);
|
||||
|
||||
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
|
||||
br_delif(br_name, vlan->ifname);
|
||||
|
||||
vlan_put_bridge(br_name, hapd, untagged);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure this VLAN interface is actually removed even if
|
||||
* NEWLINK message is only received later.
|
||||
*/
|
||||
if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
|
||||
wpa_printf(MSG_ERROR,
|
||||
"VLAN: Could not remove VLAN iface: %s: %s",
|
||||
vlan->ifname, strerror(errno));
|
||||
|
||||
if (vlan == first)
|
||||
hapd->conf->vlan = vlan->next;
|
||||
else
|
||||
prev->next = vlan->next;
|
||||
|
||||
os_free(vlan);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
struct ifinfomsg *ifi;
|
||||
int attrlen, nlmsg_len, rta_len;
|
||||
struct rtattr *attr;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
|
||||
if (len < sizeof(*ifi))
|
||||
return;
|
||||
|
||||
ifi = NLMSG_DATA(h);
|
||||
|
||||
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
||||
|
||||
attrlen = h->nlmsg_len - nlmsg_len;
|
||||
if (attrlen < 0)
|
||||
return;
|
||||
|
||||
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
|
||||
|
||||
os_memset(ifname, 0, sizeof(ifname));
|
||||
rta_len = RTA_ALIGN(sizeof(struct rtattr));
|
||||
while (RTA_OK(attr, attrlen)) {
|
||||
if (attr->rta_type == IFLA_IFNAME) {
|
||||
int n = attr->rta_len - rta_len;
|
||||
if (n < 0)
|
||||
break;
|
||||
|
||||
if ((size_t) n >= sizeof(ifname))
|
||||
n = sizeof(ifname) - 1;
|
||||
os_memcpy(ifname, ((char *) attr) + rta_len, n);
|
||||
|
||||
}
|
||||
|
||||
attr = RTA_NEXT(attr, attrlen);
|
||||
}
|
||||
|
||||
if (!ifname[0])
|
||||
return;
|
||||
if (del && if_nametoindex(ifname)) {
|
||||
/* interface still exists, race condition ->
|
||||
* iface has just been recreated */
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
|
||||
del ? "DEL" : "NEW",
|
||||
ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
|
||||
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
|
||||
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
|
||||
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
|
||||
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
|
||||
|
||||
if (del)
|
||||
vlan_dellink(ifname, hapd);
|
||||
else
|
||||
vlan_newlink(ifname, hapd);
|
||||
}
|
||||
|
||||
|
||||
static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
{
|
||||
char buf[8192];
|
||||
int left;
|
||||
struct sockaddr_nl from;
|
||||
socklen_t fromlen;
|
||||
struct nlmsghdr *h;
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
|
||||
fromlen = sizeof(from);
|
||||
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
|
||||
(struct sockaddr *) &from, &fromlen);
|
||||
if (left < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
|
||||
__func__, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
h = (struct nlmsghdr *) buf;
|
||||
while (NLMSG_OK(h, left)) {
|
||||
int len, plen;
|
||||
|
||||
len = h->nlmsg_len;
|
||||
plen = len - sizeof(*h);
|
||||
if (len > left || plen < 0) {
|
||||
wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
|
||||
"message: len=%d left=%d plen=%d",
|
||||
len, left, plen);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (h->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
vlan_read_ifnames(h, plen, 0, hapd);
|
||||
break;
|
||||
case RTM_DELLINK:
|
||||
vlan_read_ifnames(h, plen, 1, hapd);
|
||||
break;
|
||||
}
|
||||
|
||||
h = NLMSG_NEXT(h, left);
|
||||
}
|
||||
|
||||
if (left > 0) {
|
||||
wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
|
||||
"netlink message", __func__, left);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct full_dynamic_vlan *
|
||||
full_dynamic_vlan_init(struct hostapd_data *hapd)
|
||||
{
|
||||
struct sockaddr_nl local;
|
||||
struct full_dynamic_vlan *priv;
|
||||
|
||||
priv = os_zalloc(sizeof(*priv));
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
|
||||
vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
|
||||
DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
|
||||
VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
|
||||
VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
|
||||
|
||||
priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (priv->s < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
|
||||
"NETLINK_ROUTE) failed: %s",
|
||||
__func__, strerror(errno));
|
||||
os_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = RTMGRP_LINK;
|
||||
if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
|
||||
__func__, strerror(errno));
|
||||
close(priv->s);
|
||||
os_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
|
||||
{
|
||||
close(priv->s);
|
||||
os_free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
||||
|
||||
void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
|
||||
{
|
||||
if (priv == NULL)
|
||||
return;
|
||||
eloop_unregister_read_sock(priv->s);
|
||||
close(priv->s);
|
||||
os_free(priv);
|
||||
}
|
69
hostapd-2.9/src/ap/vlan_ifconfig.c
Normal file
69
hostapd-2.9/src/ap/vlan_ifconfig.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* hostapd / VLAN ifconfig helpers
|
||||
* Copyright 2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
|
||||
int ifconfig_helper(const char *if_name, int up)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
||||
"failed: %s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memset(&ifr, 0, sizeof(ifr));
|
||||
os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
|
||||
|
||||
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
|
||||
"for interface %s: %s",
|
||||
__func__, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (up)
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
else
|
||||
ifr.ifr_flags &= ~IFF_UP;
|
||||
|
||||
if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
|
||||
"for interface %s (up=%d): %s",
|
||||
__func__, if_name, up, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ifconfig_up(const char *if_name)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
|
||||
return ifconfig_helper(if_name, 1);
|
||||
}
|
||||
|
||||
|
||||
int iface_exists(const char *ifname)
|
||||
{
|
||||
return if_nametoindex(ifname);
|
||||
}
|
264
hostapd-2.9/src/ap/vlan_init.c
Normal file
264
hostapd-2.9/src/ap/vlan_init.c
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
44
hostapd-2.9/src/ap/vlan_init.h
Normal file
44
hostapd-2.9/src/ap/vlan_init.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* hostapd / VLAN initialization
|
||||
* Copyright 2003, Instant802 Networks, Inc.
|
||||
* Copyright 2005, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef VLAN_INIT_H
|
||||
#define VLAN_INIT_H
|
||||
|
||||
#ifndef CONFIG_NO_VLAN
|
||||
int vlan_init(struct hostapd_data *hapd);
|
||||
void vlan_deinit(struct hostapd_data *hapd);
|
||||
struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
|
||||
struct hostapd_vlan *vlan,
|
||||
int vlan_id,
|
||||
struct vlan_description *vlan_desc);
|
||||
int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
|
||||
#else /* CONFIG_NO_VLAN */
|
||||
static inline int vlan_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void vlan_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct hostapd_vlan *
|
||||
vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
|
||||
int vlan_id, struct vlan_description *vlan_desc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_NO_VLAN */
|
||||
|
||||
#endif /* VLAN_INIT_H */
|
155
hostapd-2.9/src/ap/vlan_ioctl.c
Normal file
155
hostapd-2.9/src/ap/vlan_ioctl.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* hostapd / VLAN ioctl API
|
||||
* 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 <sys/ioctl.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/linux_vlan.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
|
||||
int vlan_rem(const char *if_name)
|
||||
{
|
||||
int fd;
|
||||
struct vlan_ioctl_args if_request;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
|
||||
if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
|
||||
if_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(&if_request, 0, sizeof(if_request));
|
||||
|
||||
os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
|
||||
if_request.cmd = DEL_VLAN_CMD;
|
||||
|
||||
if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
|
||||
"%s", __func__, if_name, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add a vlan interface with VLAN ID 'vid' and tagged interface
|
||||
'if_name'.
|
||||
|
||||
returns -1 on error
|
||||
returns 1 if the interface already exists
|
||||
returns 0 otherwise
|
||||
*/
|
||||
int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
|
||||
{
|
||||
int fd;
|
||||
struct vlan_ioctl_args if_request;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
|
||||
if_name, vid);
|
||||
ifconfig_up(if_name);
|
||||
|
||||
if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
|
||||
if_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(&if_request, 0, sizeof(if_request));
|
||||
|
||||
/* Determine if a suitable vlan device already exists. */
|
||||
|
||||
os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
|
||||
vid);
|
||||
|
||||
if_request.cmd = GET_VLAN_VID_CMD;
|
||||
|
||||
if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
|
||||
if_request.u.VID == vid) {
|
||||
if_request.cmd = GET_VLAN_REALDEV_NAME_CMD;
|
||||
|
||||
if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
|
||||
os_strncmp(if_request.u.device2, if_name,
|
||||
sizeof(if_request.u.device2)) == 0) {
|
||||
close(fd);
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"VLAN: vlan_add: if_name %s exists already",
|
||||
if_request.device1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* A suitable vlan device does not already exist, add one. */
|
||||
|
||||
os_memset(&if_request, 0, sizeof(if_request));
|
||||
os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
|
||||
if_request.u.VID = vid;
|
||||
if_request.cmd = ADD_VLAN_CMD;
|
||||
|
||||
if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"VLAN: %s: ADD_VLAN_CMD failed for %s: %s",
|
||||
__func__, if_request.device1, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vlan_set_name_type(unsigned int name_type)
|
||||
{
|
||||
int fd;
|
||||
struct vlan_ioctl_args if_request;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
|
||||
name_type);
|
||||
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(&if_request, 0, sizeof(if_request));
|
||||
|
||||
if_request.u.name_type = name_type;
|
||||
if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
|
||||
if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s",
|
||||
__func__, name_type, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
174
hostapd-2.9/src/ap/vlan_util.c
Normal file
174
hostapd-2.9/src/ap/vlan_util.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* hostapd / VLAN netlink api
|
||||
* Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include <netlink/route/link.h>
|
||||
#include <netlink/route/link/vlan.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "vlan_util.h"
|
||||
|
||||
/*
|
||||
* Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
|
||||
* tagged interface 'if_name'.
|
||||
*
|
||||
* returns -1 on error
|
||||
* returns 1 if the interface already exists
|
||||
* returns 0 otherwise
|
||||
*/
|
||||
int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
|
||||
{
|
||||
int err, ret = -1;
|
||||
struct nl_sock *handle = NULL;
|
||||
struct rtnl_link *rlink = NULL;
|
||||
int if_idx = 0;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
|
||||
"vlan_if_name=%s)", if_name, vid, vlan_if_name);
|
||||
|
||||
if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
|
||||
if_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
|
||||
vlan_if_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
handle = nl_socket_alloc();
|
||||
if (!handle) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
err = nl_connect(handle, NETLINK_ROUTE);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
|
||||
nl_geterror(err));
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
|
||||
if (err < 0) {
|
||||
/* link does not exist */
|
||||
wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
|
||||
if_name);
|
||||
goto vlan_add_error;
|
||||
}
|
||||
if_idx = rtnl_link_get_ifindex(rlink);
|
||||
rtnl_link_put(rlink);
|
||||
rlink = NULL;
|
||||
|
||||
err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink);
|
||||
if (err >= 0) {
|
||||
/* link does exist */
|
||||
rtnl_link_put(rlink);
|
||||
rlink = NULL;
|
||||
wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
|
||||
vlan_if_name);
|
||||
ret = 1;
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
rlink = rtnl_link_alloc();
|
||||
if (!rlink) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
err = rtnl_link_set_type(rlink, "vlan");
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s",
|
||||
nl_geterror(err));
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
rtnl_link_set_link(rlink, if_idx);
|
||||
rtnl_link_set_name(rlink, vlan_if_name);
|
||||
|
||||
err = rtnl_link_vlan_set_id(rlink, vid);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s",
|
||||
nl_geterror(err));
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
err = rtnl_link_add(handle, rlink, NLM_F_CREATE);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
|
||||
"vlan %d on %s (%d): %s",
|
||||
vlan_if_name, vid, if_name, if_idx,
|
||||
nl_geterror(err));
|
||||
goto vlan_add_error;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
vlan_add_error:
|
||||
if (rlink)
|
||||
rtnl_link_put(rlink);
|
||||
if (handle)
|
||||
nl_socket_free(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int vlan_rem(const char *if_name)
|
||||
{
|
||||
int err, ret = -1;
|
||||
struct nl_sock *handle = NULL;
|
||||
struct rtnl_link *rlink = NULL;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
|
||||
|
||||
handle = nl_socket_alloc();
|
||||
if (!handle) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
|
||||
goto vlan_rem_error;
|
||||
}
|
||||
|
||||
err = nl_connect(handle, NETLINK_ROUTE);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
|
||||
nl_geterror(err));
|
||||
goto vlan_rem_error;
|
||||
}
|
||||
|
||||
err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
|
||||
if (err < 0) {
|
||||
/* link does not exist */
|
||||
wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
|
||||
if_name);
|
||||
goto vlan_rem_error;
|
||||
}
|
||||
|
||||
err = rtnl_link_delete(handle, rlink);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s",
|
||||
if_name, nl_geterror(err));
|
||||
goto vlan_rem_error;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
vlan_rem_error:
|
||||
if (rlink)
|
||||
rtnl_link_put(rlink);
|
||||
if (handle)
|
||||
nl_socket_free(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int vlan_set_name_type(unsigned int name_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
31
hostapd-2.9/src/ap/vlan_util.h
Normal file
31
hostapd-2.9/src/ap/vlan_util.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* hostapd / VLAN netlink/ioctl api
|
||||
* Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef VLAN_UTIL_H
|
||||
#define VLAN_UTIL_H
|
||||
|
||||
struct hostapd_data;
|
||||
struct hostapd_vlan;
|
||||
struct full_dynamic_vlan;
|
||||
|
||||
int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
|
||||
int vlan_rem(const char *if_name);
|
||||
int vlan_set_name_type(unsigned int name_type);
|
||||
|
||||
int ifconfig_helper(const char *if_name, int up);
|
||||
int ifconfig_up(const char *if_name);
|
||||
int iface_exists(const char *ifname);
|
||||
int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
|
||||
|
||||
struct full_dynamic_vlan *
|
||||
full_dynamic_vlan_init(struct hostapd_data *hapd);
|
||||
void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv);
|
||||
void vlan_newlink(const char *ifname, struct hostapd_data *hapd);
|
||||
void vlan_dellink(const char *ifname, struct hostapd_data *hapd);
|
||||
|
||||
#endif /* VLAN_UTIL_H */
|
388
hostapd-2.9/src/ap/wmm.c
Normal file
388
hostapd-2.9/src/ap/wmm.c
Normal file
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* hostapd / WMM (Wi-Fi Multimedia)
|
||||
* Copyright 2002-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 "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "wmm.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
|
||||
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
|
||||
{
|
||||
u8 ret;
|
||||
ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
|
||||
if (acm)
|
||||
ret |= WMM_AC_ACM;
|
||||
ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline u8 wmm_ecw(int ecwmin, int ecwmax)
|
||||
{
|
||||
return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
|
||||
((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf,
|
||||
struct hostapd_wmm_ac_params *wmm,
|
||||
const struct hostapd_wmm_rule *wmm_reg)
|
||||
{
|
||||
int ac;
|
||||
|
||||
for (ac = 0; ac < WMM_AC_NUM; ac++) {
|
||||
wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin);
|
||||
wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax);
|
||||
wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs);
|
||||
wmm[ac].txop_limit =
|
||||
MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop);
|
||||
wmm[ac].admission_control_mandatory =
|
||||
wmm_conf[ac].admission_control_mandatory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Calculate WMM regulatory limit if any.
|
||||
*/
|
||||
static void wmm_calc_regulatory_limit(struct hostapd_data *hapd,
|
||||
struct hostapd_wmm_ac_params *acp)
|
||||
{
|
||||
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
|
||||
int c;
|
||||
|
||||
os_memcpy(acp, hapd->iconf->wmm_ac_params,
|
||||
sizeof(hapd->iconf->wmm_ac_params));
|
||||
|
||||
for (c = 0; mode && c < mode->num_channels; c++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[c];
|
||||
|
||||
if (chan->freq != hapd->iface->freq)
|
||||
continue;
|
||||
|
||||
if (chan->wmm_rules_valid)
|
||||
wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params,
|
||||
acp, chan->wmm_rules);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we need to update set count. Since both were initialized to
|
||||
* zero we can compare the whole array in one shot.
|
||||
*/
|
||||
if (os_memcmp(acp, hapd->iface->prev_wmm,
|
||||
sizeof(hapd->iconf->wmm_ac_params)) != 0) {
|
||||
os_memcpy(hapd->iface->prev_wmm, acp,
|
||||
sizeof(hapd->iconf->wmm_ac_params));
|
||||
hapd->parameter_set_count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
|
||||
* Response frames.
|
||||
*/
|
||||
u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
|
||||
{
|
||||
u8 *pos = eid;
|
||||
struct wmm_parameter_element *wmm =
|
||||
(struct wmm_parameter_element *) (pos + 2);
|
||||
struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM] = { 0 };
|
||||
int e;
|
||||
|
||||
if (!hapd->conf->wmm_enabled)
|
||||
return eid;
|
||||
wmm_calc_regulatory_limit(hapd, wmmp);
|
||||
eid[0] = WLAN_EID_VENDOR_SPECIFIC;
|
||||
wmm->oui[0] = 0x00;
|
||||
wmm->oui[1] = 0x50;
|
||||
wmm->oui[2] = 0xf2;
|
||||
wmm->oui_type = WMM_OUI_TYPE;
|
||||
wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
|
||||
wmm->version = WMM_VERSION;
|
||||
wmm->qos_info = hapd->parameter_set_count & 0xf;
|
||||
|
||||
if (hapd->conf->wmm_uapsd &&
|
||||
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
|
||||
wmm->qos_info |= 0x80;
|
||||
|
||||
wmm->reserved = 0;
|
||||
|
||||
/* fill in a parameter set record for each AC */
|
||||
for (e = 0; e < 4; e++) {
|
||||
struct wmm_ac_parameter *ac = &wmm->ac[e];
|
||||
struct hostapd_wmm_ac_params *acp = &wmmp[e];
|
||||
|
||||
ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
|
||||
acp->admission_control_mandatory,
|
||||
e);
|
||||
ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
|
||||
ac->txop_limit = host_to_le16(acp->txop_limit);
|
||||
}
|
||||
|
||||
pos = (u8 *) (wmm + 1);
|
||||
eid[1] = pos - eid - 2; /* element length */
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function is called when a station sends an association request with
|
||||
* WMM info element. The function returns 1 on success or 0 on any error in WMM
|
||||
* element. eid does not include Element ID and Length octets.
|
||||
*/
|
||||
int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
|
||||
{
|
||||
struct wmm_information_element *wmm;
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
|
||||
|
||||
if (len < sizeof(struct wmm_information_element)) {
|
||||
wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
|
||||
(unsigned long) len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wmm = (struct wmm_information_element *) eid;
|
||||
wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x "
|
||||
"OUI type %d OUI sub-type %d version %d QoS info 0x%x",
|
||||
wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
|
||||
wmm->oui_subtype, wmm->version, wmm->qos_info);
|
||||
if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
|
||||
wmm->version != WMM_VERSION) {
|
||||
wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
|
||||
const struct wmm_tspec_element *tspec,
|
||||
u8 action_code, u8 dialogue_token, u8 status_code)
|
||||
{
|
||||
u8 buf[256];
|
||||
struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
|
||||
struct wmm_tspec_element *t = (struct wmm_tspec_element *)
|
||||
m->u.action.u.wmm_action.variable;
|
||||
int len;
|
||||
|
||||
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"action response - reason %d", status_code);
|
||||
os_memset(buf, 0, sizeof(buf));
|
||||
m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
os_memcpy(m->da, addr, ETH_ALEN);
|
||||
os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
|
||||
m->u.action.category = WLAN_ACTION_WMM;
|
||||
m->u.action.u.wmm_action.action_code = action_code;
|
||||
m->u.action.u.wmm_action.dialog_token = dialogue_token;
|
||||
m->u.action.u.wmm_action.status_code = status_code;
|
||||
os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
|
||||
len = ((u8 *) (t + 1)) - buf;
|
||||
|
||||
if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
|
||||
wpa_printf(MSG_INFO, "wmm_send_action: send failed");
|
||||
}
|
||||
|
||||
|
||||
int wmm_process_tspec(struct wmm_tspec_element *tspec)
|
||||
{
|
||||
u64 medium_time;
|
||||
unsigned int pps, duration;
|
||||
unsigned int up, psb, dir, tid;
|
||||
u16 val, surplus;
|
||||
|
||||
up = (tspec->ts_info[1] >> 3) & 0x07;
|
||||
psb = (tspec->ts_info[1] >> 2) & 0x01;
|
||||
dir = (tspec->ts_info[0] >> 5) & 0x03;
|
||||
tid = (tspec->ts_info[0] >> 1) & 0x0f;
|
||||
wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
|
||||
up, psb, dir, tid);
|
||||
val = le_to_host16(tspec->nominal_msdu_size);
|
||||
wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
|
||||
val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
|
||||
wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
|
||||
le_to_host32(tspec->mean_data_rate));
|
||||
wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
|
||||
le_to_host32(tspec->minimum_phy_rate));
|
||||
val = le_to_host16(tspec->surplus_bandwidth_allowance);
|
||||
wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
|
||||
val >> 13, 10000 * (val & 0x1fff) / 0x2000);
|
||||
|
||||
val = le_to_host16(tspec->nominal_msdu_size);
|
||||
if (val == 0) {
|
||||
wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
|
||||
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
/* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
|
||||
pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
|
||||
wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
|
||||
pps);
|
||||
|
||||
if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
|
||||
wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
|
||||
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
|
||||
(le_to_host32(tspec->minimum_phy_rate) / 1000000) +
|
||||
50 /* FIX: proper SIFS + ACK duration */;
|
||||
|
||||
/* unsigned binary number with an implicit binary point after the
|
||||
* leftmost 3 bits, i.e., 0x2000 = 1.0 */
|
||||
surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
|
||||
if (surplus <= 0x2000) {
|
||||
wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
|
||||
"greater than unity");
|
||||
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
medium_time = (u64) surplus * pps * duration / 0x2000;
|
||||
wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
|
||||
(unsigned long) medium_time);
|
||||
|
||||
/*
|
||||
* TODO: store list of granted (and still active) TSPECs and check
|
||||
* whether there is available medium time for this request. For now,
|
||||
* just refuse requests that would by themselves take very large
|
||||
* portion of the available bandwidth.
|
||||
*/
|
||||
if (medium_time > 750000) {
|
||||
wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
|
||||
"75%% of available bandwidth");
|
||||
return WMM_ADDTS_STATUS_REFUSED;
|
||||
}
|
||||
|
||||
/* Convert to 32 microseconds per second unit */
|
||||
tspec->medium_time = host_to_le16(medium_time / 32);
|
||||
|
||||
return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
|
||||
}
|
||||
|
||||
|
||||
static void wmm_addts_req(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
struct wmm_tspec_element *tspec, size_t len)
|
||||
{
|
||||
const u8 *end = ((const u8 *) mgmt) + len;
|
||||
int res;
|
||||
|
||||
if ((const u8 *) (tspec + 1) > end) {
|
||||
wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
|
||||
"from " MACSTR,
|
||||
mgmt->u.action.u.wmm_action.dialog_token,
|
||||
MAC2STR(mgmt->sa));
|
||||
|
||||
res = wmm_process_tspec(tspec);
|
||||
wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
|
||||
|
||||
wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
|
||||
mgmt->u.action.u.wmm_action.dialog_token, res);
|
||||
}
|
||||
|
||||
|
||||
void hostapd_wmm_action(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
int action_code;
|
||||
int left = len - IEEE80211_HDRLEN - 4;
|
||||
const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
|
||||
struct ieee802_11_elems elems;
|
||||
struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
|
||||
|
||||
/* check that the request comes from a valid station */
|
||||
if (!sta ||
|
||||
(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
|
||||
(WLAN_STA_ASSOC | WLAN_STA_WMM)) {
|
||||
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"wmm action received is not from associated wmm"
|
||||
" station");
|
||||
/* TODO: respond with action frame refused status code */
|
||||
return;
|
||||
}
|
||||
|
||||
if (left < 0)
|
||||
return; /* not a valid WMM Action frame */
|
||||
|
||||
/* extract the tspec info element */
|
||||
if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
|
||||
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"hostapd_wmm_action - could not parse wmm "
|
||||
"action");
|
||||
/* TODO: respond with action frame invalid parameters status
|
||||
* code */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!elems.wmm_tspec ||
|
||||
elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
|
||||
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"hostapd_wmm_action - missing or wrong length "
|
||||
"tspec");
|
||||
/* TODO: respond with action frame invalid parameters status
|
||||
* code */
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: check the request is for an AC with ACM set, if not, refuse
|
||||
* request */
|
||||
|
||||
action_code = mgmt->u.action.u.wmm_action.action_code;
|
||||
switch (action_code) {
|
||||
case WMM_ACTION_CODE_ADDTS_REQ:
|
||||
wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
|
||||
(elems.wmm_tspec - 2), len);
|
||||
return;
|
||||
#if 0
|
||||
/* TODO: needed for client implementation */
|
||||
case WMM_ACTION_CODE_ADDTS_RESP:
|
||||
wmm_setup_request(hapd, mgmt, len);
|
||||
return;
|
||||
/* TODO: handle station teardown requests */
|
||||
case WMM_ACTION_CODE_DELTS:
|
||||
wmm_teardown(hapd, mgmt, len);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"hostapd_wmm_action - unknown action code %d",
|
||||
action_code);
|
||||
}
|
23
hostapd-2.9/src/ap/wmm.h
Normal file
23
hostapd-2.9/src/ap/wmm.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* hostapd / WMM (Wi-Fi Multimedia)
|
||||
* Copyright 2002-2003, 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 WME_H
|
||||
#define WME_H
|
||||
|
||||
struct ieee80211_mgmt;
|
||||
struct wmm_tspec_element;
|
||||
|
||||
u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid);
|
||||
int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid,
|
||||
size_t len);
|
||||
void hostapd_wmm_action(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len);
|
||||
int wmm_process_tspec(struct wmm_tspec_element *tspec);
|
||||
|
||||
#endif /* WME_H */
|
846
hostapd-2.9/src/ap/wnm_ap.c
Normal file
846
hostapd-2.9/src/ap/wnm_ap.c
Normal file
|
@ -0,0 +1,846 @@
|
|||
/*
|
||||
* hostapd - WNM
|
||||
* Copyright (c) 2011-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 "common/ieee802_11_defs.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "common/ocv.h"
|
||||
#include "ap/hostapd.h"
|
||||
#include "ap/sta_info.h"
|
||||
#include "ap/ap_config.h"
|
||||
#include "ap/ap_drv_ops.h"
|
||||
#include "ap/wpa_auth.h"
|
||||
#include "mbo_ap.h"
|
||||
#include "wnm_ap.h"
|
||||
|
||||
#define MAX_TFS_IE_LEN 1024
|
||||
|
||||
|
||||
/* get the TFS IE from driver */
|
||||
static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 *buf, u16 *buf_len, enum wnm_oper oper)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
|
||||
|
||||
return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
|
||||
}
|
||||
|
||||
|
||||
/* set the TFS IE to driver */
|
||||
static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 *buf, u16 *buf_len, enum wnm_oper oper)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
|
||||
|
||||
return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
|
||||
}
|
||||
|
||||
|
||||
/* MLME-SLEEPMODE.response */
|
||||
static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
|
||||
const u8 *addr, u8 dialog_token,
|
||||
u8 action_type, u16 intval)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
int res;
|
||||
size_t len;
|
||||
size_t gtk_elem_len = 0;
|
||||
size_t igtk_elem_len = 0;
|
||||
struct wnm_sleep_element wnmsleep_ie;
|
||||
u8 *wnmtfs_ie, *oci_ie;
|
||||
u8 wnmsleep_ie_len, oci_ie_len;
|
||||
u16 wnmtfs_ie_len;
|
||||
u8 *pos;
|
||||
struct sta_info *sta;
|
||||
enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
|
||||
WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (sta == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* WNM-Sleep Mode IE */
|
||||
os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
|
||||
wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
|
||||
wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
|
||||
wnmsleep_ie.len = wnmsleep_ie_len - 2;
|
||||
wnmsleep_ie.action_type = action_type;
|
||||
wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
|
||||
wnmsleep_ie.intval = host_to_le16(intval);
|
||||
|
||||
/* TFS IE(s) */
|
||||
wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
|
||||
if (wnmtfs_ie == NULL)
|
||||
return -1;
|
||||
if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
|
||||
tfs_oper)) {
|
||||
wnmtfs_ie_len = 0;
|
||||
os_free(wnmtfs_ie);
|
||||
wnmtfs_ie = NULL;
|
||||
}
|
||||
|
||||
oci_ie = NULL;
|
||||
oci_ie_len = 0;
|
||||
#ifdef CONFIG_OCV
|
||||
if (action_type == WNM_SLEEP_MODE_EXIT &&
|
||||
wpa_auth_uses_ocv(sta->wpa_sm)) {
|
||||
struct wpa_channel_info ci;
|
||||
|
||||
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
|
||||
wpa_printf(MSG_WARNING,
|
||||
"Failed to get channel info for OCI element in WNM-Sleep Mode frame");
|
||||
os_free(wnmtfs_ie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oci_ie_len = OCV_OCI_EXTENDED_LEN;
|
||||
oci_ie = os_zalloc(oci_ie_len);
|
||||
if (!oci_ie) {
|
||||
wpa_printf(MSG_WARNING,
|
||||
"Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
|
||||
os_free(wnmtfs_ie);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
|
||||
os_free(wnmtfs_ie);
|
||||
os_free(oci_ie);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_OCV */
|
||||
|
||||
#define MAX_GTK_SUBELEM_LEN 45
|
||||
#define MAX_IGTK_SUBELEM_LEN 26
|
||||
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
|
||||
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
|
||||
oci_ie_len);
|
||||
if (mgmt == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
|
||||
"WNM-Sleep Response action frame");
|
||||
res = -1;
|
||||
goto fail;
|
||||
}
|
||||
os_memcpy(mgmt->da, addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
mgmt->u.action.category = WLAN_ACTION_WNM;
|
||||
mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
|
||||
mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
|
||||
pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
|
||||
/* add key data if MFP is enabled */
|
||||
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
|
||||
hapd->conf->wnm_sleep_mode_no_keys ||
|
||||
action_type != WNM_SLEEP_MODE_EXIT) {
|
||||
mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
|
||||
} else {
|
||||
gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
|
||||
pos += gtk_elem_len;
|
||||
wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
|
||||
(int) gtk_elem_len);
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
igtk_elem_len = res;
|
||||
pos += igtk_elem_len;
|
||||
wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
|
||||
(int) igtk_elem_len);
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
|
||||
WPA_PUT_LE16((u8 *)
|
||||
&mgmt->u.action.u.wnm_sleep_resp.keydata_len,
|
||||
gtk_elem_len + igtk_elem_len);
|
||||
}
|
||||
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
|
||||
/* copy TFS IE here */
|
||||
pos += wnmsleep_ie_len;
|
||||
if (wnmtfs_ie) {
|
||||
os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
|
||||
pos += wnmtfs_ie_len;
|
||||
}
|
||||
#ifdef CONFIG_OCV
|
||||
/* copy OCV OCI here */
|
||||
if (oci_ie_len > 0)
|
||||
os_memcpy(pos, oci_ie, oci_ie_len);
|
||||
#endif /* CONFIG_OCV */
|
||||
|
||||
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
|
||||
igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
|
||||
|
||||
/* In driver, response frame should be forced to sent when STA is in
|
||||
* PS mode */
|
||||
res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
|
||||
mgmt->da, &mgmt->u.action.category, len);
|
||||
|
||||
if (!res) {
|
||||
wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
|
||||
"frame");
|
||||
|
||||
/* when entering wnmsleep
|
||||
* 1. pause the node in driver
|
||||
* 2. mark the node so that AP won't update GTK/IGTK during
|
||||
* WNM Sleep
|
||||
*/
|
||||
if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
|
||||
wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
|
||||
sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
|
||||
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
|
||||
addr, NULL, NULL);
|
||||
wpa_set_wnmsleep(sta->wpa_sm, 1);
|
||||
}
|
||||
/* when exiting wnmsleep
|
||||
* 1. unmark the node
|
||||
* 2. start GTK/IGTK update if MFP is not used
|
||||
* 3. unpause the node in driver
|
||||
*/
|
||||
if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
|
||||
wnmsleep_ie.status ==
|
||||
WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
|
||||
wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
|
||||
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
|
||||
wpa_set_wnmsleep(sta->wpa_sm, 0);
|
||||
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
|
||||
addr, NULL, NULL);
|
||||
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
|
||||
hapd->conf->wnm_sleep_mode_no_keys)
|
||||
wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
|
||||
}
|
||||
} else
|
||||
wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
|
||||
|
||||
#undef MAX_GTK_SUBELEM_LEN
|
||||
#undef MAX_IGTK_SUBELEM_LEN
|
||||
fail:
|
||||
os_free(wnmtfs_ie);
|
||||
os_free(oci_ie);
|
||||
os_free(mgmt);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *frm, int len)
|
||||
{
|
||||
/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
|
||||
const u8 *pos = frm;
|
||||
u8 dialog_token;
|
||||
struct wnm_sleep_element *wnmsleep_ie = NULL;
|
||||
/* multiple TFS Req IE (assuming consecutive) */
|
||||
u8 *tfsreq_ie_start = NULL;
|
||||
u8 *tfsreq_ie_end = NULL;
|
||||
u16 tfsreq_ie_len = 0;
|
||||
#ifdef CONFIG_OCV
|
||||
struct sta_info *sta;
|
||||
const u8 *oci_ie = NULL;
|
||||
u8 oci_ie_len = 0;
|
||||
#endif /* CONFIG_OCV */
|
||||
|
||||
if (!hapd->conf->wnm_sleep_mode) {
|
||||
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
|
||||
MACSTR " since WNM-Sleep Mode is disabled",
|
||||
MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Ignore too short WNM-Sleep Mode Request from "
|
||||
MACSTR, MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
dialog_token = *pos++;
|
||||
while (pos + 1 < frm + len) {
|
||||
u8 ie_len = pos[1];
|
||||
if (pos + 2 + ie_len > frm + len)
|
||||
break;
|
||||
if (*pos == WLAN_EID_WNMSLEEP &&
|
||||
ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
|
||||
wnmsleep_ie = (struct wnm_sleep_element *) pos;
|
||||
else if (*pos == WLAN_EID_TFS_REQ) {
|
||||
if (!tfsreq_ie_start)
|
||||
tfsreq_ie_start = (u8 *) pos;
|
||||
tfsreq_ie_end = (u8 *) pos;
|
||||
#ifdef CONFIG_OCV
|
||||
} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
|
||||
pos[2] == WLAN_EID_EXT_OCV_OCI) {
|
||||
oci_ie = pos + 3;
|
||||
oci_ie_len = ie_len - 1;
|
||||
#endif /* CONFIG_OCV */
|
||||
} else
|
||||
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
|
||||
*pos);
|
||||
pos += ie_len + 2;
|
||||
}
|
||||
|
||||
if (!wnmsleep_ie) {
|
||||
wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OCV
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
|
||||
sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
|
||||
struct wpa_channel_info ci;
|
||||
|
||||
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
|
||||
wpa_printf(MSG_WARNING,
|
||||
"Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
|
||||
channel_width_to_int(ci.chanwidth),
|
||||
ci.seg1_idx) != 0) {
|
||||
wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_OCV */
|
||||
|
||||
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
|
||||
tfsreq_ie_start && tfsreq_ie_end &&
|
||||
tfsreq_ie_end - tfsreq_ie_start >= 0) {
|
||||
tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
|
||||
tfsreq_ie_start;
|
||||
wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
|
||||
/* pass the TFS Req IE(s) to driver for processing */
|
||||
if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
|
||||
&tfsreq_ie_len,
|
||||
WNM_SLEEP_TFS_REQ_IE_SET))
|
||||
wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
|
||||
}
|
||||
|
||||
ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
|
||||
wnmsleep_ie->action_type,
|
||||
le_to_host16(wnmsleep_ie->intval));
|
||||
|
||||
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
|
||||
/* clear the tfs after sending the resp frame */
|
||||
ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
|
||||
&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
|
||||
const u8 *addr,
|
||||
u8 dialog_token)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
size_t len;
|
||||
u8 *pos;
|
||||
int res;
|
||||
|
||||
mgmt = os_zalloc(sizeof(*mgmt));
|
||||
if (mgmt == NULL)
|
||||
return -1;
|
||||
os_memcpy(mgmt->da, addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
mgmt->u.action.category = WLAN_ACTION_WNM;
|
||||
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
|
||||
mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
|
||||
mgmt->u.action.u.bss_tm_req.req_mode = 0;
|
||||
mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
|
||||
mgmt->u.action.u.bss_tm_req.validity_interval = 1;
|
||||
pos = mgmt->u.action.u.bss_tm_req.variable;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
|
||||
MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
|
||||
"validity_interval=%u",
|
||||
MAC2STR(addr), dialog_token,
|
||||
mgmt->u.action.u.bss_tm_req.req_mode,
|
||||
le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
|
||||
mgmt->u.action.u.bss_tm_req.validity_interval);
|
||||
|
||||
len = pos - &mgmt->u.action.category;
|
||||
res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
|
||||
mgmt->da, &mgmt->u.action.category, len);
|
||||
os_free(mgmt);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *frm,
|
||||
size_t len)
|
||||
{
|
||||
u8 dialog_token, reason;
|
||||
const u8 *pos, *end;
|
||||
int enabled = hapd->conf->bss_transition;
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
if (hapd->conf->mbo_enabled)
|
||||
enabled = 1;
|
||||
#endif /* CONFIG_MBO */
|
||||
if (!enabled) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Ignore BSS Transition Management Query from "
|
||||
MACSTR
|
||||
" since BSS Transition Management is disabled",
|
||||
MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 2) {
|
||||
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
|
||||
MACSTR, MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
pos = frm;
|
||||
end = pos + len;
|
||||
dialog_token = *pos++;
|
||||
reason = *pos++;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
|
||||
MACSTR " dialog_token=%u reason=%u",
|
||||
MAC2STR(addr), dialog_token, reason);
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
|
||||
pos, end - pos);
|
||||
|
||||
ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
|
||||
}
|
||||
|
||||
|
||||
void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
struct sta_info *sta = timeout_ctx;
|
||||
|
||||
if (sta->agreed_to_steer) {
|
||||
wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
|
||||
hapd->conf->iface, MAC2STR(sta->addr));
|
||||
sta->agreed_to_steer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *frm,
|
||||
size_t len)
|
||||
{
|
||||
u8 dialog_token, status_code, bss_termination_delay;
|
||||
const u8 *pos, *end;
|
||||
int enabled = hapd->conf->bss_transition;
|
||||
struct sta_info *sta;
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
if (hapd->conf->mbo_enabled)
|
||||
enabled = 1;
|
||||
#endif /* CONFIG_MBO */
|
||||
if (!enabled) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Ignore BSS Transition Management Response from "
|
||||
MACSTR
|
||||
" since BSS Transition Management is disabled",
|
||||
MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 3) {
|
||||
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
|
||||
MACSTR, MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
pos = frm;
|
||||
end = pos + len;
|
||||
dialog_token = *pos++;
|
||||
status_code = *pos++;
|
||||
bss_termination_delay = *pos++;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
|
||||
MACSTR " dialog_token=%u status_code=%u "
|
||||
"bss_termination_delay=%u", MAC2STR(addr), dialog_token,
|
||||
status_code, bss_termination_delay);
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta) {
|
||||
wpa_printf(MSG_DEBUG, "Station " MACSTR
|
||||
" not found for received BSS TM Response",
|
||||
MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (status_code == WNM_BSS_TM_ACCEPT) {
|
||||
if (end - pos < ETH_ALEN) {
|
||||
wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
|
||||
return;
|
||||
}
|
||||
sta->agreed_to_steer = 1;
|
||||
eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
|
||||
eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
|
||||
hapd, sta);
|
||||
wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
|
||||
MAC2STR(pos));
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
|
||||
" status_code=%u bss_termination_delay=%u target_bssid="
|
||||
MACSTR,
|
||||
MAC2STR(addr), status_code, bss_termination_delay,
|
||||
MAC2STR(pos));
|
||||
pos += ETH_ALEN;
|
||||
} else {
|
||||
sta->agreed_to_steer = 0;
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
|
||||
" status_code=%u bss_termination_delay=%u",
|
||||
MAC2STR(addr), status_code, bss_termination_delay);
|
||||
}
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
|
||||
pos, end - pos);
|
||||
}
|
||||
|
||||
|
||||
static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
u8 dialog_token, type;
|
||||
|
||||
if (len < 2)
|
||||
return;
|
||||
dialog_token = *buf++;
|
||||
type = *buf++;
|
||||
len -= 2;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Received WNM Notification Request frame from "
|
||||
MACSTR " (dialog_token=%u type=%u)",
|
||||
MAC2STR(addr), dialog_token, type);
|
||||
wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
|
||||
buf, len);
|
||||
if (type == WLAN_EID_VENDOR_SPECIFIC)
|
||||
mbo_ap_wnm_notification_req(hapd, addr, buf, len);
|
||||
}
|
||||
|
||||
|
||||
static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
u8 dialog_token;
|
||||
char *hex;
|
||||
size_t hex_len;
|
||||
|
||||
if (!hapd->conf->coloc_intf_reporting) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Ignore unexpected Collocated Interference Report from "
|
||||
MACSTR, MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Ignore too short Collocated Interference Report from "
|
||||
MACSTR, MAC2STR(addr));
|
||||
return;
|
||||
}
|
||||
dialog_token = *buf++;
|
||||
len--;
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Received Collocated Interference Report frame from "
|
||||
MACSTR " (dialog_token=%u)",
|
||||
MAC2STR(addr), dialog_token);
|
||||
wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
|
||||
buf, len);
|
||||
|
||||
hex_len = 2 * len + 1;
|
||||
hex = os_malloc(hex_len);
|
||||
if (!hex)
|
||||
return;
|
||||
wpa_snprintf_hex(hex, hex_len, buf, len);
|
||||
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
|
||||
MAC2STR(addr), dialog_token, hex);
|
||||
os_free(hex);
|
||||
}
|
||||
|
||||
|
||||
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
u8 action;
|
||||
const u8 *payload;
|
||||
size_t plen;
|
||||
|
||||
if (len < IEEE80211_HDRLEN + 2)
|
||||
return -1;
|
||||
|
||||
payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
|
||||
action = *payload++;
|
||||
plen = len - IEEE80211_HDRLEN - 2;
|
||||
|
||||
switch (action) {
|
||||
case WNM_BSS_TRANS_MGMT_QUERY:
|
||||
ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
|
||||
plen);
|
||||
return 0;
|
||||
case WNM_BSS_TRANS_MGMT_RESP:
|
||||
ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
|
||||
plen);
|
||||
return 0;
|
||||
case WNM_SLEEP_MODE_REQ:
|
||||
ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
|
||||
return 0;
|
||||
case WNM_NOTIFICATION_REQ:
|
||||
ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
|
||||
plen);
|
||||
return 0;
|
||||
case WNM_COLLOCATED_INTERFERENCE_REPORT:
|
||||
ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
|
||||
plen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
|
||||
action, MAC2STR(mgmt->sa));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int disassoc_timer)
|
||||
{
|
||||
u8 buf[1000], *pos;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
|
||||
os_memset(buf, 0, sizeof(buf));
|
||||
mgmt = (struct ieee80211_mgmt *) buf;
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_ACTION_WNM;
|
||||
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
|
||||
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
|
||||
mgmt->u.action.u.bss_tm_req.req_mode =
|
||||
WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
|
||||
mgmt->u.action.u.bss_tm_req.disassoc_timer =
|
||||
host_to_le16(disassoc_timer);
|
||||
mgmt->u.action.u.bss_tm_req.validity_interval = 0;
|
||||
|
||||
pos = mgmt->u.action.u.bss_tm_req.variable;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
|
||||
MACSTR, disassoc_timer, MAC2STR(sta->addr));
|
||||
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
|
||||
"Management Request frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int disassoc_timer)
|
||||
{
|
||||
int timeout, beacon_int;
|
||||
|
||||
/*
|
||||
* Prevent STA from reconnecting using cached PMKSA to force
|
||||
* full authentication with the authentication server (which may
|
||||
* decide to reject the connection),
|
||||
*/
|
||||
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
|
||||
|
||||
beacon_int = hapd->iconf->beacon_int;
|
||||
if (beacon_int < 1)
|
||||
beacon_int = 100; /* best guess */
|
||||
/* Calculate timeout in ms based on beacon_int in TU */
|
||||
timeout = disassoc_timer * beacon_int * 128 / 125;
|
||||
wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
|
||||
" set to %d ms", MAC2STR(sta->addr), timeout);
|
||||
|
||||
sta->timeout_next = STA_DISASSOC_FROM_CLI;
|
||||
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
|
||||
eloop_register_timeout(timeout / 1000,
|
||||
timeout % 1000 * 1000,
|
||||
ap_handle_timer, hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, const char *url,
|
||||
int disassoc_timer)
|
||||
{
|
||||
u8 buf[1000], *pos;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
size_t url_len;
|
||||
|
||||
os_memset(buf, 0, sizeof(buf));
|
||||
mgmt = (struct ieee80211_mgmt *) buf;
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_ACTION_WNM;
|
||||
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
|
||||
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
|
||||
mgmt->u.action.u.bss_tm_req.req_mode =
|
||||
WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
|
||||
WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
|
||||
mgmt->u.action.u.bss_tm_req.disassoc_timer =
|
||||
host_to_le16(disassoc_timer);
|
||||
mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
|
||||
|
||||
pos = mgmt->u.action.u.bss_tm_req.variable;
|
||||
|
||||
/* Session Information URL */
|
||||
url_len = os_strlen(url);
|
||||
if (url_len > 255)
|
||||
return -1;
|
||||
*pos++ = url_len;
|
||||
os_memcpy(pos, url, url_len);
|
||||
pos += url_len;
|
||||
|
||||
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
|
||||
"Management Request frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (disassoc_timer) {
|
||||
/* send disassociation frame after time-out */
|
||||
set_disassoc_timer(hapd, sta, disassoc_timer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 req_mode, int disassoc_timer, u8 valid_int,
|
||||
const u8 *bss_term_dur, const char *url,
|
||||
const u8 *nei_rep, size_t nei_rep_len,
|
||||
const u8 *mbo_attrs, size_t mbo_len)
|
||||
{
|
||||
u8 *buf, *pos;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
size_t url_len;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
|
||||
MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
|
||||
MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
|
||||
buf = os_zalloc(1000 + nei_rep_len + mbo_len);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
mgmt = (struct ieee80211_mgmt *) buf;
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_ACTION_WNM;
|
||||
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
|
||||
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
|
||||
mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
|
||||
mgmt->u.action.u.bss_tm_req.disassoc_timer =
|
||||
host_to_le16(disassoc_timer);
|
||||
mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
|
||||
|
||||
pos = mgmt->u.action.u.bss_tm_req.variable;
|
||||
|
||||
if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
|
||||
bss_term_dur) {
|
||||
os_memcpy(pos, bss_term_dur, 12);
|
||||
pos += 12;
|
||||
}
|
||||
|
||||
if (url) {
|
||||
/* Session Information URL */
|
||||
url_len = os_strlen(url);
|
||||
if (url_len > 255) {
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pos++ = url_len;
|
||||
os_memcpy(pos, url, url_len);
|
||||
pos += url_len;
|
||||
}
|
||||
|
||||
if (nei_rep) {
|
||||
os_memcpy(pos, nei_rep, nei_rep_len);
|
||||
pos += nei_rep_len;
|
||||
}
|
||||
|
||||
if (mbo_len > 0) {
|
||||
pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
|
||||
mbo_len);
|
||||
}
|
||||
|
||||
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Failed to send BSS Transition Management Request frame");
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
os_free(buf);
|
||||
|
||||
if (disassoc_timer) {
|
||||
/* send disassociation frame after time-out */
|
||||
set_disassoc_timer(hapd, sta, disassoc_timer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
unsigned int auto_report, unsigned int timeout)
|
||||
{
|
||||
u8 buf[100], *pos;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
u8 dialog_token = 1;
|
||||
|
||||
if (auto_report > 3 || timeout > 63)
|
||||
return -1;
|
||||
os_memset(buf, 0, sizeof(buf));
|
||||
mgmt = (struct ieee80211_mgmt *) buf;
|
||||
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
||||
WLAN_FC_STYPE_ACTION);
|
||||
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
|
||||
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_ACTION_WNM;
|
||||
mgmt->u.action.u.coloc_intf_req.action =
|
||||
WNM_COLLOCATED_INTERFERENCE_REQ;
|
||||
mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
|
||||
mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
|
||||
pos = &mgmt->u.action.u.coloc_intf_req.req_info;
|
||||
pos++;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
|
||||
MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
|
||||
MAC2STR(sta->addr), dialog_token, auto_report, timeout);
|
||||
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Failed to send Collocated Interference Request frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
30
hostapd-2.9/src/ap/wnm_ap.h
Normal file
30
hostapd-2.9/src/ap/wnm_ap.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* IEEE 802.11v WNM related functions and structures
|
||||
* Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WNM_AP_H
|
||||
#define WNM_AP_H
|
||||
|
||||
struct sta_info;
|
||||
|
||||
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len);
|
||||
int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int disassoc_timer);
|
||||
int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, const char *url,
|
||||
int disassoc_timer);
|
||||
int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 req_mode, int disassoc_timer, u8 valid_int,
|
||||
const u8 *bss_term_dur, const char *url,
|
||||
const u8 *nei_rep, size_t nei_rep_len,
|
||||
const u8 *mbo_attrs, size_t mbo_len);
|
||||
void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
|
||||
int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
unsigned int auto_report, unsigned int timeout);
|
||||
|
||||
#endif /* WNM_AP_H */
|
5230
hostapd-2.9/src/ap/wpa_auth.c
Normal file
5230
hostapd-2.9/src/ap/wpa_auth.c
Normal file
File diff suppressed because it is too large
Load diff
496
hostapd-2.9/src/ap/wpa_auth.h
Normal file
496
hostapd-2.9/src/ap/wpa_auth.h
Normal file
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
|
||||
* Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WPA_AUTH_H
|
||||
#define WPA_AUTH_H
|
||||
|
||||
#include "common/defs.h"
|
||||
#include "common/eapol_common.h"
|
||||
#include "common/wpa_common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
|
||||
struct vlan_description;
|
||||
|
||||
#define MAX_OWN_IE_OVERRIDE 256
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(push, 1)
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
|
||||
*/
|
||||
struct ft_rrb_frame {
|
||||
u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
|
||||
u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
|
||||
le16 action_length; /* little endian length of action_frame */
|
||||
u8 ap_address[ETH_ALEN];
|
||||
/*
|
||||
* Followed by action_length bytes of FT Action frame (from Category
|
||||
* field to the end of Action Frame body.
|
||||
*/
|
||||
} STRUCT_PACKED;
|
||||
|
||||
#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
|
||||
|
||||
#define FT_PACKET_REQUEST 0
|
||||
#define FT_PACKET_RESPONSE 1
|
||||
|
||||
/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
|
||||
* use OUI Extended EtherType as the encapsulating format. */
|
||||
#define FT_PACKET_R0KH_R1KH_PULL 0x01
|
||||
#define FT_PACKET_R0KH_R1KH_RESP 0x02
|
||||
#define FT_PACKET_R0KH_R1KH_PUSH 0x03
|
||||
#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
|
||||
#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
|
||||
|
||||
/* packet layout
|
||||
* IEEE 802 extended OUI ethertype frame header
|
||||
* u16 authlen (little endian)
|
||||
* multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
|
||||
* multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
|
||||
* blocksize length)
|
||||
*
|
||||
* AES-SIV AAD;
|
||||
* source MAC address (6)
|
||||
* authenticated-only TLVs (authlen)
|
||||
* subtype (1; FT_PACKET_*)
|
||||
*/
|
||||
|
||||
#define FT_RRB_NONCE_LEN 16
|
||||
|
||||
#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */
|
||||
|
||||
#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */
|
||||
#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */
|
||||
#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */
|
||||
|
||||
#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */
|
||||
#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */
|
||||
#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */
|
||||
|
||||
#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */
|
||||
#define FT_RRB_PMK_R0 8 /* PMK_LEN */
|
||||
#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */
|
||||
#define FT_RRB_PMK_R1 10 /* PMK_LEN */
|
||||
|
||||
#define FT_RRB_PAIRWISE 11 /* le16 */
|
||||
#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */
|
||||
|
||||
#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
|
||||
#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */
|
||||
|
||||
#define FT_RRB_IDENTITY 15
|
||||
#define FT_RRB_RADIUS_CUI 16
|
||||
#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */
|
||||
|
||||
struct ft_rrb_tlv {
|
||||
le16 type;
|
||||
le16 len;
|
||||
/* followed by data of length len */
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct ft_rrb_seq {
|
||||
le32 dom;
|
||||
le32 seq;
|
||||
le32 ts;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
/* session TLVs:
|
||||
* required: PMK_R1, PMK_R1_NAME, PAIRWISE
|
||||
* optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
|
||||
* SESSION_TIMEOUT
|
||||
*
|
||||
* pull frame TLVs:
|
||||
* auth:
|
||||
* required: SEQ, NONCE, R0KH_ID, R1KH_ID
|
||||
* encrypted:
|
||||
* required: PMK_R0_NAME, S1KH_ID
|
||||
*
|
||||
* response frame TLVs:
|
||||
* auth:
|
||||
* required: SEQ, NONCE, R0KH_ID, R1KH_ID
|
||||
* encrypted:
|
||||
* required: S1KH_ID
|
||||
* optional: session TLVs
|
||||
*
|
||||
* push frame TLVs:
|
||||
* auth:
|
||||
* required: SEQ, R0KH_ID, R1KH_ID
|
||||
* encrypted:
|
||||
* required: S1KH_ID, PMK_R0_NAME, session TLVs
|
||||
*
|
||||
* sequence number request frame TLVs:
|
||||
* auth:
|
||||
* required: R0KH_ID, R1KH_ID, NONCE
|
||||
*
|
||||
* sequence number response frame TLVs:
|
||||
* auth:
|
||||
* required: SEQ, NONCE, R0KH_ID, R1KH_ID
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(pop)
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
|
||||
/* per STA state machine data */
|
||||
|
||||
struct wpa_authenticator;
|
||||
struct wpa_state_machine;
|
||||
struct rsn_pmksa_cache_entry;
|
||||
struct eapol_state_machine;
|
||||
struct ft_remote_seq;
|
||||
struct wpa_channel_info;
|
||||
|
||||
|
||||
struct ft_remote_r0kh {
|
||||
struct ft_remote_r0kh *next;
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 id[FT_R0KH_ID_MAX_LEN];
|
||||
size_t id_len;
|
||||
u8 key[32];
|
||||
struct ft_remote_seq *seq;
|
||||
};
|
||||
|
||||
|
||||
struct ft_remote_r1kh {
|
||||
struct ft_remote_r1kh *next;
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 id[FT_R1KH_ID_LEN];
|
||||
u8 key[32];
|
||||
struct ft_remote_seq *seq;
|
||||
};
|
||||
|
||||
|
||||
struct wpa_auth_config {
|
||||
int wpa;
|
||||
int wpa_key_mgmt;
|
||||
int wpa_pairwise;
|
||||
int wpa_group;
|
||||
int wpa_group_rekey;
|
||||
int wpa_strict_rekey;
|
||||
int wpa_gmk_rekey;
|
||||
int wpa_ptk_rekey;
|
||||
u32 wpa_group_update_count;
|
||||
u32 wpa_pairwise_update_count;
|
||||
int wpa_disable_eapol_key_retries;
|
||||
int rsn_pairwise;
|
||||
int rsn_preauth;
|
||||
int eapol_version;
|
||||
int wmm_enabled;
|
||||
int wmm_uapsd;
|
||||
int disable_pmksa_caching;
|
||||
int okc;
|
||||
int tx_status;
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
enum mfp_options ieee80211w;
|
||||
int group_mgmt_cipher;
|
||||
int sae_require_mfp;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
#ifdef CONFIG_OCV
|
||||
int ocv; /* Operating Channel Validation */
|
||||
#endif /* CONFIG_OCV */
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
u8 ssid[SSID_MAX_LEN];
|
||||
size_t ssid_len;
|
||||
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
|
||||
u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
|
||||
size_t r0_key_holder_len;
|
||||
u8 r1_key_holder[FT_R1KH_ID_LEN];
|
||||
u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
|
||||
int rkh_pos_timeout;
|
||||
int rkh_neg_timeout;
|
||||
int rkh_pull_timeout; /* ms */
|
||||
int rkh_pull_retries;
|
||||
int r1_max_key_lifetime;
|
||||
u32 reassociation_deadline;
|
||||
struct ft_remote_r0kh **r0kh_list;
|
||||
struct ft_remote_r1kh **r1kh_list;
|
||||
int pmk_r1_push;
|
||||
int ft_over_ds;
|
||||
int ft_psk_generate_local;
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
int disable_gtk;
|
||||
int ap_mlme;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
double corrupt_gtk_rekey_mic_probability;
|
||||
u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
|
||||
size_t own_ie_override_len;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
#ifdef CONFIG_P2P
|
||||
u8 ip_addr_go[4];
|
||||
u8 ip_addr_mask[4];
|
||||
u8 ip_addr_start[4];
|
||||
u8 ip_addr_end[4];
|
||||
#endif /* CONFIG_P2P */
|
||||
#ifdef CONFIG_FILS
|
||||
unsigned int fils_cache_id_set:1;
|
||||
u8 fils_cache_id[FILS_CACHE_ID_LEN];
|
||||
#endif /* CONFIG_FILS */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
|
||||
} logger_level;
|
||||
|
||||
typedef enum {
|
||||
WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
|
||||
WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
|
||||
WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
|
||||
} wpa_eapol_variable;
|
||||
|
||||
struct wpa_auth_callbacks {
|
||||
void (*logger)(void *ctx, const u8 *addr, logger_level level,
|
||||
const char *txt);
|
||||
void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
|
||||
int (*mic_failure_report)(void *ctx, const u8 *addr);
|
||||
void (*psk_failure_report)(void *ctx, const u8 *addr);
|
||||
void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
|
||||
int value);
|
||||
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
|
||||
const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
|
||||
const u8 *prev_psk, size_t *psk_len,
|
||||
int *vlan_id);
|
||||
int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
|
||||
int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
|
||||
const u8 *addr, int idx, u8 *key, size_t key_len);
|
||||
int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
|
||||
int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
|
||||
size_t data_len, int encrypt);
|
||||
int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
|
||||
void *ctx), void *cb_ctx);
|
||||
int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
|
||||
void *ctx), void *cb_ctx);
|
||||
int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
|
||||
size_t data_len);
|
||||
int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
|
||||
size_t data_len);
|
||||
int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
|
||||
int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
|
||||
int (*get_sta_tx_params)(void *ctx, const u8 *addr,
|
||||
int ap_max_chanwidth, int ap_seg1_idx,
|
||||
int *bandwidth, int *seg1_idx);
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
|
||||
int (*set_vlan)(void *ctx, const u8 *sta_addr,
|
||||
struct vlan_description *vlan);
|
||||
int (*get_vlan)(void *ctx, const u8 *sta_addr,
|
||||
struct vlan_description *vlan);
|
||||
int (*set_identity)(void *ctx, const u8 *sta_addr,
|
||||
const u8 *identity, size_t identity_len);
|
||||
size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
|
||||
int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
|
||||
const u8 *radius_cui, size_t radius_cui_len);
|
||||
size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
|
||||
void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
|
||||
int session_timeout);
|
||||
int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
|
||||
|
||||
int (*send_ft_action)(void *ctx, const u8 *dst,
|
||||
const u8 *data, size_t data_len);
|
||||
int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
|
||||
size_t tspec_ielen);
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
#ifdef CONFIG_MESH
|
||||
int (*start_ampe)(void *ctx, const u8 *sta_addr);
|
||||
#endif /* CONFIG_MESH */
|
||||
};
|
||||
|
||||
struct wpa_authenticator * wpa_init(const u8 *addr,
|
||||
struct wpa_auth_config *conf,
|
||||
const struct wpa_auth_callbacks *cb,
|
||||
void *cb_ctx);
|
||||
int wpa_init_keys(struct wpa_authenticator *wpa_auth);
|
||||
void wpa_deinit(struct wpa_authenticator *wpa_auth);
|
||||
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_auth_config *conf);
|
||||
|
||||
enum {
|
||||
WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
|
||||
WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
|
||||
WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
|
||||
WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID
|
||||
};
|
||||
|
||||
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_state_machine *sm, int freq,
|
||||
const u8 *wpa_ie, size_t wpa_ie_len,
|
||||
const u8 *mdie, size_t mdie_len,
|
||||
const u8 *owe_dh, size_t owe_dh_len);
|
||||
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_state_machine *sm,
|
||||
const u8 *osen_ie, size_t osen_ie_len);
|
||||
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
|
||||
void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
|
||||
int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
|
||||
struct wpa_state_machine *
|
||||
wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
|
||||
const u8 *p2p_dev_addr);
|
||||
int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_state_machine *sm);
|
||||
void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
|
||||
void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
|
||||
void wpa_receive(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_state_machine *sm,
|
||||
u8 *data, size_t data_len);
|
||||
enum wpa_event {
|
||||
WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
|
||||
WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
|
||||
};
|
||||
void wpa_remove_ptk(struct wpa_state_machine *sm);
|
||||
int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
|
||||
void wpa_auth_sm_notify(struct wpa_state_machine *sm);
|
||||
void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
|
||||
int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
|
||||
int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
|
||||
void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
|
||||
int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
|
||||
int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
|
||||
const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
|
||||
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
|
||||
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
|
||||
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
|
||||
int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
|
||||
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
|
||||
struct rsn_pmksa_cache_entry *entry);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
|
||||
void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
|
||||
const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
|
||||
size_t *len);
|
||||
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
|
||||
unsigned int pmk_len,
|
||||
int session_timeout, struct eapol_state_machine *eapol);
|
||||
int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
|
||||
const u8 *pmk, size_t len, const u8 *sta_addr,
|
||||
int session_timeout,
|
||||
struct eapol_state_machine *eapol);
|
||||
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
|
||||
const u8 *pmk, const u8 *pmkid);
|
||||
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
|
||||
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
|
||||
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
|
||||
int session_timeout, int akmp);
|
||||
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
|
||||
const u8 *sta_addr);
|
||||
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
|
||||
size_t len);
|
||||
void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
|
||||
int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
|
||||
char *buf, size_t len);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
|
||||
const u8 *pmkid, int expiration);
|
||||
int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
|
||||
struct rsn_pmksa_cache_entry *entry);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
|
||||
const u8 *pmkid);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
|
||||
const u8 *sta_addr, const u8 *pmkid);
|
||||
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
|
||||
struct wpa_state_machine *sm,
|
||||
struct wpa_authenticator *wpa_auth,
|
||||
u8 *pmkid, u8 *pmk);
|
||||
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
|
||||
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_state_machine *sm, int ack);
|
||||
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
|
||||
size_t max_len, int auth_alg,
|
||||
const u8 *req_ies, size_t req_ies_len);
|
||||
void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
|
||||
u16 auth_transaction, const u8 *ies, size_t ies_len,
|
||||
void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
|
||||
u16 auth_transaction, u16 resp,
|
||||
const u8 *ies, size_t ies_len),
|
||||
void *ctx);
|
||||
u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
|
||||
size_t ies_len);
|
||||
int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
|
||||
int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
|
||||
const u8 *data, size_t data_len);
|
||||
void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
|
||||
const u8 *dst_addr, u8 oui_suffix, const u8 *data,
|
||||
size_t data_len);
|
||||
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
|
||||
void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
|
||||
void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
|
||||
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
|
||||
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
|
||||
int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
|
||||
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
|
||||
|
||||
int wpa_auth_uses_sae(struct wpa_state_machine *sm);
|
||||
int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
|
||||
|
||||
int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
|
||||
|
||||
struct radius_das_attrs;
|
||||
int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
|
||||
struct radius_das_attrs *attr);
|
||||
void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
|
||||
|
||||
int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
|
||||
int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
|
||||
int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
||||
size_t pmk_len, const u8 *snonce, const u8 *anonce,
|
||||
const u8 *dhss, size_t dhss_len,
|
||||
struct wpabuf *g_sta, struct wpabuf *g_ap);
|
||||
int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
|
||||
const struct ieee80211_mgmt *mgmt, size_t frame_len,
|
||||
u8 *pos, size_t left);
|
||||
int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
|
||||
size_t current_len, size_t max_len,
|
||||
const struct wpabuf *hlp);
|
||||
int fils_set_tk(struct wpa_state_machine *sm);
|
||||
u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
|
||||
const u8 *fils_session,
|
||||
struct wpabuf *fils_hlp_resp);
|
||||
const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
|
||||
const u8 *ies, size_t ies_len,
|
||||
const u8 *fils_session);
|
||||
int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
|
||||
size_t ies_len);
|
||||
|
||||
int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
|
||||
int ap_seg1_idx, int *bandwidth, int *seg1_idx);
|
||||
|
||||
int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
|
||||
u8 *buf, size_t len);
|
||||
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
|
||||
u8 *fils_anonce, u8 *fils_snonce,
|
||||
u8 *fils_kek, size_t *fils_kek_len);
|
||||
void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
|
||||
size_t pmk_len, const u8 *pmkid);
|
||||
u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
|
||||
u8 *pos, size_t max_len,
|
||||
const u8 *req_ies, size_t req_ies_len);
|
||||
u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
|
||||
u8 *pos, size_t max_len,
|
||||
const u8 *req_ies, size_t req_ies_len);
|
||||
void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
|
||||
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
|
||||
|
||||
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
|
||||
void (*cb)(void *ctx1, void *ctx2),
|
||||
void *ctx1, void *ctx2);
|
||||
int wpa_auth_resend_m3(struct wpa_state_machine *sm,
|
||||
void (*cb)(void *ctx1, void *ctx2),
|
||||
void *ctx1, void *ctx2);
|
||||
int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
|
||||
void (*cb)(void *ctx1, void *ctx2),
|
||||
void *ctx1, void *ctx2);
|
||||
int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
|
||||
void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
|
||||
|
||||
#endif /* WPA_AUTH_H */
|
4585
hostapd-2.9/src/ap/wpa_auth_ft.c
Normal file
4585
hostapd-2.9/src/ap/wpa_auth_ft.c
Normal file
File diff suppressed because it is too large
Load diff
1403
hostapd-2.9/src/ap/wpa_auth_glue.c
Normal file
1403
hostapd-2.9/src/ap/wpa_auth_glue.c
Normal file
File diff suppressed because it is too large
Load diff
16
hostapd-2.9/src/ap/wpa_auth_glue.h
Normal file
16
hostapd-2.9/src/ap/wpa_auth_glue.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* hostapd / WPA authenticator glue code
|
||||
* 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 WPA_AUTH_GLUE_H
|
||||
#define WPA_AUTH_GLUE_H
|
||||
|
||||
int hostapd_setup_wpa(struct hostapd_data *hapd);
|
||||
void hostapd_reconfig_wpa(struct hostapd_data *hapd);
|
||||
void hostapd_deinit_wpa(struct hostapd_data *hapd);
|
||||
|
||||
#endif /* WPA_AUTH_GLUE_H */
|
303
hostapd-2.9/src/ap/wpa_auth_i.h
Normal file
303
hostapd-2.9/src/ap/wpa_auth_i.h
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
|
||||
* Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WPA_AUTH_I_H
|
||||
#define WPA_AUTH_I_H
|
||||
|
||||
#include "utils/list.h"
|
||||
|
||||
/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
|
||||
#define RSNA_MAX_EAPOL_RETRIES 4
|
||||
|
||||
struct wpa_group;
|
||||
|
||||
struct wpa_state_machine {
|
||||
struct wpa_authenticator *wpa_auth;
|
||||
struct wpa_group *group;
|
||||
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 p2p_dev_addr[ETH_ALEN];
|
||||
u16 auth_alg;
|
||||
|
||||
enum {
|
||||
WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
|
||||
WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
|
||||
WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
|
||||
WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
|
||||
WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
|
||||
} wpa_ptk_state;
|
||||
|
||||
enum {
|
||||
WPA_PTK_GROUP_IDLE = 0,
|
||||
WPA_PTK_GROUP_REKEYNEGOTIATING,
|
||||
WPA_PTK_GROUP_REKEYESTABLISHED,
|
||||
WPA_PTK_GROUP_KEYERROR
|
||||
} wpa_ptk_group_state;
|
||||
|
||||
Boolean Init;
|
||||
Boolean DeauthenticationRequest;
|
||||
Boolean AuthenticationRequest;
|
||||
Boolean ReAuthenticationRequest;
|
||||
Boolean Disconnect;
|
||||
u16 disconnect_reason; /* specific reason code to use with Disconnect */
|
||||
u32 TimeoutCtr;
|
||||
u32 GTimeoutCtr;
|
||||
Boolean TimeoutEvt;
|
||||
Boolean EAPOLKeyReceived;
|
||||
Boolean EAPOLKeyPairwise;
|
||||
Boolean EAPOLKeyRequest;
|
||||
Boolean MICVerified;
|
||||
Boolean GUpdateStationKeys;
|
||||
u8 ANonce[WPA_NONCE_LEN];
|
||||
u8 SNonce[WPA_NONCE_LEN];
|
||||
u8 alt_SNonce[WPA_NONCE_LEN];
|
||||
u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
|
||||
u8 PMK[PMK_LEN_MAX];
|
||||
unsigned int pmk_len;
|
||||
u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
|
||||
struct wpa_ptk PTK;
|
||||
Boolean PTK_valid;
|
||||
Boolean pairwise_set;
|
||||
Boolean tk_already_set;
|
||||
int keycount;
|
||||
Boolean Pair;
|
||||
struct wpa_key_replay_counter {
|
||||
u8 counter[WPA_REPLAY_COUNTER_LEN];
|
||||
Boolean valid;
|
||||
} key_replay[RSNA_MAX_EAPOL_RETRIES],
|
||||
prev_key_replay[RSNA_MAX_EAPOL_RETRIES];
|
||||
Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
|
||||
Boolean PTKRequest; /* not in IEEE 802.11i state machine */
|
||||
Boolean has_GTK;
|
||||
Boolean PtkGroupInit; /* init request for PTK Group state machine */
|
||||
|
||||
u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
|
||||
size_t last_rx_eapol_key_len;
|
||||
|
||||
unsigned int changed:1;
|
||||
unsigned int in_step_loop:1;
|
||||
unsigned int pending_deinit:1;
|
||||
unsigned int started:1;
|
||||
unsigned int mgmt_frame_prot:1;
|
||||
unsigned int rx_eapol_key_secure:1;
|
||||
unsigned int update_snonce:1;
|
||||
unsigned int alt_snonce_valid:1;
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
unsigned int ft_completed:1;
|
||||
unsigned int pmk_r1_name_valid:1;
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
unsigned int is_wnmsleep:1;
|
||||
unsigned int pmkid_set:1;
|
||||
#ifdef CONFIG_OCV
|
||||
unsigned int ocv_enabled:1;
|
||||
#endif /* CONFIG_OCV */
|
||||
|
||||
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
|
||||
int req_replay_counter_used;
|
||||
|
||||
u8 *wpa_ie;
|
||||
size_t wpa_ie_len;
|
||||
|
||||
enum {
|
||||
WPA_VERSION_NO_WPA = 0 /* WPA not used */,
|
||||
WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
|
||||
WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
|
||||
} wpa;
|
||||
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
|
||||
int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
|
||||
struct rsn_pmksa_cache_entry *pmksa;
|
||||
|
||||
u32 dot11RSNAStatsTKIPLocalMICFailures;
|
||||
u32 dot11RSNAStatsTKIPRemoteMICFailures;
|
||||
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
|
||||
* first 384 bits of MSK */
|
||||
size_t xxkey_len;
|
||||
u8 pmk_r1[PMK_LEN_MAX];
|
||||
unsigned int pmk_r1_len;
|
||||
u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
|
||||
* Request */
|
||||
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
|
||||
size_t r0kh_id_len;
|
||||
u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
|
||||
* message 2/4 */
|
||||
u8 *assoc_resp_ftie;
|
||||
|
||||
void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
|
||||
u16 auth_transaction, u16 status,
|
||||
const u8 *ies, size_t ies_len);
|
||||
void *ft_pending_cb_ctx;
|
||||
struct wpabuf *ft_pending_req_ies;
|
||||
u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
|
||||
u8 ft_pending_auth_transaction;
|
||||
u8 ft_pending_current_ap[ETH_ALEN];
|
||||
int ft_pending_pull_left_retries;
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
|
||||
int pending_1_of_4_timeout;
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
u8 ip_addr[4];
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
|
||||
u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
|
||||
size_t fils_key_auth_len;
|
||||
unsigned int fils_completed:1;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
#ifdef CONFIG_DPP2
|
||||
struct wpabuf *dpp_z;
|
||||
#endif /* CONFIG_DPP2 */
|
||||
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
void (*eapol_status_cb)(void *ctx1, void *ctx2);
|
||||
void *eapol_status_cb_ctx1;
|
||||
void *eapol_status_cb_ctx2;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
};
|
||||
|
||||
|
||||
/* per group key state machine data */
|
||||
struct wpa_group {
|
||||
struct wpa_group *next;
|
||||
int vlan_id;
|
||||
|
||||
Boolean GInit;
|
||||
int GKeyDoneStations;
|
||||
Boolean GTKReKey;
|
||||
int GTK_len;
|
||||
int GN, GM;
|
||||
Boolean GTKAuthenticator;
|
||||
u8 Counter[WPA_NONCE_LEN];
|
||||
|
||||
enum {
|
||||
WPA_GROUP_GTK_INIT = 0,
|
||||
WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
|
||||
WPA_GROUP_FATAL_FAILURE
|
||||
} wpa_group_state;
|
||||
|
||||
u8 GMK[WPA_GMK_LEN];
|
||||
u8 GTK[2][WPA_GTK_MAX_LEN];
|
||||
u8 GNonce[WPA_NONCE_LEN];
|
||||
Boolean changed;
|
||||
Boolean first_sta_seen;
|
||||
Boolean reject_4way_hs_for_entropy;
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
u8 IGTK[2][WPA_IGTK_MAX_LEN];
|
||||
int GN_igtk, GM_igtk;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
/* Number of references except those in struct wpa_group->next */
|
||||
unsigned int references;
|
||||
unsigned int num_setup_iface;
|
||||
};
|
||||
|
||||
|
||||
struct wpa_ft_pmk_cache;
|
||||
|
||||
/* per authenticator data */
|
||||
struct wpa_authenticator {
|
||||
struct wpa_group *group;
|
||||
|
||||
unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
|
||||
u32 dot11RSNAAuthenticationSuiteSelected;
|
||||
u32 dot11RSNAPairwiseCipherSelected;
|
||||
u32 dot11RSNAGroupCipherSelected;
|
||||
u8 dot11RSNAPMKIDUsed[PMKID_LEN];
|
||||
u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */
|
||||
u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */
|
||||
u32 dot11RSNAGroupCipherRequested; /* FIX: update */
|
||||
unsigned int dot11RSNATKIPCounterMeasuresInvoked;
|
||||
unsigned int dot11RSNA4WayHandshakeFailures;
|
||||
|
||||
struct wpa_auth_config conf;
|
||||
const struct wpa_auth_callbacks *cb;
|
||||
void *cb_ctx;
|
||||
|
||||
u8 *wpa_ie;
|
||||
size_t wpa_ie_len;
|
||||
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
struct rsn_pmksa_cache *pmksa;
|
||||
struct wpa_ft_pmk_cache *ft_pmk_cache;
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
struct bitfield *ip_pool;
|
||||
#endif /* CONFIG_P2P */
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
|
||||
#define FT_REMOTE_SEQ_BACKLOG 16
|
||||
struct ft_remote_seq_rx {
|
||||
u32 dom;
|
||||
struct os_reltime time_offset; /* local time - offset = remote time */
|
||||
|
||||
/* accepted sequence numbers: (offset ... offset + 0x40000000]
|
||||
* (except those in last)
|
||||
* dropped sequence numbers: (offset - 0x40000000 ... offset]
|
||||
* all others trigger SEQ_REQ message (except first message)
|
||||
*/
|
||||
u32 last[FT_REMOTE_SEQ_BACKLOG];
|
||||
unsigned int num_last;
|
||||
u32 offsetidx;
|
||||
|
||||
struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
|
||||
};
|
||||
|
||||
struct ft_remote_seq_tx {
|
||||
u32 dom; /* non zero if initialized */
|
||||
u32 seq;
|
||||
};
|
||||
|
||||
struct ft_remote_seq {
|
||||
struct ft_remote_seq_rx rx;
|
||||
struct ft_remote_seq_tx tx;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
|
||||
|
||||
int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
|
||||
const u8 *pmkid);
|
||||
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
|
||||
logger_level level, const char *txt);
|
||||
void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
|
||||
logger_level level, const char *fmt, ...);
|
||||
void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
|
||||
struct wpa_state_machine *sm, int key_info,
|
||||
const u8 *key_rsc, const u8 *nonce,
|
||||
const u8 *kde, size_t kde_len,
|
||||
int keyidx, int encr, int force_version);
|
||||
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
|
||||
int (*cb)(struct wpa_state_machine *sm, void *ctx),
|
||||
void *cb_ctx);
|
||||
int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
|
||||
int (*cb)(struct wpa_authenticator *a, void *ctx),
|
||||
void *cb_ctx);
|
||||
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
|
||||
int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
|
||||
const u8 *r0kh_id, size_t r0kh_id_len,
|
||||
const u8 *anonce, const u8 *snonce,
|
||||
u8 *buf, size_t len, const u8 *subelem,
|
||||
size_t subelem_len);
|
||||
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk);
|
||||
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
|
||||
void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
|
||||
void wpa_ft_install_ptk(struct wpa_state_machine *sm);
|
||||
int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
|
||||
const u8 *pmk_r0_name);
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
|
||||
#endif /* WPA_AUTH_I_H */
|
1198
hostapd-2.9/src/ap/wpa_auth_ie.c
Normal file
1198
hostapd-2.9/src/ap/wpa_auth_ie.c
Normal file
File diff suppressed because it is too large
Load diff
51
hostapd-2.9/src/ap/wpa_auth_ie.h
Normal file
51
hostapd-2.9/src/ap/wpa_auth_ie.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* hostapd - WPA/RSN IE and KDE definitions
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WPA_AUTH_IE_H
|
||||
#define WPA_AUTH_IE_H
|
||||
|
||||
struct wpa_eapol_ie_parse {
|
||||
const u8 *wpa_ie;
|
||||
size_t wpa_ie_len;
|
||||
const u8 *rsn_ie;
|
||||
size_t rsn_ie_len;
|
||||
const u8 *pmkid;
|
||||
const u8 *gtk;
|
||||
size_t gtk_len;
|
||||
const u8 *mac_addr;
|
||||
size_t mac_addr_len;
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
const u8 *igtk;
|
||||
size_t igtk_len;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
#ifdef CONFIG_IEEE80211R_AP
|
||||
const u8 *mdie;
|
||||
size_t mdie_len;
|
||||
const u8 *ftie;
|
||||
size_t ftie_len;
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
#ifdef CONFIG_P2P
|
||||
const u8 *ip_addr_req;
|
||||
const u8 *ip_addr_alloc;
|
||||
#endif /* CONFIG_P2P */
|
||||
#ifdef CONFIG_OCV
|
||||
const u8 *oci;
|
||||
size_t oci_len;
|
||||
#endif /* CONFIG_OCV */
|
||||
|
||||
const u8 *osen;
|
||||
size_t osen_len;
|
||||
};
|
||||
|
||||
int wpa_parse_kde_ies(const u8 *buf, size_t len,
|
||||
struct wpa_eapol_ie_parse *ie);
|
||||
u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
|
||||
const u8 *data2, size_t data2_len);
|
||||
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
|
||||
|
||||
#endif /* WPA_AUTH_IE_H */
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue