Clean up HT40 scan and share nl80211 scanning code

Instead of adding a new driver_ops for fetching neighbor BSS data (that
nl80211 driver interface had to scan during initialization), share the
same scan operations that wpa_supplicant is using. This gets rid of
duplicated scan code in driver_nl80211.c (and better yet, removes large
part of old WEXT code).

hostapd interface initialization is now completed in a callback, if
needed, i.e., he_features channel/hw_mode selection can use as much time
as needed. This can also help with radar detection in the future.
master
Jouni Malinen 15 years ago committed by Jouni Malinen
parent 814aaa844a
commit ad1e68e6b5

@ -36,7 +36,6 @@ hostapd_driver_init(struct hostapd_data *hapd, const u8 *bssid)
params.ssid_len = hapd->conf->ssid.ssid_len;
params.test_socket = hapd->conf->test_socket;
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
params.ht_40mhz_scan = hapd->iconf->secondary_channel != 0;
params.num_bridge = hapd->iface->num_bss;
params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *));
@ -553,12 +552,27 @@ hostapd_set_wps_probe_resp_ie(struct hostapd_data *hapd, const u8 *ie,
hapd->drv_priv, ie, len);
}
static inline const struct hostapd_neighbor_bss *
hostapd_driver_get_neighbor_bss(struct hostapd_data *hapd, size_t *num)
static inline int hostapd_driver_set_mode(struct hostapd_data *hapd, int mode)
{
if (hapd->driver == NULL || hapd->driver->get_neighbor_bss == NULL)
return NULL;
return hapd->driver->get_neighbor_bss(hapd->drv_priv, num);
if (hapd->driver == NULL || hapd->driver->set_mode == NULL)
return 0;
return hapd->driver->set_mode(hapd->drv_priv, mode);
}
static inline 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;
}
static inline 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;
}
#endif /* DRIVER_I_H */

@ -294,3 +294,25 @@ struct hostapd_data * hostapd_sta_get_bss(struct hostapd_data *hapd,
return NULL;
}
#ifndef CONFIG_AP
void wpa_supplicant_event(void *ctx, wpa_event_type event,
union wpa_event_data *data)
{
struct hostapd_data *hapd = ctx;
switch (event) {
case EVENT_MICHAEL_MIC_FAILURE:
michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
break;
case EVENT_SCAN_RESULTS:
if (hapd->iface->scan_cb)
hapd->iface->scan_cb(hapd->iface);
break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
}
}
#endif /* CONFIG_AP */

@ -1314,9 +1314,6 @@ static int setup_interface(struct hostapd_iface *iface)
size_t i;
char country[4];
u8 *b = conf->bssid;
int freq;
size_t j;
u8 *prev_addr;
/*
* Initialize the driver interface and make sure that all BSSes get
@ -1337,6 +1334,11 @@ static int setup_interface(struct hostapd_iface *iface)
iface->bss[i]->drv_priv = hapd->drv_priv;
}
if (hostapd_driver_set_mode(hapd, IEEE80211_MODE_AP)) {
wpa_printf(MSG_ERROR, "Failed to set driver in AP mode");
return -1;
}
if (hostapd_validate_bssid_configuration(iface))
return -1;
@ -1379,8 +1381,33 @@ static int setup_interface(struct hostapd_iface *iface)
"channel. (%d)", ret);
return -1;
}
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
return -1;
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will "
"be completed in a callback");
return 0;
}
}
return hostapd_setup_interface_complete(iface, 0);
}
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
{
struct hostapd_data *hapd = iface->bss[0];
int freq;
size_t j;
u8 *prev_addr;
if (err) {
wpa_printf(MSG_ERROR, "Interface initialization failed");
eloop_terminate();
return -1;
}
wpa_printf(MSG_DEBUG, "Completing interface initialization");
if (hapd->iconf->channel) {
freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d "
@ -1436,6 +1463,9 @@ static int setup_interface(struct hostapd_iface *iface)
return -1;
}
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
return 0;
}
@ -1460,9 +1490,6 @@ int hostapd_setup_interface(struct hostapd_iface *iface)
iface->bss[0]->conf->iface);
eloop_terminate();
return -1;
} else if (!hostapd_drv_none(iface->bss[0])) {
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
}
return 0;

@ -154,9 +154,8 @@ struct hostapd_iface {
/* Overlapping BSS information */
int olbc_ht;
#ifdef CONFIG_IEEE80211N
u16 ht_op_mode;
#endif /* CONFIG_IEEE80211N */
void (*scan_cb)(struct hostapd_iface *iface);
};
int hostapd_reload_config(struct hostapd_iface *iface);
@ -165,6 +164,7 @@ 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);
int handle_reload_iface(struct hostapd_iface *iface, void *ctx);
int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx);

@ -18,6 +18,8 @@
#include "hostapd.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
#include "eloop.h"
#include "hw_features.h"
#include "driver_i.h"
#include "config.h"
@ -249,11 +251,37 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
}
static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
int *pri_chan, int *sec_chan)
{
struct ieee80211_ht_operation *oper;
struct ieee802_11_elems elems;
*pri_chan = *sec_chan = 0;
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
if (elems.ht_operation &&
elems.ht_operation_len >= sizeof(*oper)) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
*pri_chan = oper->control_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
if (oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
*sec_chan = *pri_chan + 4;
else if (oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
*sec_chan = *pri_chan - 4;
}
}
}
static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
const struct hostapd_neighbor_bss *n;
size_t i, num;
int bss_pri_chan, bss_sec_chan;
size_t i;
int match;
pri_chan = iface->conf->channel;
@ -264,17 +292,16 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
else
sec_freq = pri_freq - 20;
n = hostapd_driver_get_neighbor_bss(iface->bss[0], &num);
/*
* Switch PRI/SEC channels if Beacons were detected on selected SEC
* channel, but not on selected PRI channel.
*/
pri_bss = sec_bss = 0;
for (i = 0; n && i < num; i++) {
if (n[i].freq == pri_freq)
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
if (bss->freq == pri_freq)
pri_bss++;
else if (n[i].freq == sec_freq)
else if (bss->freq == sec_freq)
sec_bss++;
}
if (sec_bss && !pri_bss) {
@ -290,21 +317,25 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
* existing BSSes, use own preference).
*/
match = 0;
for (i = 0; n && i < num; i++) {
if (pri_chan == n[i].pri_chan &&
sec_chan == n[i].sec_chan) {
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
if (pri_chan == bss_pri_chan &&
sec_chan == bss_sec_chan) {
match = 1;
break;
}
}
if (!match) {
for (i = 0; n && i < num; i++) {
if (pri_chan == n[i].sec_chan &&
sec_chan == n[i].pri_chan) {
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
if (pri_chan == bss_sec_chan &&
sec_chan == bss_pri_chan) {
wpa_printf(MSG_INFO, "Switch own primary and "
"secondary channel due to BSS "
"overlap with " MACSTR,
MAC2STR(n[i].bssid));
MAC2STR(bss->bssid));
ieee80211n_switch_pri_sec(iface);
break;
}
@ -315,12 +346,12 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
}
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
int pri_freq, sec_freq;
int affected_start, affected_end;
const struct hostapd_neighbor_bss *n;
size_t i, num;
size_t i;
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
if (iface->conf->secondary_channel > 0)
@ -331,12 +362,16 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
affected_end = (pri_freq + sec_freq) / 2 + 25;
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
affected_start, affected_end);
n = hostapd_driver_get_neighbor_bss(iface->bss[0], &num);
for (i = 0; n && i < num; i++) {
int pri = n[i].freq;
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
int pri = bss->freq;
int sec = pri;
if (n[i].sec_chan) {
if (n[i].sec_chan < n[i].pri_chan)
int sec_chan, pri_chan;
ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
if (sec_chan) {
if (sec_chan < pri_chan)
sec = pri - 20;
else
sec = pri + 20;
@ -346,13 +381,17 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
(sec < affected_start || sec > affected_end))
continue; /* not within affected channel range */
if (n[i].sec_chan) {
wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
" freq=%d pri=%d sec=%d",
MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
if (sec_chan) {
if (pri_freq != pri || sec_freq != sec) {
wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
"mismatch with BSS " MACSTR
" <%d,%d> (chan=%d%c) vs. <%d,%d>",
MAC2STR(n[i].bssid),
pri, sec, n[i].pri_chan,
MAC2STR(bss->bssid),
pri, sec, pri_chan,
sec > pri ? '+' : '-',
pri_freq, sec_freq);
return 0;
@ -366,20 +405,27 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
}
static void ieee80211n_check_40mhz(struct hostapd_iface *iface)
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
int oper40;
if (!iface->conf->secondary_channel)
return; /* HT40 not used */
/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
* allowed per IEEE 802.11n/D7.0, 11.14.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);
oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
else
oper40 = ieee80211n_check_40mhz_2g4(iface);
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
wpa_scan_results_free(scan_res);
if (!oper40) {
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
@ -390,6 +436,30 @@ static void ieee80211n_check_40mhz(struct hostapd_iface *iface)
iface->conf->secondary_channel = 0;
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
}
hostapd_setup_interface_complete(iface, 0);
}
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
if (!iface->conf->secondary_channel)
return 0; /* HT40 not used */
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
"40 MHz channel");
os_memset(&params, 0, sizeof(params));
/* TODO: scan only the needed frequency */
if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
wpa_printf(MSG_ERROR, "Failed to request a scan of "
"neighboring BSSes");
return -1;
}
iface->scan_cb = ieee80211n_check_scan;
return 1;
}
@ -492,8 +562,26 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
return 1;
}
#endif /* CONFIG_IEEE80211N */
int hostapd_check_ht_capab(struct hostapd_iface *iface)
{
#ifdef CONFIG_IEEE80211N
int ret;
ret = ieee80211n_check_40mhz(iface);
if (ret)
return ret;
if (!ieee80211n_allowed_ht40_channel_pair(iface))
return -1;
if (!ieee80211n_supported_ht_capab(iface))
return -1;
#endif /* CONFIG_IEEE80211N */
return 0;
}
/**
* hostapd_select_hw_mode - Select the hardware mode
@ -558,14 +646,6 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
return -1;
}
#ifdef CONFIG_IEEE80211N
ieee80211n_check_40mhz(iface);
if (!ieee80211n_allowed_ht40_channel_pair(iface))
return -1;
if (!ieee80211n_supported_ht_capab(iface))
return -1;
#endif /* CONFIG_IEEE80211N */
if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,

@ -24,6 +24,7 @@ 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);
#else /* NEED_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@ -50,6 +51,12 @@ 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;
}
#endif /* NEED_MLME */
#endif /* HW_FEATURES_H */

@ -61,6 +61,7 @@ struct hostapd_hw_modes {
#define IEEE80211_MODE_INFRA 0
#define IEEE80211_MODE_IBSS 1
#define IEEE80211_MODE_AP 2
#define IEEE80211_CAP_ESS 0x0001
#define IEEE80211_CAP_IBSS 0x0002
@ -494,14 +495,6 @@ enum hostapd_driver_if_type {
HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS
};
struct hostapd_neighbor_bss {
u8 bssid[ETH_ALEN];
int freq; /* MHz */
unsigned int ht:1;
int pri_chan;
int sec_chan; /* 0 for 20 MHz channels */
};
struct wpa_init_params {
const u8 *bssid;
const char *ifname;
@ -509,7 +502,6 @@ struct wpa_init_params {
size_t ssid_len;
const char *test_socket;
int use_pae_group_addr;
int ht_40mhz_scan;
char **bridge;
size_t num_bridge;
};
@ -1341,9 +1333,6 @@ struct wpa_driver_ops {
const u8 *ie, size_t len);
int (*set_wps_probe_resp_ie)(const char *ifname, void *priv,
const u8 *ie, size_t len);
const struct hostapd_neighbor_bss *
(*get_neighbor_bss)(void *priv, size_t *num);
};
/**
@ -1589,6 +1578,7 @@ union wpa_event_data {
*/
struct michael_mic_failure {
int unicast;
const u8 *src;
} michael_mic_failure;
/**

@ -3266,6 +3266,5 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
NULL /* set_radius_acl_expire */,
NULL /* set_ht_params */,
NULL /* set_wps_beacon_ie */,
NULL /* set_wps_probe_resp_ie */,
NULL /* get_neighbor_bss */
NULL /* set_wps_probe_resp_ie */
};

@ -110,6 +110,8 @@ struct wpa_driver_nl80211_data {
int associated;
u8 ssid[32];
size_t ssid_len;
int nlmode;
int ap_scan_as_station;
#if defined(CONFIG_AP) || defined(HOSTAPD)
int beacon_int;
@ -131,20 +133,16 @@ struct wpa_driver_nl80211_data {
int num_if_indices;
struct i802_bss bss;
unsigned int ht_40mhz_scan:1;
int last_freq;
int last_freq_ht;
struct hostapd_neighbor_bss *neighbors;
size_t num_neighbors;
#endif /* HOSTAPD */
};
static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
void *timeout_ctx);
static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
int mode);
static int wpa_driver_nl80211_set_mode(void *priv, int mode);
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
@ -740,11 +738,6 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv,
static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
struct nlattr *tb[])
{
#ifdef HOSTAPD
if (tb[NL80211_ATTR_MAC])
hostapd_michael_mic_failure(drv->hapd,
nla_data(tb[NL80211_ATTR_MAC]));
#else /* HOSTAPD */
union wpa_event_data data;
wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
@ -753,6 +746,7 @@ static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
nla_data(tb[NL80211_ATTR_MAC]),
nla_len(tb[NL80211_ATTR_MAC]));
data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
}
if (tb[NL80211_ATTR_KEY_SEQ]) {
wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
@ -774,7 +768,6 @@ static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
}
wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
#endif /* HOSTAPD */
}
@ -797,8 +790,14 @@ static int process_event(struct nl_msg *msg, void *arg)
}
}
if (drv->ap_scan_as_station &&
(gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP);
drv->ap_scan_as_station = 0;
}
switch (gnlh->cmd) {
#ifndef HOSTAPD
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
drv->scan_complete_events = 1;
@ -816,6 +815,7 @@ static int process_event(struct nl_msg *msg, void *arg)
drv->ctx);
wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
break;
#ifndef HOSTAPD
case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE:
case NL80211_CMD_DEAUTHENTICATE:
@ -1174,7 +1174,7 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
{
drv->ifindex = if_nametoindex(drv->ifname);
if (wpa_driver_nl80211_set_mode(drv, 0) < 0) {
if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
"use managed mode");
}
@ -1222,7 +1222,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
eloop_unregister_read_sock(drv->link_event_sock);
hostapd_set_iface_flags(drv, drv->ifname, 0);
wpa_driver_nl80211_set_mode(drv, 0);
wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
close(drv->link_event_sock);
close(drv->ioctl_sock);
@ -1239,7 +1239,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
/**
* wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
* @eloop_ctx: Unused
* @eloop_ctx: Driver private data
* @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
*
* This function can be used as registered timeout when starting a scan to
@ -1247,16 +1247,19 @@ static void wpa_driver_nl80211_deinit(void *priv)
*/
static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
if (drv->ap_scan_as_station) {
wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP);
drv->ap_scan_as_station = 0;
}
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
#ifndef HOSTAPD
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
#endif /* HOSTAPD */
}
/**
* wpa_driver_nl80211_scan - Request the driver to initiate scan
* @priv: Pointer to private wext data from wpa_driver_nl80211_init()
* @priv: Pointer to private driver data from wpa_driver_nl80211_init()
* @params: Scan parameters
* Returns: 0 on success, -1 on failure
*/
@ -1306,7 +1309,30 @@ static int wpa_driver_nl80211_scan(void *priv,
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
"(%s)", ret, strerror(-ret));
#ifdef HOSTAPD
if (drv->nlmode == NL80211_IFTYPE_AP) {
/*
* mac80211 does not allow scan requests in AP mode, so
* try to do this in station mode.
*/
if (wpa_driver_nl80211_set_mode(drv,
IEEE80211_MODE_INFRA))
goto nla_put_failure;
if (wpa_driver_nl80211_scan(drv, params)) {
wpa_driver_nl80211_set_mode(drv,
IEEE80211_MODE_AP);
goto nla_put_failure;
}
/* Restore AP mode when processing scan results */
drv->ap_scan_as_station = 1;
ret = 0;
} else
goto nla_put_failure;
#else /* HOSTAPD */
goto nla_put_failure;
#endif /* HOSTAPD */
}
/* Not all drivers generate "scan completed" wireless event, so try to
@ -2869,17 +2895,35 @@ nla_put_failure:
}
/**
* wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc)
* @drv: Pointer to private driver data from wpa_driver_nl80211_init()
* @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
* Returns: 0 on success, -1 on failure
*/
static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
int mode)
static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
int ifindex, int mode)
{
int ret = -1;
struct nl_msg *msg;
int ret = -ENOBUFS;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
0, NL80211_CMD_SET_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (!ret)
return 0;
nla_put_failure:
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
" %d (%s)", ifindex, mode, ret, strerror(-ret));
return ret;
}
static int wpa_driver_nl80211_set_mode(void *priv, int mode)
{
struct wpa_driver_nl80211_data *drv = priv;
int ret = -1;
int nlmode;
switch (mode) {
@ -2896,52 +2940,28 @@ static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
return -1;
}
msg = nlmsg_alloc();
if (!msg)
return -1;
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
0, NL80211_CMD_SET_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (!ret)
if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
drv->nlmode = nlmode;
return 0;
else
goto try_again;
}
nla_put_failure:
wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)",
ret, strerror(-ret));
return -1;
if (nlmode == drv->nlmode)
return 0; /* Already in the requested mode */
try_again:
/* mac80211 doesn't allow mode changes while the device is up, so
* take the device down, try to set the mode again, and bring the
* device back up.
*/
if (hostapd_set_iface_flags(drv, drv->ifname, 0) == 0) {
/* Try to set the mode again while the interface is down */
msg = nlmsg_alloc();
if (!msg)
return -1;
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
0, NL80211_CMD_SET_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
wpa_printf(MSG_ERROR, "Failed to set interface %s "
"mode(try_again): %d (%s)",
drv->ifname, ret, strerror(-ret));
}
ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
if (hostapd_set_iface_flags(drv, drv->ifname, 1))
ret = -1;
}
if (!ret)
drv->nlmode = nlmode;
return ret;
}
@ -3755,492 +3775,6 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
}
static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, const char *ifname,
int mode)
{
struct nl_msg *msg;
int ret = -ENOBUFS;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
0, NL80211_CMD_SET_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(ifname));
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (!ret)
return 0;
nla_put_failure:
wpa_printf(MSG_ERROR, "Failed to set interface %s to master "
"mode.", ifname);
return ret;
}
#ifdef CONFIG_IEEE80211N
static void i802_add_neighbor(struct wpa_driver_nl80211_data *drv, u8 *bssid,
int freq, u8 *ie, size_t ie_len)
{
struct ieee802_11_elems elems;
int ht, pri_chan = 0, sec_chan = 0;
struct ieee80211_ht_operation *oper;
struct hostapd_neighbor_bss *nnei;
ieee802_11_parse_elems(ie, ie_len, &elems, 0);
ht = elems.ht_capabilities || elems.ht_operation;
if (elems.ht_operation && elems.ht_operation_len >= sizeof(*oper)) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
pri_chan = oper->control_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
if (oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
sec_chan = pri_chan + 4;
else if (oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
sec_chan = pri_chan - 4;
}
}
wpa_printf(MSG_DEBUG, "nl80211: Neighboring BSS - bssid=" MACSTR
" freq=%d MHz HT=%d pri_chan=%d sec_chan=%d",
MAC2STR(bssid), freq, ht, pri_chan, sec_chan);
nnei = os_realloc(drv->neighbors, (drv->num_neighbors + 1) *
sizeof(struct hostapd_neighbor_bss));
if (nnei == NULL)
return;
drv->neighbors = nnei;
nnei = &nnei[drv->num_neighbors];
os_memcpy(nnei->bssid, bssid, ETH_ALEN);
nnei->freq = freq;
nnei->ht = !!ht;
nnei->pri_chan = pri_chan;
nnei->sec_chan = sec_chan;
drv->num_neighbors++;
}
static int i802_get_scan_freq(struct iw_event *iwe, int *freq)
{
int divi = 1000000, i;
if (iwe->u.freq.e == 0) {
/*
* Some drivers do not report frequency, but a channel.
* Try to map this to frequency by assuming they are using
* IEEE 802.11b/g. But don't overwrite a previously parsed
* frequency if the driver sends both frequency and channel,
* since the driver may be sending an A-band channel that we
* don't handle here.
*/
if (*freq)
return 0;
if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
*freq = 2407 + 5 * iwe->u.freq.m;
return 0;
} else if (iwe->u.freq.m == 14) {
*freq = 2484;
return 0;
}
}
if (iwe->u.freq.e > 6) {
wpa_printf(MSG_DEBUG, "Invalid freq in scan results: "
"m=%d e=%d", iwe->u.freq.m, iwe->u.freq.e);
return -1;
}
for (i = 0; i < iwe->u.freq.e; i++)
divi /= 10;
*freq = iwe->u.freq.m / divi;
return 0;
}
static int i802_parse_scan(struct wpa_driver_nl80211_data *drv, u8 *res_buf,
size_t len)
{
size_t ap_num = 0;
int first;
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom;
u8 bssid[ETH_ALEN];
int freq = 0;
u8 *ie = NULL;
size_t ie_len = 0;
ap_num = 0;
first = 1;
pos = (char *) res_buf;
end = (char *) res_buf + len;
while (pos + IW_EV_LCP_LEN <= end) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
if (iwe->len <= IW_EV_LCP_LEN)
break;
custom = pos + IW_EV_POINT_LEN;
if (iwe->cmd == IWEVGENIE) {
/* WE-19 removed the pointer from struct iw_point */
char *dpos = (char *) &iwe_buf.u.data.length;
int dlen = dpos - (char *) &iwe_buf;
os_memcpy(dpos, pos + IW_EV_LCP_LEN,
sizeof(struct iw_event) - dlen);
} else {
os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
custom += IW_EV_POINT_OFF;
}
switch (iwe->cmd) {
case SIOCGIWAP:
if (!first)
i802_add_neighbor(drv, bssid, freq, ie,
ie_len);
first = 0;
os_memcpy(bssid, iwe->u.ap_addr.sa_data, ETH_ALEN);
freq = 0;
ie = NULL;
ie_len = 0;
break;
case SIOCGIWFREQ:
i802_get_scan_freq(iwe, &freq);
break;
case IWEVGENIE:
if (custom + iwe->u.data.length > end) {
wpa_printf(MSG_ERROR, "IWEVGENIE overflow");
return -1;
}
ie = (u8 *) custom;
ie_len = iwe->u.data.length;
break;
}
pos += iwe->len;
}
if (!first)
i802_add_neighbor(drv, bssid, freq, ie, ie_len);
return 0;
}
static int i802_get_ht_scan_res(struct wpa_driver_nl80211_data *drv)
{
struct iwreq iwr;
u8 *res_buf;
size_t res_buf_len;
int res;
res_buf_len = IW_SCAN_MAX_DATA;
for (;;) {
res_buf = os_malloc(res_buf_len);
if (res_buf == NULL)
return -1;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.data.pointer = res_buf;
iwr.u.data.length = res_buf_len;
if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
break;
if (errno == E2BIG && res_buf_len < 65535) {
os_free(res_buf);
res_buf = NULL;
res_buf_len *= 2;
if (res_buf_len > 65535)
res_buf_len = 65535; /* 16-bit length field */
wpa_printf(MSG_DEBUG, "Scan results did not fit - "
"trying larger buffer (%lu bytes)",
(unsigned long) res_buf_len);
} else {
perror("ioctl[SIOCGIWSCAN]");
os_free(res_buf);
return -1;
}
}
if (iwr.u.data.length > res_buf_len) {
os_free(res_buf);
return -1;
}
res = i802_parse_scan(drv, res_buf, iwr.u.data.length);
os_free(res_buf);
return res;
}
static int i802_is_event_wireless_scan_complete(char *data, int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end;
pos = data;
end = data + len;
while (pos + IW_EV_LCP_LEN <= end) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
if (iwe->cmd == SIOCGIWSCAN)
return 1;
pos += iwe->len;
}
return 0;
}
static int i802_is_rtm_scan_complete(int ifindex, struct nlmsghdr *h, int len)
{
struct ifinfomsg *ifi;
int attrlen, _nlmsg_len, rta_len;
struct rtattr *attr;
if (len < (int) sizeof(*ifi))
return 0;
ifi = NLMSG_DATA(h);
if (ifindex != ifi->ifi_index)
return 0; /* event for foreign ifindex */
_nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
attrlen = h->nlmsg_len - _nlmsg_len;
if (attrlen < 0)
return 0;
attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_WIRELESS &&
i802_is_event_wireless_scan_complete(
((char *) attr) + rta_len,
attr->rta_len - rta_len))
return 1;
attr = RTA_NEXT(attr, attrlen);
}
return 0;
}
static int i802_is_scan_complete(int s, int ifindex)
{
char buf[1024];
int left;
struct nlmsghdr *h;
left = recv(s, buf, sizeof(buf), MSG_DONTWAIT);
if (left < 0) {
perror("recv(netlink)");
return 0;
}
h = (struct nlmsghdr *) buf;
while (left >= (int) sizeof(*h)) {
int len, plen;
len = h->nlmsg_len;
plen = len - sizeof(*h);
if (len > left || plen < 0) {
wpa_printf(MSG_DEBUG, "Malformed netlink message: "
"len=%d left=%d plen=%d",
len, left, plen);
break;
}
switch (h->nlmsg_type) {
case RTM_NEWLINK:
if (i802_is_rtm_scan_complete(ifindex, h, plen))
return 1;
break;
}
len = NLMSG_ALIGN(len);
left -= len;
h = (struct nlmsghdr *) ((char *) h + len);
}
return 0;
}
static int i802_ht_scan(struct wpa_driver_nl80211_data *drv)
{
struct iwreq iwr;
int s, res, ifindex;
struct sockaddr_nl local;
time_t now, end;
fd_set rfds;
struct timeval tv;
wpa_printf(MSG_DEBUG, "nl80211: Scanning overlapping BSSes before "
"starting HT 20/40 MHz BSS");
/* Request a new scan */
/* TODO: would be enough to scan the selected band */
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
perror("ioctl[SIOCSIWSCAN]");
return -1;
}
ifindex = if_nametoindex(drv->ifname);
/* Wait for scan completion event or timeout */
s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (s < 0) {
perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
return -1;
}
os_memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = RTMGRP_LINK;
if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) {
perror("bind(netlink)");
close(s);
return -1;
}
time(&end);
end += 30; /* Wait at most 30 seconds for scan results */
for (;;) {
time(&now);
tv.tv_sec = end > now ? end - now : 0;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(s, &rfds);
res = select(s + 1, &rfds, NULL, NULL, &tv);
if (res < 0) {
perror("select");
/* Assume results are ready after 10 seconds wait */
os_sleep(10, 0);
break;
} else if (res) {
if (i802_is_scan_complete(s, ifindex)) {
wpa_printf(MSG_DEBUG, "nl80211: Scan "
"completed");
break;
}
} else {
wpa_printf(MSG_DEBUG, "nl80211: Scan timeout");
/* Assume results are ready to be read now */
break;
}
}
close(s);
return i802_get_ht_scan_res(drv);
}
#endif /* CONFIG_IEEE80211N */
static int i802_init_sockets(struct wpa_driver_nl80211_data *drv, const u8 *bssid)
{
struct ifreq ifr;
drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (drv->ioctl_sock < 0) {
perror("socket[PF_INET,SOCK_DGRAM]");
return -1;
}
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, if_nametoindex(drv->ifname));
if (hostapd_set_iface_flags(drv, drv->ifname, 0))
return -1;
if (bssid) {
os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
memcpy(ifr.ifr_hwaddr.sa_data, bssid, ETH_ALEN);
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
perror("ioctl(SIOCSIFHWADDR)");
return -1;
}
}
if (wpa_driver_nl80211_init_nl(drv, drv->hapd))
return -1;
#ifdef CONFIG_IEEE80211N
if (drv->ht_40mhz_scan) {
if (nl80211_set_mode(drv, drv->ifname, NL80211_IFTYPE_STATION)
|| hostapd_set_iface_flags(drv, drv->ifname, 1) ||
i802_ht_scan(drv) ||
hostapd_set_iface_flags(drv, drv->ifname, 0)) {
wpa_printf(MSG_ERROR, "Failed to scan channels for "
"HT 40 MHz operations");
return -1;
}
}
#endif /* CONFIG_IEEE80211N */
/* Initialise a monitor interface */
if (nl80211_create_monitor_interface(drv))
return -1;
if (nl80211_set_mode(drv, drv->ifname, NL80211_IFTYPE_AP))
goto fail1;
if (hostapd_set_iface_flags(drv, drv->ifname, 1))
goto fail1;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
if (drv->eapol_sock < 0) {
perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
goto fail1;
}
if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
{
printf("Could not register read socket for eapol\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) {
perror("ioctl(SIOCGIFHWADDR)");
goto fail1;
}
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
printf("Invalid HW-addr family 0x%04x\n",
ifr.ifr_hwaddr.sa_family);
goto fail1;
}
memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
return 0;
fail1:
nl80211_remove_iface(drv, drv->monitor_ifidx);
return -1;
}
static int i802_get_inact_sec(void *priv, const u8 *addr)
{
struct hostap_sta_driver_data data;
@ -4299,20 +3833,12 @@ static int i802_sta_disassoc(void *priv, const u8 *addr, int reason)
}
static const struct hostapd_neighbor_bss *
i802_get_neighbor_bss(void *priv, size_t *num)
{
struct wpa_driver_nl80211_data *drv = priv;
*num = drv->num_neighbors;
return drv->neighbors;
}
static void *i802_init(struct hostapd_data *hapd,
struct wpa_init_params *params)
{
struct wpa_driver_nl80211_data *drv;
size_t i;
struct ifreq ifr;
drv = os_zalloc(sizeof(struct wpa_driver_nl80211_data));
if (drv == NULL) {
@ -4321,6 +3847,7 @@ static void *i802_init(struct hostapd_data *hapd,
}
drv->hapd = hapd;
drv->ctx = hapd;
memcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
drv->ifindex = if_nametoindex(drv->ifname);
drv->bss.ifindex = drv->ifindex;
@ -4331,13 +3858,76 @@ static void *i802_init(struct hostapd_data *hapd,
if (params->bridge[i])
add_ifidx(drv, if_nametoindex(params->bridge[i]));
}
drv->ht_40mhz_scan = params->ht_40mhz_scan;
if (i802_init_sockets(drv, params->bssid))
drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (drv->ioctl_sock < 0) {
perror("socket[PF_INET,SOCK_DGRAM]");
goto failed;
}
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
if (hostapd_set_iface_flags(drv, drv->ifname, 0))
goto failed;
if (params->bssid) {
os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
os_memcpy(ifr.ifr_hwaddr.sa_data, params->bssid, ETH_ALEN);
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
perror("ioctl(SIOCSIFHWADDR)");
goto failed;
}
}
if (wpa_driver_nl80211_init_nl(drv, drv->hapd))
goto failed;
/* Initialise a monitor interface */
if (nl80211_create_monitor_interface(drv))
goto failed;
if (nl80211_set_mode(drv, drv->ifindex, NL80211_IFTYPE_AP)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
"into AP mode", drv->ifname);
goto fail1;
}
if (hostapd_set_iface_flags(drv, drv->ifname, 1))
goto fail1;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
if (drv->eapol_sock < 0) {
perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
goto fail1;
}
if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
{
printf("Could not register read socket for eapol\n");
goto failed;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) {
perror("ioctl(SIOCGIFHWADDR)");
goto fail1;
}
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
printf("Invalid HW-addr family 0x%04x\n",
ifr.ifr_hwaddr.sa_family);
goto fail1;
}
os_memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
return drv;
fail1:
nl80211_remove_iface(drv, drv->monitor_ifidx);
failed:
free(drv);
return NULL;
@ -4384,8 +3974,6 @@ static void i802_deinit(void *priv)
if (drv->if_indices != drv->default_if_indices)
free(drv->if_indices);
os_free(drv->neighbors);
bss = drv->bss.next;
while (bss) {
prev = bss;
@ -4416,6 +4004,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.get_capa = wpa_driver_nl80211_get_capa,
.set_operstate = wpa_driver_nl80211_set_operstate,
.set_country = wpa_driver_nl80211_set_country,
.set_mode = wpa_driver_nl80211_set_mode,
#ifdef CONFIG_AP
.set_beacon = wpa_driver_nl80211_set_beacon,
#endif /* CONFIG_AP */
@ -4455,6 +4044,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.if_update = i802_if_update,
.if_remove = i802_if_remove,
.set_sta_vlan = i802_set_sta_vlan,
.get_neighbor_bss = i802_get_neighbor_bss,
#endif /* HOSTAPD */
};

@ -862,8 +862,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = {
NULL /* set_radius_acl_expire */,
NULL /* set_ht_params */,
NULL /* set_wps_beacon_ie */,
NULL /* set_wps_probe_resp_ie */,
NULL /* get_neighbor_bss */
NULL /* set_wps_probe_resp_ie */
};

@ -2567,7 +2567,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
NULL /* set_radius_acl_expire */,
NULL /* set_ht_params */,
NULL /* set_wps_beacon_ie */,
NULL /* set_wps_probe_resp_ie */,
NULL /* get_neighbor_bss */
NULL /* set_wps_probe_resp_ie */
#endif /* HOSTAPD */
};

Loading…
Cancel
Save