rtl8188eu: Add hostapd-2.9 modified for the rtl871x driver

Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
Larry Finger 2022-04-17 13:00:45 -05:00
parent 13531f3844
commit 903b6fcb82
644 changed files with 378143 additions and 0 deletions

12
hostapd-2.9/src/Makefile Normal file
View file

@ -0,0 +1,12 @@
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae 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

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

View file

@ -0,0 +1,547 @@
/*
* hostapd / RADIUS Accounting
* Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "ap_config.h"
#include "sta_info.h"
#include "ap_drv_ops.h"
#include "accounting.h"
/* Default interval in seconds for polling TX/RX octets from the driver if
* STA is not using interim accounting. This detects wrap arounds for
* input/output octets and updates Acct-{Input,Output}-Gigawords. */
#define ACCT_DEFAULT_UPDATE_INTERVAL 300
static void accounting_sta_interim(struct hostapd_data *hapd,
struct sta_info *sta);
static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
struct sta_info *sta,
int status_type)
{
struct radius_msg *msg;
char buf[128];
u8 *val;
size_t len;
int i;
struct wpabuf *b;
struct os_time now;
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
radius_client_get_id(hapd->radius));
if (msg == NULL) {
wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
return NULL;
}
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
status_type)) {
wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
goto fail;
}
if (sta) {
if (!hostapd_config_get_radius_attr(
hapd->conf->radius_acct_req_attr,
RADIUS_ATTR_ACCT_AUTHENTIC) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
hapd->conf->ieee802_1x ?
RADIUS_ACCT_AUTHENTIC_RADIUS :
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
goto fail;
}
/* Use 802.1X identity if available */
val = ieee802_1x_get_identity(sta->eapol_sm, &len);
/* Use RADIUS ACL identity if 802.1X provides no identity */
if (!val && sta->identity) {
val = (u8 *) sta->identity;
len = os_strlen(sta->identity);
}
/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
* identity */
if (!val) {
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
MAC2STR(sta->addr));
val = (u8 *) buf;
len = os_strlen(buf);
}
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
len)) {
wpa_printf(MSG_INFO, "Could not add User-Name");
goto fail;
}
}
if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
msg) < 0)
goto fail;
if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
goto fail;
if (sta) {
for (i = 0; ; i++) {
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
i);
if (val == NULL)
break;
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
val, len)) {
wpa_printf(MSG_INFO, "Could not add Class");
goto fail;
}
}
b = ieee802_1x_get_radius_cui(sta->eapol_sm);
if (b &&
!radius_msg_add_attr(msg,
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
wpabuf_head(b), wpabuf_len(b))) {
wpa_printf(MSG_ERROR, "Could not add CUI");
goto fail;
}
if (!b && sta->radius_cui &&
!radius_msg_add_attr(msg,
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
(u8 *) sta->radius_cui,
os_strlen(sta->radius_cui))) {
wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
goto fail;
}
if (sta->ipaddr &&
!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_FRAMED_IP_ADDRESS,
be_to_host32(sta->ipaddr))) {
wpa_printf(MSG_ERROR,
"Could not add Framed-IP-Address");
goto fail;
}
}
os_get_time(&now);
if (now.sec > 1000000000 &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
now.sec)) {
wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
goto fail;
}
/*
* Add Acct-Delay-Time with zero value for the first transmission. This
* will be updated within radius_client.c when retransmitting the frame.
*/
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
goto fail;
}
return msg;
fail:
radius_msg_free(msg);
return NULL;
}
static int accounting_sta_update_stats(struct hostapd_data *hapd,
struct sta_info *sta,
struct hostap_sta_driver_data *data)
{
if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
return -1;
if (!data->bytes_64bit) {
/* Extend 32-bit counters from the driver to 64-bit counters */
if (sta->last_rx_bytes_lo > data->rx_bytes)
sta->last_rx_bytes_hi++;
sta->last_rx_bytes_lo = data->rx_bytes;
if (sta->last_tx_bytes_lo > data->tx_bytes)
sta->last_tx_bytes_hi++;
sta->last_tx_bytes_lo = data->tx_bytes;
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG,
"updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
data->rx_bytes, sta->last_rx_bytes_hi,
sta->last_rx_bytes_lo,
data->tx_bytes, sta->last_tx_bytes_hi,
sta->last_tx_bytes_lo,
data->bytes_64bit);
return 0;
}
static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
int interval;
if (sta->acct_interim_interval) {
accounting_sta_interim(hapd, sta);
interval = sta->acct_interim_interval;
} else {
struct hostap_sta_driver_data data;
accounting_sta_update_stats(hapd, sta, &data);
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
}
eloop_register_timeout(interval, 0, accounting_interim_update,
hapd, sta);
}
/**
* accounting_sta_start - Start STA accounting
* @hapd: hostapd BSS data
* @sta: The station
*/
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
{
struct radius_msg *msg;
int interval;
if (sta->acct_session_started)
return;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"starting accounting session %016llX",
(unsigned long long) sta->acct_session_id);
os_get_reltime(&sta->acct_session_start);
sta->last_rx_bytes_hi = 0;
sta->last_rx_bytes_lo = 0;
sta->last_tx_bytes_hi = 0;
sta->last_tx_bytes_lo = 0;
hostapd_drv_sta_clear_stats(hapd, sta->addr);
if (!hapd->conf->radius->acct_server)
return;
if (sta->acct_interim_interval)
interval = sta->acct_interim_interval;
else
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
eloop_register_timeout(interval, 0, accounting_interim_update,
hapd, sta);
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
if (msg &&
radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
radius_msg_free(msg);
sta->acct_session_started = 1;
}
static void accounting_sta_report(struct hostapd_data *hapd,
struct sta_info *sta, int stop)
{
struct radius_msg *msg;
int cause = sta->acct_terminate_cause;
struct hostap_sta_driver_data data;
struct os_reltime now_r, diff;
u64 bytes;
if (!hapd->conf->radius->acct_server)
return;
msg = accounting_msg(hapd, sta,
stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
if (!msg) {
wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
return;
}
os_get_reltime(&now_r);
os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
diff.sec)) {
wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
goto fail;
}
if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_PACKETS,
data.rx_packets)) {
wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
goto fail;
}
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
data.tx_packets)) {
wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
goto fail;
}
if (data.bytes_64bit)
bytes = data.rx_bytes;
else
bytes = ((u64) sta->last_rx_bytes_hi << 32) |
sta->last_rx_bytes_lo;
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_OCTETS,
(u32) bytes)) {
wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
goto fail;
}
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
(u32) (bytes >> 32))) {
wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
goto fail;
}
if (data.bytes_64bit)
bytes = data.tx_bytes;
else
bytes = ((u64) sta->last_tx_bytes_hi << 32) |
sta->last_tx_bytes_lo;
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
(u32) bytes)) {
wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
goto fail;
}
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
(u32) (bytes >> 32))) {
wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
goto fail;
}
}
if (eloop_terminated())
cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
if (stop && cause &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
cause)) {
wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
goto fail;
}
if (radius_client_send(hapd->radius, msg,
stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
sta->addr) < 0)
goto fail;
return;
fail:
radius_msg_free(msg);
}
/**
* accounting_sta_interim - Send a interim STA accounting report
* @hapd: hostapd BSS data
* @sta: The station
*/
static void accounting_sta_interim(struct hostapd_data *hapd,
struct sta_info *sta)
{
if (sta->acct_session_started)
accounting_sta_report(hapd, sta, 0);
}
/**
* accounting_sta_stop - Stop STA accounting
* @hapd: hostapd BSS data
* @sta: The station
*/
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
{
if (sta->acct_session_started) {
accounting_sta_report(hapd, sta, 1);
eloop_cancel_timeout(accounting_interim_update, hapd, sta);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"stopped accounting session %016llX",
(unsigned long long) sta->acct_session_id);
sta->acct_session_started = 0;
}
}
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
{
return radius_gen_session_id((u8 *) &sta->acct_session_id,
sizeof(sta->acct_session_id));
}
/**
* accounting_receive - Process the RADIUS frames from Accounting Server
* @msg: RADIUS response message
* @req: RADIUS request message
* @shared_secret: RADIUS shared secret
* @shared_secret_len: Length of shared_secret in octets
* @data: Context data (struct hostapd_data *)
* Returns: Processing status
*/
static RadiusRxResult
accounting_receive(struct radius_msg *msg, struct radius_msg *req,
const u8 *shared_secret, size_t shared_secret_len,
void *data)
{
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
wpa_printf(MSG_INFO, "Unknown RADIUS message code");
return RADIUS_RX_UNKNOWN;
}
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
}
return RADIUS_RX_PROCESSED;
}
static void accounting_report_state(struct hostapd_data *hapd, int on)
{
struct radius_msg *msg;
if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
return;
/* Inform RADIUS server that accounting will start/stop so that the
* server can close old accounting sessions. */
msg = accounting_msg(hapd, NULL,
on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
if (!msg)
return;
if (hapd->acct_session_id) {
char buf[20];
os_snprintf(buf, sizeof(buf), "%016llX",
(unsigned long long) hapd->acct_session_id);
if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
(u8 *) buf, os_strlen(buf)))
wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
}
if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
radius_msg_free(msg);
}
static void accounting_interim_error_cb(const u8 *addr, void *ctx)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
unsigned int i, wait_time;
int res;
sta = ap_get_sta(hapd, addr);
if (!sta)
return;
sta->acct_interim_errors++;
if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
wpa_printf(MSG_DEBUG,
"Interim RADIUS accounting update failed for " MACSTR
" - too many errors, abandon this interim accounting update",
MAC2STR(addr));
sta->acct_interim_errors = 0;
/* Next update will be tried after normal update interval */
return;
}
/*
* Use a shorter update interval as an improved retransmission mechanism
* for failed interim accounting updates. This allows the statistics to
* be updated for each retransmission.
*
* RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
* Schedule the first retry attempt immediately and every following one
* with exponential backoff.
*/
if (sta->acct_interim_errors == 1) {
wait_time = 0;
} else {
wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
for (i = 1; i < sta->acct_interim_errors; i++)
wait_time *= 2;
}
res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
hapd, sta);
if (res == 1)
wpa_printf(MSG_DEBUG,
"Interim RADIUS accounting update failed for " MACSTR
" (error count: %u) - schedule next update in %u seconds",
MAC2STR(addr), sta->acct_interim_errors, wait_time);
else if (res == 0)
wpa_printf(MSG_DEBUG,
"Interim RADIUS accounting update failed for " MACSTR
" (error count: %u)", MAC2STR(addr),
sta->acct_interim_errors);
else
wpa_printf(MSG_DEBUG,
"Interim RADIUS accounting update failed for " MACSTR
" (error count: %u) - no timer found", MAC2STR(addr),
sta->acct_interim_errors);
}
/**
* accounting_init: Initialize accounting
* @hapd: hostapd BSS data
* Returns: 0 on success, -1 on failure
*/
int accounting_init(struct hostapd_data *hapd)
{
if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
sizeof(hapd->acct_session_id)) < 0)
return -1;
if (radius_client_register(hapd->radius, RADIUS_ACCT,
accounting_receive, hapd))
return -1;
radius_client_set_interim_error_cb(hapd->radius,
accounting_interim_error_cb, hapd);
accounting_report_state(hapd, 1);
return 0;
}
/**
* accounting_deinit: Deinitialize accounting
* @hapd: hostapd BSS data
*/
void accounting_deinit(struct hostapd_data *hapd)
{
accounting_report_state(hapd, 0);
}

View file

@ -0,0 +1,45 @@
/*
* hostapd / RADIUS Accounting
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef ACCOUNTING_H
#define ACCOUNTING_H
#ifdef CONFIG_NO_ACCOUNTING
static inline int accounting_sta_get_id(struct hostapd_data *hapd,
struct sta_info *sta)
{
return 0;
}
static inline void accounting_sta_start(struct hostapd_data *hapd,
struct sta_info *sta)
{
}
static inline void accounting_sta_stop(struct hostapd_data *hapd,
struct sta_info *sta)
{
}
static inline int accounting_init(struct hostapd_data *hapd)
{
return 0;
}
static inline void accounting_deinit(struct hostapd_data *hapd)
{
}
#else /* CONFIG_NO_ACCOUNTING */
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
int accounting_init(struct hostapd_data *hapd);
void accounting_deinit(struct hostapd_data *hapd);
#endif /* CONFIG_NO_ACCOUNTING */
#endif /* ACCOUNTING_H */

986
hostapd-2.9/src/ap/acs.c Normal file
View 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(&params, 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], &params) < 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
View 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 */

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

View file

@ -0,0 +1,48 @@
/*
* Airtime policy configuration
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef AIRTIME_POLICY_H
#define AIRTIME_POLICY_H
struct hostapd_iface;
#ifdef CONFIG_AIRTIME_POLICY
#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
/* scale quantum so this becomes the effective quantum after applying the max
* weight, but never go below min or above max */
#define AIRTIME_QUANTUM_MIN 8 /* usec */
#define AIRTIME_QUANTUM_MAX 256 /* usec */
#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
int airtime_policy_update_init(struct hostapd_iface *iface);
void airtime_policy_update_deinit(struct hostapd_iface *iface);
#else /* CONFIG_AIRTIME_POLICY */
static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
struct sta_info *sta)
{
return -1;
}
static inline int airtime_policy_update_init(struct hostapd_iface *iface)
{
return -1;
}
static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
{
}
#endif /* CONFIG_AIRTIME_POLICY */
#endif /* AIRTIME_POLICY_H */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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(&params, 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, &params);
}
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(&params, 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,
&params.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, &params);
}
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(&params, 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, &params);
}
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(&params, 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, &params);
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);
}

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

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

View file

@ -0,0 +1,58 @@
/*
* hostapd / AP table
* Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2006, Devicescape Software, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef AP_LIST_H
#define AP_LIST_H
struct ap_info {
/* Note: next/prev pointers are updated whenever a new beacon is
* received because these are used to find the least recently used
* entries. */
struct ap_info *next; /* next entry in AP list */
struct ap_info *prev; /* previous entry in AP list */
struct ap_info *hnext; /* next entry in hash table list */
u8 addr[6];
u8 supported_rates[WLAN_SUPP_RATES_MAX];
int erp; /* ERP Info or -1 if ERP info element not present */
int channel;
int ht_support;
struct os_reltime last_beacon;
};
struct ieee802_11_elems;
struct hostapd_frame_info;
void ap_list_process_beacon(struct hostapd_iface *iface,
const struct ieee80211_mgmt *mgmt,
struct ieee802_11_elems *elems,
struct hostapd_frame_info *fi);
#ifdef NEED_AP_MLME
int ap_list_init(struct hostapd_iface *iface);
void ap_list_deinit(struct hostapd_iface *iface);
void ap_list_timer(struct hostapd_iface *iface);
#else /* NEED_AP_MLME */
static inline int ap_list_init(struct hostapd_iface *iface)
{
return 0;
}
static inline void ap_list_deinit(struct hostapd_iface *iface)
{
}
static inline void ap_list_timer(struct hostapd_iface *iface)
{
}
#endif /* NEED_AP_MLME */
#endif /* AP_LIST_H */

View file

@ -0,0 +1,191 @@
/*
* hostapd / IEEE 802.11 MLME
* Copyright 2003-2006, Jouni Malinen <j@w1.fi>
* Copyright 2003-2004, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
#include "sta_info.h"
#include "ap_mlme.h"
#include "hostapd.h"
#ifndef CONFIG_NO_HOSTAPD_LOGGER
static const char * mlme_auth_alg_str(int alg)
{
switch (alg) {
case WLAN_AUTH_OPEN:
return "OPEN_SYSTEM";
case WLAN_AUTH_SHARED_KEY:
return "SHARED_KEY";
case WLAN_AUTH_FT:
return "FT";
}
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);
}

View file

@ -0,0 +1,34 @@
/*
* hostapd / IEEE 802.11 MLME
* Copyright 2003, Jouni Malinen <j@w1.fi>
* Copyright 2003-2004, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef MLME_H
#define MLME_H
void mlme_authenticate_indication(struct hostapd_data *hapd,
struct sta_info *sta);
void mlme_deauthenticate_indication(struct hostapd_data *hapd,
struct sta_info *sta, u16 reason_code);
void mlme_associate_indication(struct hostapd_data *hapd,
struct sta_info *sta);
void mlme_reassociate_indication(struct hostapd_data *hapd,
struct sta_info *sta);
void mlme_disassociate_indication(struct hostapd_data *hapd,
struct sta_info *sta, u16 reason_code);
void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
const u8 *addr);
void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* MLME_H */

View 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(&params, 0, sizeof(params));
params.ca_cert = hapd->conf->ca_cert;
params.client_cert = hapd->conf->server_cert;
params.client_cert2 = hapd->conf->server_cert2;
params.private_key = hapd->conf->private_key;
params.private_key2 = hapd->conf->private_key2;
params.private_key_passwd = hapd->conf->private_key_passwd;
params.private_key_passwd2 = hapd->conf->private_key_passwd2;
params.dh_file = hapd->conf->dh_file;
params.openssl_ciphers = hapd->conf->openssl_ciphers;
params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
params.ocsp_stapling_response =
hapd->conf->ocsp_stapling_response;
params.ocsp_stapling_response_multi =
hapd->conf->ocsp_stapling_response_multi;
params.check_cert_subject = hapd->conf->check_cert_subject;
if (tls_global_set_params(hapd->ssl_ctx, &params)) {
wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
authsrv_deinit(hapd);
return -1;
}
if (tls_global_set_verify(hapd->ssl_ctx,
hapd->conf->check_crl,
hapd->conf->check_crl_strict)) {
wpa_printf(MSG_ERROR, "Failed to enable check_crl");
authsrv_deinit(hapd);
return -1;
}
}
#endif /* EAP_TLS_FUNCS */
#ifdef 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 */
}

View file

@ -0,0 +1,15 @@
/*
* Authentication server setup
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef AUTHSRV_H
#define AUTHSRV_H
int authsrv_init(struct hostapd_data *hapd);
void authsrv_deinit(struct hostapd_data *hapd);
#endif /* AUTHSRV_H */

1485
hostapd-2.9/src/ap/beacon.c Normal file

File diff suppressed because it is too large Load diff

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

View file

@ -0,0 +1,99 @@
/*
* BSS Load Element / Channel Utilization
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "hostapd.h"
#include "bss_load.h"
#include "ap_drv_ops.h"
#include "beacon.h"
static int get_bss_load_update_timeout(struct hostapd_data *hapd,
unsigned int *sec, unsigned int *usec)
{
unsigned int update_period = hapd->conf->bss_load_update_period;
unsigned int beacon_int = hapd->iconf->beacon_int;
unsigned int update_timeout;
if (!update_period || !beacon_int) {
wpa_printf(MSG_ERROR,
"BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
update_period, beacon_int);
return -1;
}
update_timeout = update_period * beacon_int;
*sec = ((update_timeout / 1000) * 1024) / 1000;
*usec = (update_timeout % 1000) * 1024;
return 0;
}
static void update_channel_utilization(void *eloop_data, void *user_data)
{
struct hostapd_data *hapd = eloop_data;
unsigned int sec, usec;
int err;
struct hostapd_iface *iface = hapd->iface;
if (!(hapd->beacon_set_done && hapd->started))
return;
err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
if (err) {
wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
return;
}
ieee802_11_set_beacon(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);
}

View file

@ -0,0 +1,17 @@
/*
* BSS load update
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef BSS_LOAD_UPDATE_H
#define BSS_LOAD_UPDATE_H
int bss_load_update_init(struct hostapd_data *hapd);
void bss_load_update_deinit(struct hostapd_data *hapd);
#endif /* BSS_LOAD_UPDATE_H */

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

View 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

File diff suppressed because it is too large Load diff

33
hostapd-2.9/src/ap/dfs.h Normal file
View 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 */

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

View file

@ -0,0 +1,30 @@
/*
* DHCP snooping for Proxy ARP
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef DHCP_SNOOP_H
#define DHCP_SNOOP_H
#ifdef CONFIG_PROXYARP
int dhcp_snoop_init(struct hostapd_data *hapd);
void dhcp_snoop_deinit(struct hostapd_data *hapd);
#else /* CONFIG_PROXYARP */
static inline int dhcp_snoop_init(struct hostapd_data *hapd)
{
return 0;
}
static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
{
}
#endif /* CONFIG_PROXYARP */
#endif /* DHCP_SNOOP_H */

File diff suppressed because it is too large Load diff

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,290 @@
/*
* hostapd / EAP user database
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#ifdef CONFIG_SQLITE
#include <sqlite3.h>
#endif /* CONFIG_SQLITE */
#include "common.h"
#include "eap_common/eap_wsc_common.h"
#include "eap_server/eap_methods.h"
#include "eap_server/eap.h"
#include "ap_config.h"
#include "hostapd.h"
#ifdef CONFIG_SQLITE
static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
{
char *buf, *start;
int num_methods;
buf = os_strdup(methods);
if (buf == NULL)
return;
os_memset(&user->methods, 0, sizeof(user->methods));
num_methods = 0;
start = buf;
while (*start) {
char *pos3 = os_strchr(start, ',');
if (pos3)
*pos3++ = '\0';
user->methods[num_methods].method =
eap_server_get_type(start,
&user->methods[num_methods].vendor);
if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
user->methods[num_methods].method == EAP_TYPE_NONE) {
if (os_strcmp(start, "TTLS-PAP") == 0) {
user->ttls_auth |= EAP_TTLS_AUTH_PAP;
goto skip_eap;
}
if (os_strcmp(start, "TTLS-CHAP") == 0) {
user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
goto skip_eap;
}
if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
goto skip_eap;
}
if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
goto skip_eap;
}
wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
start);
os_free(buf);
return;
}
num_methods++;
if (num_methods >= EAP_MAX_METHODS)
break;
skip_eap:
if (pos3 == NULL)
break;
start = pos3;
}
os_free(buf);
}
static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
{
struct hostapd_eap_user *user = ctx;
int i;
for (i = 0; i < argc; i++) {
if (os_strcmp(col[i], "password") == 0 && argv[i]) {
bin_clear_free(user->password, user->password_len);
user->password_len = os_strlen(argv[i]);
user->password = (u8 *) os_strdup(argv[i]);
user->next = (void *) 1;
} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
set_user_methods(user, argv[i]);
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
user->remediation = strlen(argv[i]) > 0;
} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
user->t_c_timestamp = strtol(argv[i], NULL, 10);
}
}
return 0;
}
static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
{
struct hostapd_eap_user *user = ctx;
int i, id = -1, methods = -1;
size_t len;
for (i = 0; i < argc; i++) {
if (os_strcmp(col[i], "identity") == 0 && argv[i])
id = i;
else if (os_strcmp(col[i], "methods") == 0 && argv[i])
methods = i;
}
if (id < 0 || methods < 0)
return 0;
len = os_strlen(argv[id]);
if (len <= user->identity_len &&
os_memcmp(argv[id], user->identity, len) == 0 &&
(user->password == NULL || len > user->password_len)) {
bin_clear_free(user->password, user->password_len);
user->password_len = os_strlen(argv[id]);
user->password = (u8 *) os_strdup(argv[id]);
user->next = (void *) 1;
set_user_methods(user, argv[methods]);
}
return 0;
}
static const struct hostapd_eap_user *
eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2)
{
sqlite3 *db;
struct hostapd_eap_user *user = NULL;
char id_str[256], cmd[300];
size_t i;
int res;
if (identity_len >= sizeof(id_str)) {
wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
__func__, (int) identity_len,
(int) (sizeof(id_str)));
return NULL;
}
os_memcpy(id_str, identity, identity_len);
id_str[identity_len] = '\0';
for (i = 0; i < identity_len; i++) {
if (id_str[i] >= 'a' && id_str[i] <= 'z')
continue;
if (id_str[i] >= 'A' && id_str[i] <= 'Z')
continue;
if (id_str[i] >= '0' && id_str[i] <= '9')
continue;
if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
id_str[i] == '=' || id_str[i] == ' ')
continue;
wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
return NULL;
}
bin_clear_free(hapd->tmp_eap_user.identity,
hapd->tmp_eap_user.identity_len);
bin_clear_free(hapd->tmp_eap_user.password,
hapd->tmp_eap_user.password_len);
os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
hapd->tmp_eap_user.phase2 = phase2;
hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
if (hapd->tmp_eap_user.identity == NULL)
return NULL;
os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
hapd->tmp_eap_user.identity_len = identity_len;
if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
sqlite3_close(db);
return NULL;
}
res = os_snprintf(cmd, sizeof(cmd),
"SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
id_str, phase2);
if (os_snprintf_error(sizeof(cmd), res))
goto fail;
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
SQLITE_OK) {
wpa_printf(MSG_DEBUG,
"DB: Failed to complete SQL operation: %s db: %s",
sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
} else if (hapd->tmp_eap_user.next)
user = &hapd->tmp_eap_user;
if (user == NULL && !phase2) {
os_snprintf(cmd, sizeof(cmd),
"SELECT identity,methods FROM wildcards;");
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
NULL) != SQLITE_OK) {
wpa_printf(MSG_DEBUG,
"DB: Failed to complete SQL operation: %s db: %s",
sqlite3_errmsg(db),
hapd->conf->eap_user_sqlite);
} else if (hapd->tmp_eap_user.next) {
user = &hapd->tmp_eap_user;
os_free(user->identity);
user->identity = user->password;
user->identity_len = user->password_len;
user->password = NULL;
user->password_len = 0;
}
}
fail:
sqlite3_close(db);
return user;
}
#endif /* CONFIG_SQLITE */
const struct hostapd_eap_user *
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2)
{
const struct hostapd_bss_config *conf = hapd->conf;
struct hostapd_eap_user *user = conf->eap_user;
#ifdef CONFIG_WPS
if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
static struct hostapd_eap_user wsc_enrollee;
os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
wsc_enrollee.methods[0].method = eap_server_get_type(
"WSC", &wsc_enrollee.methods[0].vendor);
return &wsc_enrollee;
}
if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
static struct hostapd_eap_user wsc_registrar;
os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
wsc_registrar.methods[0].method = eap_server_get_type(
"WSC", &wsc_registrar.methods[0].vendor);
wsc_registrar.password = (u8 *) conf->ap_pin;
wsc_registrar.password_len = conf->ap_pin ?
os_strlen(conf->ap_pin) : 0;
return &wsc_registrar;
}
#endif /* CONFIG_WPS */
while (user) {
if (!phase2 && user->identity == NULL) {
/* Wildcard match */
break;
}
if (user->phase2 == !!phase2 && user->wildcard_prefix &&
identity_len >= user->identity_len &&
os_memcmp(user->identity, identity, user->identity_len) ==
0) {
/* Wildcard prefix match */
break;
}
if (user->phase2 == !!phase2 &&
user->identity_len == identity_len &&
os_memcmp(user->identity, identity, identity_len) == 0)
break;
user = user->next;
}
#ifdef CONFIG_SQLITE
if (user == NULL && conf->eap_user_sqlite) {
return eap_user_sqlite_get(hapd, identity, identity_len,
phase2);
}
#endif /* CONFIG_SQLITE */
return user;
}

View file

@ -0,0 +1,191 @@
/*
* hostapd / IEEE 802 OUI Extended EtherType 88-B7
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "l2_packet/l2_packet.h"
#include "hostapd.h"
#include "eth_p_oui.h"
/*
* See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
* EtherType 88-B7. This file implements this with OUI 00:13:74 and
* vendor-specific subtype 0x0001.
*/
static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
struct eth_p_oui_iface {
struct dl_list list;
char ifname[IFNAMSIZ + 1];
struct l2_packet_data *l2;
struct dl_list receiver;
};
struct eth_p_oui_ctx {
struct dl_list list;
struct eth_p_oui_iface *iface;
/* all data needed to deliver and unregister */
u8 oui_suffix; /* last byte of OUI */
void (*rx_callback)(void *ctx, const u8 *src_addr,
const u8 *dst_addr, u8 oui_suffix,
const u8 *buf, size_t len);
void *rx_callback_ctx;
};
void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
const u8 *dst_addr, const u8 *buf, size_t len)
{
ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
ctx->oui_suffix, buf, len);
}
static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
{
struct eth_p_oui_iface *iface = ctx;
struct eth_p_oui_ctx *receiver;
const struct l2_ethhdr *ethhdr;
if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
/* too short packet */
return;
}
ethhdr = (struct l2_ethhdr *) buf;
/* trim eth_hdr from buf and len */
buf += sizeof(*ethhdr);
len -= sizeof(*ethhdr);
/* verify OUI and vendor-specific subtype match */
if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
return;
buf += sizeof(global_oui);
len -= sizeof(global_oui);
dl_list_for_each(receiver, &iface->receiver,
struct eth_p_oui_ctx, list) {
if (buf[0] != receiver->oui_suffix)
continue;
eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
buf + 1, len - 1);
}
}
struct eth_p_oui_ctx *
eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
void (*rx_callback)(void *ctx, const u8 *src_addr,
const u8 *dst_addr, u8 oui_suffix,
const u8 *buf, size_t len),
void *rx_callback_ctx)
{
struct eth_p_oui_iface *iface;
struct eth_p_oui_ctx *receiver;
int found = 0;
struct hapd_interfaces *interfaces;
receiver = os_zalloc(sizeof(*receiver));
if (!receiver)
goto err;
receiver->oui_suffix = oui_suffix;
receiver->rx_callback = rx_callback;
receiver->rx_callback_ctx = rx_callback_ctx;
interfaces = hapd->iface->interfaces;
dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
list) {
if (os_strcmp(iface->ifname, ifname) != 0)
continue;
found = 1;
break;
}
if (!found) {
iface = os_zalloc(sizeof(*iface));
if (!iface)
goto err;
os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
iface, 1);
if (!iface->l2) {
os_free(iface);
goto err;
}
dl_list_init(&iface->receiver);
dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
}
dl_list_add_tail(&iface->receiver, &receiver->list);
receiver->iface = iface;
return receiver;
err:
os_free(receiver);
return NULL;
}
void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
{
struct eth_p_oui_iface *iface;
if (!ctx)
return;
iface = ctx->iface;
dl_list_del(&ctx->list);
os_free(ctx);
if (dl_list_empty(&iface->receiver)) {
dl_list_del(&iface->list);
l2_packet_deinit(iface->l2);
os_free(iface);
}
}
int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
const u8 *dst_addr, const u8 *buf, size_t len)
{
struct eth_p_oui_iface *iface = ctx->iface;
u8 *packet, *p;
size_t packet_len;
int ret;
struct l2_ethhdr *ethhdr;
packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
packet = os_zalloc(packet_len);
if (!packet)
return -1;
p = packet;
ethhdr = (struct l2_ethhdr *) packet;
os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
ethhdr->h_proto = host_to_be16(ETH_P_OUI);
p += sizeof(*ethhdr);
os_memcpy(p, global_oui, sizeof(global_oui));
p[sizeof(global_oui)] = ctx->oui_suffix;
p += sizeof(global_oui) + 1;
os_memcpy(p, buf, len);
ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
os_free(packet);
return ret;
}

View file

@ -0,0 +1,28 @@
/*
* hostapd / IEEE 802 OUI Extended Ethertype
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef ETH_P_OUI_H
#define ETH_P_OUI_H
struct eth_p_oui_ctx;
struct hostapd_data;
/* rx_callback only gets payload after OUI passed as buf */
struct eth_p_oui_ctx *
eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
void (*rx_callback)(void *ctx, const u8 *src_addr,
const u8 *dst_addr, u8 oui_suffix,
const u8 *buf, size_t len),
void *rx_callback_ctx);
void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
const u8 *dst_addr, const u8 *buf, size_t len);
void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
const u8 *dst_addr, const u8 *buf, size_t len);
#endif /* ETH_P_OUI_H */

View file

@ -0,0 +1,654 @@
/*
* FILS HLP request processing
* Copyright (c) 2017, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/dhcp.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ieee802_11.h"
#include "fils_hlp.h"
static be16 ip_checksum(const void *buf, size_t len)
{
u32 sum = 0;
const u16 *pos;
for (pos = buf; len >= 2; len -= 2)
sum += ntohs(*pos++);
if (len)
sum += ntohs(*pos << 8);
sum = (sum >> 16) + (sum & 0xffff);
sum += sum >> 16;
return htons(~sum);
}
static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
{
u8 *pos, *end;
struct dhcp_data *dhcp;
struct sockaddr_in addr;
ssize_t res;
const u8 *server_id = NULL;
if (!sta->hlp_dhcp_discover) {
wpa_printf(MSG_DEBUG,
"FILS: No pending HLP DHCPDISCOVER available");
return -1;
}
/* Convert to DHCPREQUEST, remove rapid commit option, replace requested
* IP address option with yiaddr. */
pos = wpabuf_mhead(sta->hlp_dhcp_discover);
end = pos + wpabuf_len(sta->hlp_dhcp_discover);
dhcp = (struct dhcp_data *) pos;
pos = (u8 *) (dhcp + 1);
pos += 4; /* skip magic */
while (pos < end && *pos != DHCP_OPT_END) {
u8 opt, olen;
opt = *pos++;
if (opt == DHCP_OPT_PAD)
continue;
if (pos >= end)
break;
olen = *pos++;
if (olen > end - pos)
break;
switch (opt) {
case DHCP_OPT_MSG_TYPE:
if (olen > 0)
*pos = DHCPREQUEST;
break;
case DHCP_OPT_RAPID_COMMIT:
case DHCP_OPT_REQUESTED_IP_ADDRESS:
case DHCP_OPT_SERVER_ID:
/* Remove option */
pos -= 2;
os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
end -= 2 + olen;
olen = 0;
break;
}
pos += olen;
}
if (pos >= end || *pos != DHCP_OPT_END) {
wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
return -1;
}
sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
/* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
pos = (u8 *) (dhcpoffer + 1);
end = dhcpofferend;
pos += 4; /* skip magic */
while (pos < end && *pos != DHCP_OPT_END) {
u8 opt, olen;
opt = *pos++;
if (opt == DHCP_OPT_PAD)
continue;
if (pos >= end)
break;
olen = *pos++;
if (olen > end - pos)
break;
switch (opt) {
case DHCP_OPT_SERVER_ID:
server_id = pos - 2;
break;
}
pos += olen;
}
if (wpabuf_resize(&sta->hlp_dhcp_discover,
6 + 1 + (server_id ? 2 + server_id[1] : 0)))
return -1;
if (server_id)
wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
2 + server_id[1]);
wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
os_memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
addr.sin_port = htons(hapd->conf->dhcp_server_port);
res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
wpabuf_len(sta->hlp_dhcp_discover), 0,
(const struct sockaddr *) &addr, sizeof(addr));
if (res < 0) {
wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
strerror(errno));
return -1;
}
wpa_printf(MSG_DEBUG,
"FILS: Acting as DHCP rapid commit proxy for %s:%d",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
wpabuf_free(sta->hlp_dhcp_discover);
sta->hlp_dhcp_discover = NULL;
sta->fils_dhcp_rapid_commit_proxy = 1;
return 0;
}
static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
{
struct hostapd_data *hapd = sock_ctx;
struct sta_info *sta;
u8 buf[1500], *pos, *end, *end_opt = NULL;
struct dhcp_data *dhcp;
struct sockaddr_in addr;
socklen_t addr_len;
ssize_t res;
u8 msgtype = 0;
int rapid_commit = 0;
struct 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;
}
}

View file

@ -0,0 +1,27 @@
/*
* FILS HLP request processing
* Copyright (c) 2017, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef FILS_HLP_H
#define FILS_HLP_H
int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *pos, int left);
#ifdef CONFIG_FILS
void fils_hlp_deinit(struct hostapd_data *hapd);
#else /* CONFIG_FILS */
static inline void fils_hlp_deinit(struct hostapd_data *hapd)
{
}
#endif /* CONFIG_FILS */
#endif /* FILS_HLP_H */

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

View file

@ -0,0 +1,43 @@
/*
* Generic advertisement service (GAS) query
* Copyright (c) 2009, Atheros Communications
* Copyright (c) 2011-2017, Qualcomm Atheros
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef GAS_QUERY_AP_H
#define GAS_QUERY_AP_H
struct gas_query_ap;
struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
void *msg_ctx);
void gas_query_ap_deinit(struct gas_query_ap *gas);
int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
const u8 *data, size_t len, int freq);
/**
* enum gas_query_ap_result - GAS query result
*/
enum gas_query_ap_result {
GAS_QUERY_AP_SUCCESS,
GAS_QUERY_AP_FAILURE,
GAS_QUERY_AP_TIMEOUT,
GAS_QUERY_AP_PEER_ERROR,
GAS_QUERY_AP_INTERNAL_ERROR,
GAS_QUERY_AP_DELETED_AT_DEINIT
};
int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
struct wpabuf *req,
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_ap_result result,
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code),
void *ctx);
void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
const u8 *data, size_t data_len, int ok);
#endif /* GAS_QUERY_AP_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
/*
* Generic advertisement service (GAS) server
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef GAS_SERV_H
#define GAS_SERV_H
/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
#define ANQP_REQ_CAPABILITY_LIST \
(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
#define ANQP_REQ_VENUE_NAME \
(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
(1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
#define ANQP_REQ_NETWORK_AUTH_TYPE \
(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
#define ANQP_REQ_ROAMING_CONSORTIUM \
(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
(1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
#define ANQP_REQ_NAI_REALM \
(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
(1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
#define ANQP_REQ_AP_CIVIC_LOCATION \
(1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
(1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
#define ANQP_REQ_DOMAIN_NAME \
(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
#define ANQP_REQ_EMERGENCY_ALERT_URI \
(1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
#define ANQP_REQ_TDLS_CAPABILITY \
(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
#define ANQP_REQ_EMERGENCY_NAI \
(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
/*
* First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
* optimized bitmap.
*/
#define ANQP_REQ_HS_CAPABILITY_LIST \
(0x10000 << HS20_STYPE_CAPABILITY_LIST)
#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
(0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
#define ANQP_REQ_WAN_METRICS \
(0x10000 << HS20_STYPE_WAN_METRICS)
#define ANQP_REQ_CONNECTION_CAPABILITY \
(0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
#define ANQP_REQ_NAI_HOME_REALM \
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
#define ANQP_REQ_OPERATING_CLASS \
(0x10000 << HS20_STYPE_OPERATING_CLASS)
#define ANQP_REQ_OSU_PROVIDERS_LIST \
(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
#define ANQP_REQ_ICON_REQUEST \
(0x10000 << HS20_STYPE_ICON_REQUEST)
#define ANQP_REQ_OPERATOR_ICON_METADATA \
(0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
(0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
/* The first MBO ANQP-element can be included in the optimized bitmap. */
#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
struct gas_dialog_info {
u8 valid;
struct wpabuf *sd_resp; /* Fragmented response */
u8 dialog_token;
size_t sd_resp_pos; /* Offset in sd_resp */
u8 sd_frag_id;
int prot; /* whether Protected Dual of Public Action frame is used */
int dpp; /* whether this is a DPP Config Response */
};
struct hostapd_data;
struct gas_dialog_info *
gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
u8 dialog_token);
void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
int gas_serv_init(struct hostapd_data *hapd);
void gas_serv_deinit(struct hostapd_data *hapd);
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
int prot, struct wpabuf *buf);
#endif /* GAS_SERV_H */

3573
hostapd-2.9/src/ap/hostapd.c Normal file

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,255 @@
/*
* Hotspot 2.0 AP ANQP processing
* Copyright (c) 2009, Atheros Communications, Inc.
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "sta_info.h"
#include "hs20.h"
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
{
u8 conf;
if (!hapd->conf->hs20)
return eid;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
*eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
WPA_PUT_BE24(eid, OUI_WFA);
eid += 3;
*eid++ = HS20_INDICATION_OUI_TYPE;
conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
if (hapd->conf->hs20_release >= 2)
conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
if (hapd->conf->disable_dgaf)
conf |= HS20_DGAF_DISABLED;
*eid++ = conf;
if (hapd->conf->hs20_release >= 2) {
WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
eid += 2;
}
return eid;
}
u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
{
u8 *len;
u16 capab;
if (!hapd->conf->osen)
return eid;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
len = eid++; /* to be filled */
WPA_PUT_BE24(eid, OUI_WFA);
eid += 3;
*eid++ = HS20_OSEN_OUI_TYPE;
/* Group Data Cipher Suite */
RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
eid += RSN_SELECTOR_LEN;
/* Pairwise Cipher Suite Count and List */
WPA_PUT_LE16(eid, 1);
eid += 2;
RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
eid += RSN_SELECTOR_LEN;
/* AKM Suite Count and List */
WPA_PUT_LE16(eid, 1);
eid += 2;
RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
eid += RSN_SELECTOR_LEN;
/* RSN Capabilities */
capab = 0;
if (hapd->conf->wmm_enabled) {
/* 4 PTKSA replay counters when using WMM */
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
}
#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
View file

@ -0,0 +1,26 @@
/*
* Hotspot 2.0 AP ANQP processing
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef HS20_H
#define HS20_H
struct hostapd_data;
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
u8 osu_method, const char *url);
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
const u8 *addr,
const struct wpabuf *payload);
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
const u8 *addr, const char *url);
void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
int enabled);
#endif /* HS20_H */

View 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(&params, 0, sizeof(params));
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
ieee80211n_scan_channels_2g4(iface, &params);
else
ieee80211n_scan_channels_5g(iface, &params);
ret = hostapd_driver_scan(iface->bss[0], &params);
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(&params, 0, sizeof(params));
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
ieee80211n_scan_channels_2g4(iface, &params);
else
ieee80211n_scan_channels_5g(iface, &params);
ret = hostapd_driver_scan(iface->bss[0], &params);
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;
}

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

File diff suppressed because it is too large Load diff

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

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

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

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

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

File diff suppressed because it is too large Load diff

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

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,244 @@
/*
* hostapd - MBO
* Copyright (c) 2016, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "hostapd.h"
#include "sta_info.h"
#include "mbo_ap.h"
void mbo_ap_sta_free(struct sta_info *sta)
{
struct mbo_non_pref_chan_info *info, *prev;
info = sta->non_pref_chan;
sta->non_pref_chan = NULL;
while (info) {
prev = info;
info = info->next;
os_free(prev);
}
}
static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
const u8 *buf, size_t len)
{
struct mbo_non_pref_chan_info *info, *tmp;
char channels[200], *pos, *end;
size_t num_chan, i;
int ret;
if (len <= 3)
return; /* Not enough room for any channels */
num_chan = len - 3;
info = os_zalloc(sizeof(*info) + num_chan);
if (!info)
return;
info->op_class = buf[0];
info->pref = buf[len - 2];
info->reason_code = buf[len - 1];
info->num_channels = num_chan;
buf++;
os_memcpy(info->channels, buf, num_chan);
if (!sta->non_pref_chan) {
sta->non_pref_chan = info;
} else {
tmp = sta->non_pref_chan;
while (tmp->next)
tmp = tmp->next;
tmp->next = info;
}
pos = channels;
end = pos + sizeof(channels);
*pos = '\0';
for (i = 0; i < num_chan; i++) {
ret = os_snprintf(pos, end - pos, "%s%u",
i == 0 ? "" : " ", buf[i]);
if (os_snprintf_error(end - pos, ret)) {
*pos = '\0';
break;
}
pos += ret;
}
wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
" non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
MAC2STR(sta->addr), info->op_class, info->pref,
info->reason_code, channels);
}
void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
{
const u8 *pos, *attr, *end;
size_t len;
if (!hapd->conf->mbo_enabled || !elems->mbo)
return;
pos = elems->mbo + 4;
len = elems->mbo_len - 4;
wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
if (attr && attr[1] >= 1)
sta->cell_capa = attr[2];
mbo_ap_sta_free(sta);
end = pos + len;
while (end - pos > 1) {
u8 ie_len = pos[1];
if (2 + ie_len > end - pos)
break;
if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
pos += 2 + pos[1];
}
}
int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
{
char *pos = buf, *end = buf + buflen;
int ret;
struct mbo_non_pref_chan_info *info;
u8 i;
unsigned int count = 0;
if (!sta->cell_capa)
return 0;
ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
for (info = sta->non_pref_chan; info; info = info->next) {
char *pos2 = pos;
ret = os_snprintf(pos2, end - pos2,
"non_pref_chan[%u]=%u:%u:%u:",
count, info->op_class, info->pref,
info->reason_code);
count++;
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
for (i = 0; i < info->num_channels; i++) {
ret = os_snprintf(pos2, end - pos2, "%u%s",
info->channels[i],
i + 1 < info->num_channels ?
"," : "");
if (os_snprintf_error(end - pos2, ret)) {
pos2 = NULL;
break;
}
pos2 += ret;
}
if (!pos2)
break;
ret = os_snprintf(pos2, end - pos2, "\n");
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
pos = pos2;
}
return pos - buf;
}
static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
const u8 *buf, size_t len)
{
if (len < 1)
return;
wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
" updated cellular data capability: %u",
MAC2STR(sta->addr), buf[0]);
sta->cell_capa = buf[0];
}
static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
const u8 *buf, size_t len,
int *first_non_pref_chan)
{
switch (type) {
case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
if (*first_non_pref_chan) {
/*
* Need to free the previously stored entries now to
* allow the update to replace all entries.
*/
*first_non_pref_chan = 0;
mbo_ap_sta_free(sta);
}
mbo_ap_parse_non_pref_chan(sta, buf, len);
break;
case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
break;
default:
wpa_printf(MSG_DEBUG,
"MBO: Ignore unknown WNM Notification WFA subelement %u",
type);
break;
}
}
void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len)
{
const u8 *pos, *end;
u8 ie_len;
struct sta_info *sta;
int first_non_pref_chan = 1;
if (!hapd->conf->mbo_enabled)
return;
sta = ap_get_sta(hapd, addr);
if (!sta)
return;
pos = buf;
end = buf + len;
while (end - pos > 1) {
ie_len = pos[1];
if (2 + ie_len > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
mbo_ap_wnm_notif_req_elem(sta, pos[5],
pos + 6, ie_len - 4,
&first_non_pref_chan);
else
wpa_printf(MSG_DEBUG,
"MBO: Ignore unknown WNM Notification element %u (len=%u)",
pos[0], pos[1]);
pos += 2 + pos[1];
}
}

View file

@ -0,0 +1,51 @@
/*
* MBO related functions and structures
* Copyright (c) 2016, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef MBO_AP_H
#define MBO_AP_H
struct hostapd_data;
struct sta_info;
struct ieee802_11_elems;
#ifdef CONFIG_MBO
void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems);
int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len);
void mbo_ap_sta_free(struct sta_info *sta);
#else /* CONFIG_MBO */
static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
struct sta_info *sta,
struct ieee802_11_elems *elems)
{
}
static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
size_t buflen)
{
return 0;
}
static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
const u8 *addr,
const u8 *buf, size_t len)
{
}
static inline void mbo_ap_sta_free(struct sta_info *sta)
{
}
#endif /* CONFIG_MBO */
#endif /* MBO_AP_H */

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

View file

@ -0,0 +1,36 @@
/*
* Neighbor Discovery snooping for Proxy ARP
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef NDISC_SNOOP_H
#define NDISC_SNOOP_H
#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
int ndisc_snoop_init(struct hostapd_data *hapd);
void ndisc_snoop_deinit(struct hostapd_data *hapd);
void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
static inline int ndisc_snoop_init(struct hostapd_data *hapd)
{
return 0;
}
static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
{
}
static inline void sta_ip6addr_del(struct hostapd_data *hapd,
struct sta_info *sta)
{
}
#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
#endif /* NDISC_SNOOP_H */

View 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,
&center_freq1_idx);
}
ssid.ssid_len = hapd->conf->ssid.ssid_len;
os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
/*
* Neighbor Report element size = BSSID + BSSID info + op_class + chan +
* phy type + wide bandwidth channel subelement.
*/
nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
if (!nr)
return;
wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
wpabuf_put_le32(nr, bssid_info);
wpabuf_put_u8(nr, op_class);
wpabuf_put_u8(nr, channel);
wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
/*
* Wide Bandwidth Channel subelement may be needed to allow the
* receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
* Figure 9-301.
*/
wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
wpabuf_put_u8(nr, 3);
wpabuf_put_u8(nr, width);
wpabuf_put_u8(nr, center_freq1_idx);
wpabuf_put_u8(nr, center_freq2_idx);
hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
hapd->iconf->civic, hapd->iconf->stationary_ap);
wpabuf_free(nr);
#endif /* NEED_AP_MLME */
}

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

View file

@ -0,0 +1,113 @@
/*
* hostapd / P2P integration
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "sta_info.h"
#include "p2p_hostapd.h"
#ifdef CONFIG_P2P
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen)
{
if (sta->p2p_ie == NULL)
return 0;
return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
}
int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
int duration)
{
wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
"duration=%d", count, start, duration);
if (count == 0) {
hapd->noa_enabled = 0;
hapd->noa_start = 0;
hapd->noa_duration = 0;
}
if (count != 255) {
wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
"NoA parameters");
return hostapd_driver_set_noa(hapd, count, start, duration);
}
hapd->noa_enabled = 1;
hapd->noa_start = start;
hapd->noa_duration = duration;
if (hapd->num_sta_no_p2p == 0) {
wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
"periodic NoA parameters");
return hostapd_driver_set_noa(hapd, count, start, duration);
}
wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
"periodic NoA");
return 0;
}
void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
if (hapd->noa_enabled) {
wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
hostapd_driver_set_noa(hapd, 0, 0, 0);
}
}
void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
if (hapd->noa_enabled) {
wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
hapd->noa_duration);
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_P2P_MANAGER
u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
{
u8 bitmap;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
*eid++ = 4 + 3 + 1;
WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
eid += 4;
*eid++ = P2P_ATTR_MANAGEABILITY;
WPA_PUT_LE16(eid, 1);
eid += 2;
bitmap = P2P_MAN_DEVICE_MANAGEMENT;
if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
*eid++ = bitmap;
return eid;
}
#endif /* CONFIG_P2P_MANAGER */

View file

@ -0,0 +1,35 @@
/*
* hostapd / P2P integration
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef P2P_HOSTAPD_H
#define P2P_HOSTAPD_H
#ifdef CONFIG_P2P
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen);
int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
int duration);
void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
#else /* CONFIG_P2P */
static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
struct sta_info *sta,
char *buf, size_t buflen)
{
return 0;
}
#endif /* CONFIG_P2P */
u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
#endif /* P2P_HOSTAPD_H */

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

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

View file

@ -0,0 +1,273 @@
/*
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#ifdef CONFIG_RSN_PREAUTH
#include "utils/common.h"
#include "utils/eloop.h"
#include "l2_packet/l2_packet.h"
#include "common/wpa_common.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ieee802_1x.h"
#include "sta_info.h"
#include "wpa_auth.h"
#include "preauth_auth.h"
#ifndef ETH_P_PREAUTH
#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
#endif /* ETH_P_PREAUTH */
static const int dot11RSNAConfigPMKLifetime = 43200;
struct rsn_preauth_interface {
struct rsn_preauth_interface *next;
struct hostapd_data *hapd;
struct l2_packet_data *l2;
char *ifname;
int ifindex;
};
static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct rsn_preauth_interface *piface = ctx;
struct hostapd_data *hapd = piface->hapd;
struct ieee802_1x_hdr *hdr;
struct sta_info *sta;
struct l2_ethhdr *ethhdr;
wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
"from interface '%s'", piface->ifname);
if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
"(len=%lu)", (unsigned long) len);
return;
}
ethhdr = (struct l2_ethhdr *) buf;
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
if (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 */

View file

@ -0,0 +1,52 @@
/*
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef PREAUTH_H
#define PREAUTH_H
#ifdef CONFIG_RSN_PREAUTH
int rsn_preauth_iface_init(struct hostapd_data *hapd);
void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
int success);
void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len);
void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
#else /* CONFIG_RSN_PREAUTH */
static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
{
return 0;
}
static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
{
}
static inline void rsn_preauth_finished(struct hostapd_data *hapd,
struct sta_info *sta,
int success)
{
}
static inline void rsn_preauth_send(struct hostapd_data *hapd,
struct sta_info *sta,
u8 *buf, size_t len)
{
}
static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
struct sta_info *sta)
{
}
#endif /* CONFIG_RSN_PREAUTH */
#endif /* PREAUTH_H */

674
hostapd-2.9/src/ap/rrm.c Normal file
View 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
View 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 */

File diff suppressed because it is too large Load diff

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

View file

@ -0,0 +1,292 @@
/*
* hostapd / Client taxonomy
* Copyright (c) 2015 Google, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
* and render them to a descriptive string. The tag number of standard options
* is written to the string, while the vendor ID and subtag are written for
* vendor options.
*
* Example strings:
* 0,1,50,45,221(00904c,51)
* 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "sta_info.h"
#include "taxonomy.h"
/* Copy a string with no funny schtuff allowed; only alphanumerics. */
static void no_mischief_strncpy(char *dst, const char *src, size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
unsigned char s = src[i];
int is_lower = s >= 'a' && s <= 'z';
int is_upper = s >= 'A' && s <= 'Z';
int is_digit = s >= '0' && s <= '9';
if (is_lower || is_upper || is_digit) {
/* TODO: if any manufacturer uses Unicode within the
* WPS header, it will get mangled here. */
dst[i] = s;
} else {
/* Note that even spaces will be transformed to
* underscores, so 'Nexus 7' will turn into 'Nexus_7'.
* This is deliberate, to make the string easier to
* parse. */
dst[i] = '_';
}
}
}
static int get_wps_name(char *name, size_t name_len,
const u8 *data, size_t data_len)
{
/* Inside the WPS IE are a series of attributes, using two byte IDs
* and two byte lengths. We're looking for the model name, if
* present. */
while (data_len >= 4) {
u16 id, elen;
id = WPA_GET_BE16(data);
elen = WPA_GET_BE16(data + 2);
data += 4;
data_len -= 4;
if (elen > data_len)
return 0;
if (id == 0x1023) {
/* Model name, like 'Nexus 7' */
size_t n = (elen < name_len) ? elen : name_len;
no_mischief_strncpy(name, (const char *) data, n);
return n;
}
data += elen;
data_len -= elen;
}
return 0;
}
static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
{
char *fpos = fstr;
char *fend = fstr + fstr_len;
char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
#define MAX_EXTCAP 254
char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
*/
char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
#define WPS_NAME_LEN 32
char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
* NUL */
int num = 0;
const u8 *ie;
size_t ie_len;
int ret;
os_memset(htcap, 0, sizeof(htcap));
os_memset(htagg, 0, sizeof(htagg));
os_memset(htmcs, 0, sizeof(htmcs));
os_memset(vhtcap, 0, sizeof(vhtcap));
os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
os_memset(extcap, 0, sizeof(extcap));
os_memset(txpow, 0, sizeof(txpow));
os_memset(wps, 0, sizeof(wps));
*fpos = '\0';
if (!ies)
return;
ie = wpabuf_head(ies);
ie_len = wpabuf_len(ies);
while (ie_len >= 2) {
u8 id, elen;
char *sep = (num++ == 0) ? "" : ",";
id = *ie++;
elen = *ie++;
ie_len -= 2;
if (elen > ie_len)
break;
if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
/* Vendor specific */
if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
/* WPS */
char model_name[WPS_NAME_LEN + 1];
const u8 *data = &ie[4];
size_t data_len = elen - 4;
os_memset(model_name, 0, sizeof(model_name));
if (get_wps_name(model_name, WPS_NAME_LEN, data,
data_len)) {
os_snprintf(wps, sizeof(wps),
",wps:%s", model_name);
}
}
ret = os_snprintf(fpos, fend - fpos,
"%s%d(%02x%02x%02x,%d)",
sep, id, ie[0], ie[1], ie[2], ie[3]);
} else {
if (id == WLAN_EID_HT_CAP && elen >= 2) {
/* HT Capabilities (802.11n) */
os_snprintf(htcap, sizeof(htcap),
",htcap:%04hx",
WPA_GET_LE16(ie));
}
if (id == WLAN_EID_HT_CAP && elen >= 3) {
/* HT Capabilities (802.11n), A-MPDU information
*/
os_snprintf(htagg, sizeof(htagg),
",htagg:%02hx", (u16) ie[2]);
}
if (id == WLAN_EID_HT_CAP && elen >= 7) {
/* HT Capabilities (802.11n), MCS information */
os_snprintf(htmcs, sizeof(htmcs),
",htmcs:%08hx",
(u16) WPA_GET_LE32(ie + 3));
}
if (id == WLAN_EID_VHT_CAP && elen >= 4) {
/* VHT Capabilities (802.11ac) */
os_snprintf(vhtcap, sizeof(vhtcap),
",vhtcap:%08x",
WPA_GET_LE32(ie));
}
if (id == WLAN_EID_VHT_CAP && elen >= 8) {
/* VHT Capabilities (802.11ac), RX MCS
* information */
os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
",vhtrxmcs:%08x",
WPA_GET_LE32(ie + 4));
}
if (id == WLAN_EID_VHT_CAP && elen >= 12) {
/* VHT Capabilities (802.11ac), TX MCS
* information */
os_snprintf(vhttxmcs, sizeof(vhttxmcs),
",vhttxmcs:%08x",
WPA_GET_LE32(ie + 8));
}
if (id == WLAN_EID_EXT_CAPAB) {
/* Extended Capabilities */
int i;
int len = (elen < MAX_EXTCAP) ? elen :
MAX_EXTCAP;
char *p = extcap;
p += os_snprintf(extcap, sizeof(extcap),
",extcap:");
for (i = 0; i < len; i++) {
int lim;
lim = sizeof(extcap) -
os_strlen(extcap);
if (lim <= 0)
break;
p += os_snprintf(p, lim, "%02x",
*(ie + i));
}
}
if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
/* TX Power */
os_snprintf(txpow, sizeof(txpow),
",txpow:%04hx",
WPA_GET_LE16(ie));
}
ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
}
if (os_snprintf_error(fend - fpos, ret))
goto fail;
fpos += ret;
ie += elen;
ie_len -= elen;
}
ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
txpow, extcap, wps);
if (os_snprintf_error(fend - fpos, ret)) {
fail:
fstr[0] = '\0';
}
}
int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
struct sta_info *sta, char *buf, size_t buflen)
{
int ret;
char *pos, *end;
if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
return 0;
ret = os_snprintf(buf, buflen, "wifi4|probe:");
if (os_snprintf_error(buflen, ret))
return 0;
pos = buf + ret;
end = buf + buflen;
ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
pos = os_strchr(pos, '\0');
if (pos >= end)
return 0;
ret = os_snprintf(pos, end - pos, "|assoc:");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
pos = os_strchr(pos, '\0');
return pos - buf;
}
void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *ie, size_t ie_len)
{
wpabuf_free(sta->probe_ie_taxonomy);
sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
}
void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
struct hostapd_sta_info *info,
const u8 *ie, size_t ie_len)
{
wpabuf_free(info->probe_ie_taxonomy);
info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
}
void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *ie, size_t ie_len)
{
wpabuf_free(sta->assoc_ie_taxonomy);
sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
}

View file

@ -0,0 +1,24 @@
/*
* hostapd / Station client taxonomy
* Copyright (c) 2015 Google, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef TAXONOMY_H
#define TAXONOMY_H
void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *ie, size_t ie_len);
void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
struct hostapd_sta_info *sta,
const u8 *ie, size_t ie_len);
void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *ie, size_t ie_len);
int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
struct sta_info *sta, char *buf, size_t buflen);
#endif /* TAXONOMY_H */

View file

@ -0,0 +1,110 @@
/*
* hostapd / TKIP countermeasures
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "radius/radius.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_mlme.h"
#include "wpa_auth.h"
#include "ap_drv_ops.h"
#include "tkip_countermeasures.h"
static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
hapd->tkip_countermeasures = 0;
hostapd_drv_set_countermeasures(hapd, 0);
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
}
static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
{
struct sta_info *sta;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
wpa_auth_countermeasures_start(hapd->wpa_auth);
hapd->tkip_countermeasures = 1;
hostapd_drv_set_countermeasures(hapd, 1);
wpa_gtk_rekey(hapd->wpa_auth);
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
hapd, NULL);
while ((sta = hapd->sta_list)) {
sta->acct_terminate_cause =
RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
if (sta->flags & WLAN_STA_AUTH) {
mlme_deauthenticate_indication(
hapd, sta,
WLAN_REASON_MICHAEL_MIC_FAILURE);
}
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_MICHAEL_MIC_FAILURE);
ap_free_sta(hapd, sta);
}
}
void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
{
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
}
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
{
struct os_reltime now;
int ret = 0;
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Michael MIC failure detected in received frame%s",
local ? " (local)" : "");
if (addr && local) {
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta != NULL) {
wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Michael MIC failure detected in "
"received frame");
mlme_michaelmicfailure_indication(hapd, addr);
} else {
wpa_printf(MSG_DEBUG,
"MLME-MICHAELMICFAILURE.indication "
"for not associated STA (" MACSTR
") ignored", MAC2STR(addr));
return ret;
}
}
os_get_reltime(&now);
if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
hapd->michael_mic_failures = 1;
} else {
hapd->michael_mic_failures++;
if (hapd->michael_mic_failures > 1) {
ieee80211_tkip_countermeasures_start(hapd);
ret = 1;
}
}
hapd->michael_mic_failure = now;
return ret;
}

View file

@ -0,0 +1,15 @@
/*
* hostapd / TKIP countermeasures
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef TKIP_COUNTERMEASURES_H
#define TKIP_COUNTERMEASURES_H
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
#endif /* TKIP_COUNTERMEASURES_H */

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

@ -0,0 +1,34 @@
/*
* hostapd / VLAN definition
* Copyright (c) 2016, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "ap/vlan.h"
/* compare the two arguments, NULL is treated as empty
* return zero iff they are equal
*/
int vlan_compare(struct vlan_description *a, struct vlan_description *b)
{
int i;
const int a_empty = !a || !a->notempty;
const int b_empty = !b || !b->notempty;
if (a_empty && b_empty)
return 0;
if (a_empty || b_empty)
return 1;
if (a->untagged != b->untagged)
return 1;
for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
if (a->tagged[i] != b->tagged[i])
return 1;
}
return 0;
}

30
hostapd-2.9/src/ap/vlan.h Normal file
View file

@ -0,0 +1,30 @@
/*
* hostapd / VLAN definition
* Copyright (c) 2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef VLAN_H
#define VLAN_H
#define MAX_NUM_TAGGED_VLAN 32
struct vlan_description {
int notempty; /* 0 : no vlan information present, 1: else */
int untagged; /* >0 802.1q vid */
int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
};
#ifndef CONFIG_NO_VLAN
int vlan_compare(struct vlan_description *a, struct vlan_description *b);
#else /* CONFIG_NO_VLAN */
static inline int
vlan_compare(struct vlan_description *a, struct vlan_description *b)
{
return 0;
}
#endif /* CONFIG_NO_VLAN */
#endif /* VLAN_H */

View file

@ -0,0 +1,801 @@
/*
* hostapd / VLAN initialization - full dynamic VLAN
* Copyright 2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include <net/if.h>
/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
#undef if_type
#include <sys/ioctl.h>
#include "utils/common.h"
#include "drivers/priv_netlink.h"
#include "drivers/linux_ioctl.h"
#include "common/linux_bridge.h"
#include "common/linux_vlan.h"
#include "utils/eloop.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "wpa_auth.h"
#include "vlan_init.h"
#include "vlan_util.h"
struct full_dynamic_vlan {
int s; /* socket on which to listen for new/removed interfaces. */
};
#define DVLAN_CLEAN_BR 0x1
#define DVLAN_CLEAN_VLAN 0x2
#define DVLAN_CLEAN_VLAN_PORT 0x4
struct dynamic_iface {
char ifname[IFNAMSIZ + 1];
int usage;
int clean;
struct dynamic_iface *next;
};
/* Increment ref counter for ifname and add clean flag.
* If not in list, add it only if some flags are given.
*/
static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
int clean)
{
struct dynamic_iface *next, **dynamic_ifaces;
struct hapd_interfaces *interfaces;
interfaces = hapd->iface->interfaces;
dynamic_ifaces = &interfaces->vlan_priv;
for (next = *dynamic_ifaces; next; next = next->next) {
if (os_strcmp(ifname, next->ifname) == 0)
break;
}
if (next) {
next->usage++;
next->clean |= clean;
return;
}
if (!clean)
return;
next = os_zalloc(sizeof(*next));
if (!next)
return;
os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
next->usage = 1;
next->clean = clean;
next->next = *dynamic_ifaces;
*dynamic_ifaces = next;
}
/* Decrement reference counter for given ifname.
* Return clean flag iff reference counter was decreased to zero, else zero
*/
static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
{
struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
struct hapd_interfaces *interfaces;
int clean;
interfaces = hapd->iface->interfaces;
dynamic_ifaces = &interfaces->vlan_priv;
for (next = *dynamic_ifaces; next; next = next->next) {
if (os_strcmp(ifname, next->ifname) == 0)
break;
prev = next;
}
if (!next)
return 0;
next->usage--;
if (next->usage)
return 0;
if (prev)
prev->next = next->next;
else
*dynamic_ifaces = next->next;
clean = next->clean;
os_free(next);
return clean;
}
static int ifconfig_down(const char *if_name)
{
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
return ifconfig_helper(if_name, 0);
}
/* This value should be 256 ONLY. If it is something else, then hostapd
* might crash!, as this value has been hard-coded in 2.4.x kernel
* bridging code.
*/
#define MAX_BR_PORTS 256
static int br_delif(const char *br_name, const char *if_name)
{
int fd;
struct ifreq ifr;
unsigned long args[2];
int if_index;
wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
"failed: %s", __func__, strerror(errno));
return -1;
}
if (linux_br_del_if(fd, br_name, if_name) == 0)
goto done;
if_index = if_nametoindex(if_name);
if (if_index == 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
"interface index for '%s'",
__func__, if_name);
close(fd);
return -1;
}
args[0] = BRCTL_DEL_IF;
args[1] = if_index;
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *) args;
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
/* No error if interface already removed. */
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
"BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
"%s", __func__, br_name, if_name, strerror(errno));
close(fd);
return -1;
}
done:
close(fd);
return 0;
}
/*
Add interface 'if_name' to the bridge 'br_name'
returns -1 on error
returns 1 if the interface is already part of the bridge
returns 0 otherwise
*/
static int br_addif(const char *br_name, const char *if_name)
{
int fd;
struct ifreq ifr;
unsigned long args[2];
int if_index;
wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
"failed: %s", __func__, strerror(errno));
return -1;
}
if (linux_br_add_if(fd, br_name, if_name) == 0)
goto done;
if (errno == EBUSY) {
/* The interface is already added. */
close(fd);
return 1;
}
if_index = if_nametoindex(if_name);
if (if_index == 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
"interface index for '%s'",
__func__, if_name);
close(fd);
return -1;
}
args[0] = BRCTL_ADD_IF;
args[1] = if_index;
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *) args;
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
if (errno == EBUSY) {
/* The interface is already added. */
close(fd);
return 1;
}
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
"BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
"%s", __func__, br_name, if_name, strerror(errno));
close(fd);
return -1;
}
done:
close(fd);
return 0;
}
static int br_delbr(const char *br_name)
{
int fd;
unsigned long arg[2];
wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
"failed: %s", __func__, strerror(errno));
return -1;
}
if (linux_br_del(fd, br_name) == 0)
goto done;
arg[0] = BRCTL_DEL_BRIDGE;
arg[1] = (unsigned long) br_name;
if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
/* No error if bridge already removed. */
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
"%s: %s", __func__, br_name, strerror(errno));
close(fd);
return -1;
}
done:
close(fd);
return 0;
}
/*
Add a bridge with the name 'br_name'.
returns -1 on error
returns 1 if the bridge already exists
returns 0 otherwise
*/
static int br_addbr(const char *br_name)
{
int fd;
unsigned long arg[4];
struct ifreq ifr;
wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
"failed: %s", __func__, strerror(errno));
return -1;
}
if (linux_br_add(fd, br_name) == 0)
goto done;
if (errno == EEXIST) {
/* The bridge is already added. */
close(fd);
return 1;
}
arg[0] = BRCTL_ADD_BRIDGE;
arg[1] = (unsigned long) br_name;
if (ioctl(fd, SIOCGIFBR, arg) < 0) {
if (errno == EEXIST) {
/* The bridge is already added. */
close(fd);
return 1;
} else {
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
"failed for %s: %s",
__func__, br_name, strerror(errno));
close(fd);
return -1;
}
}
done:
/* Decrease forwarding delay to avoid EAPOL timeouts. */
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
arg[1] = 1;
arg[2] = 0;
arg[3] = 0;
ifr.ifr_data = (char *) &arg;
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: "
"BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
"%s: %s", __func__, br_name, strerror(errno));
/* Continue anyway */
}
close(fd);
return 0;
}
static int br_getnumports(const char *br_name)
{
int fd;
int i;
int port_cnt = 0;
unsigned long arg[4];
int ifindices[MAX_BR_PORTS];
struct ifreq ifr;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
"failed: %s", __func__, strerror(errno));
return -1;
}
arg[0] = BRCTL_GET_PORT_LIST;
arg[1] = (unsigned long) ifindices;
arg[2] = MAX_BR_PORTS;
arg[3] = 0;
os_memset(ifindices, 0, sizeof(ifindices));
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *) arg;
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
"failed for %s: %s",
__func__, br_name, strerror(errno));
close(fd);
return -1;
}
for (i = 1; i < MAX_BR_PORTS; i++) {
if (ifindices[i] > 0) {
port_cnt++;
}
}
close(fd);
return port_cnt;
}
static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
const char *br_name, int vid,
struct hostapd_data *hapd)
{
char vlan_ifname[IFNAMSIZ];
int clean;
int ret;
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
tagged_interface, vid);
else
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
vid);
if (ret >= (int) sizeof(vlan_ifname))
wpa_printf(MSG_WARNING,
"VLAN: Interface name was truncated to %s",
vlan_ifname);
clean = 0;
ifconfig_up(tagged_interface);
if (!vlan_add(tagged_interface, vid, vlan_ifname))
clean |= DVLAN_CLEAN_VLAN;
if (!br_addif(br_name, vlan_ifname))
clean |= DVLAN_CLEAN_VLAN_PORT;
dyn_iface_get(hapd, vlan_ifname, clean);
ifconfig_up(vlan_ifname);
}
static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
struct hostapd_vlan *vlan, int vid)
{
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
int ret;
if (vlan->bridge[0]) {
os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
ret = 0;
} else if (hapd->conf->vlan_bridge[0]) {
ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
hapd->conf->vlan_bridge, vid);
} else if (tagged_interface) {
ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
tagged_interface, vid);
} else {
ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
}
if (ret >= IFNAMSIZ)
wpa_printf(MSG_WARNING,
"VLAN: Interface name was truncated to %s",
br_name);
}
static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
int vid)
{
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
int vlan_naming = hapd->conf->ssid.vlan_naming;
dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
ifconfig_up(br_name);
if (tagged_interface)
vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
vid, hapd);
}
void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
{
char br_name[IFNAMSIZ];
struct hostapd_vlan *vlan;
int untagged, *tagged, i, notempty;
wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
if (vlan->configured ||
os_strcmp(ifname, vlan->ifname) != 0)
continue;
break;
}
if (!vlan)
return;
vlan->configured = 1;
notempty = vlan->vlan_desc.notempty;
untagged = vlan->vlan_desc.untagged;
tagged = vlan->vlan_desc.tagged;
if (!notempty) {
/* Non-VLAN STA */
if (hapd->conf->bridge[0] &&
!br_addif(hapd->conf->bridge, ifname))
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
vlan_bridge_name(br_name, hapd, vlan, untagged);
vlan_get_bridge(br_name, hapd, untagged);
if (!br_addif(br_name, ifname))
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
}
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
if (tagged[i] == untagged ||
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
(i > 0 && tagged[i] == tagged[i - 1]))
continue;
vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
vlan_get_bridge(br_name, hapd, tagged[i]);
vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
ifname, br_name, tagged[i], hapd);
}
ifconfig_up(ifname);
}
static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
const char *br_name, int vid,
struct hostapd_data *hapd)
{
char vlan_ifname[IFNAMSIZ];
int clean;
int ret;
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
tagged_interface, vid);
else
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
vid);
if (ret >= (int) sizeof(vlan_ifname))
wpa_printf(MSG_WARNING,
"VLAN: Interface name was truncated to %s",
vlan_ifname);
clean = dyn_iface_put(hapd, vlan_ifname);
if (clean & DVLAN_CLEAN_VLAN_PORT)
br_delif(br_name, vlan_ifname);
if (clean & DVLAN_CLEAN_VLAN) {
ifconfig_down(vlan_ifname);
vlan_rem(vlan_ifname);
}
}
static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
int vid)
{
int clean;
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
int vlan_naming = hapd->conf->ssid.vlan_naming;
if (tagged_interface)
vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
vid, hapd);
clean = dyn_iface_put(hapd, br_name);
if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
ifconfig_down(br_name);
br_delbr(br_name);
}
}
void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
{
struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
first = prev = vlan;
while (vlan) {
if (os_strcmp(ifname, vlan->ifname) != 0) {
prev = vlan;
vlan = vlan->next;
continue;
}
break;
}
if (!vlan)
return;
if (vlan->configured) {
int notempty = vlan->vlan_desc.notempty;
int untagged = vlan->vlan_desc.untagged;
int *tagged = vlan->vlan_desc.tagged;
char br_name[IFNAMSIZ];
int i;
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
if (tagged[i] == untagged ||
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
(i > 0 && tagged[i] == tagged[i - 1]))
continue;
vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
ifname, br_name, tagged[i], hapd);
vlan_put_bridge(br_name, hapd, tagged[i]);
}
if (!notempty) {
/* Non-VLAN STA */
if (hapd->conf->bridge[0] &&
(vlan->clean & DVLAN_CLEAN_WLAN_PORT))
br_delif(hapd->conf->bridge, ifname);
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
vlan_bridge_name(br_name, hapd, vlan, untagged);
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
br_delif(br_name, vlan->ifname);
vlan_put_bridge(br_name, hapd, untagged);
}
}
/*
* Ensure this VLAN interface is actually removed even if
* NEWLINK message is only received later.
*/
if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
wpa_printf(MSG_ERROR,
"VLAN: Could not remove VLAN iface: %s: %s",
vlan->ifname, strerror(errno));
if (vlan == first)
hapd->conf->vlan = vlan->next;
else
prev->next = vlan->next;
os_free(vlan);
}
static void
vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
struct hostapd_data *hapd)
{
struct ifinfomsg *ifi;
int attrlen, nlmsg_len, rta_len;
struct rtattr *attr;
char ifname[IFNAMSIZ + 1];
if (len < sizeof(*ifi))
return;
ifi = NLMSG_DATA(h);
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
attrlen = h->nlmsg_len - nlmsg_len;
if (attrlen < 0)
return;
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
os_memset(ifname, 0, sizeof(ifname));
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
int n = attr->rta_len - rta_len;
if (n < 0)
break;
if ((size_t) n >= sizeof(ifname))
n = sizeof(ifname) - 1;
os_memcpy(ifname, ((char *) attr) + rta_len, n);
}
attr = RTA_NEXT(attr, attrlen);
}
if (!ifname[0])
return;
if (del && if_nametoindex(ifname)) {
/* interface still exists, race condition ->
* iface has just been recreated */
return;
}
wpa_printf(MSG_DEBUG,
"VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
del ? "DEL" : "NEW",
ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
if (del)
vlan_dellink(ifname, hapd);
else
vlan_newlink(ifname, hapd);
}
static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
char buf[8192];
int left;
struct sockaddr_nl from;
socklen_t fromlen;
struct nlmsghdr *h;
struct hostapd_data *hapd = eloop_ctx;
fromlen = sizeof(from);
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *) &from, &fromlen);
if (left < 0) {
if (errno != EINTR && errno != EAGAIN)
wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
__func__, strerror(errno));
return;
}
h = (struct nlmsghdr *) buf;
while (NLMSG_OK(h, left)) {
int len, plen;
len = h->nlmsg_len;
plen = len - sizeof(*h);
if (len > left || plen < 0) {
wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
"message: len=%d left=%d plen=%d",
len, left, plen);
break;
}
switch (h->nlmsg_type) {
case RTM_NEWLINK:
vlan_read_ifnames(h, plen, 0, hapd);
break;
case RTM_DELLINK:
vlan_read_ifnames(h, plen, 1, hapd);
break;
}
h = NLMSG_NEXT(h, left);
}
if (left > 0) {
wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
"netlink message", __func__, left);
}
}
struct full_dynamic_vlan *
full_dynamic_vlan_init(struct hostapd_data *hapd)
{
struct sockaddr_nl local;
struct full_dynamic_vlan *priv;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (priv->s < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
"NETLINK_ROUTE) failed: %s",
__func__, strerror(errno));
os_free(priv);
return NULL;
}
os_memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = RTMGRP_LINK;
if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
__func__, strerror(errno));
close(priv->s);
os_free(priv);
return NULL;
}
if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
{
close(priv->s);
os_free(priv);
return NULL;
}
return priv;
}
void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
{
if (priv == NULL)
return;
eloop_unregister_read_sock(priv->s);
close(priv->s);
os_free(priv);
}

View file

@ -0,0 +1,69 @@
/*
* hostapd / VLAN ifconfig helpers
* Copyright 2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include <net/if.h>
#include <sys/ioctl.h>
#include "utils/common.h"
#include "vlan_util.h"
int ifconfig_helper(const char *if_name, int up)
{
int fd;
struct ifreq ifr;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
"failed: %s", __func__, strerror(errno));
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
"for interface %s: %s",
__func__, if_name, strerror(errno));
close(fd);
return -1;
}
if (up)
ifr.ifr_flags |= IFF_UP;
else
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
"for interface %s (up=%d): %s",
__func__, if_name, up, strerror(errno));
close(fd);
return -1;
}
close(fd);
return 0;
}
int ifconfig_up(const char *if_name)
{
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
return ifconfig_helper(if_name, 1);
}
int iface_exists(const char *ifname)
{
return if_nametoindex(ifname);
}

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

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

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

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

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

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

File diff suppressed because it is too large Load diff

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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

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

File diff suppressed because it is too large Load diff

View 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