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.
This commit is contained in:
Jouni Malinen 2009-04-16 16:22:40 +03:00 committed by Jouni Malinen
parent 814aaa844a
commit ad1e68e6b5
11 changed files with 357 additions and 632 deletions

View file

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

View file

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

View file

@ -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;

View file

@ -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);

View file

@ -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,9 +562,27 @@ 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
* @iface: Pointer to interface data.
@ -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,

View file

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

View file

@ -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;
/**

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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 */
};