/*
* Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
size_t ssid_len;
int nlmode;
int ap_scan_as_station;
+ unsigned int assoc_freq;
int monitor_sock;
int monitor_ifidx;
+ int probe_req_report;
unsigned int beacon_set:1;
+ unsigned int pending_remain_on_chan:1;
+
+ u64 remain_on_chan_cookie;
#ifdef HOSTAPD
int eapol_sock; /* socket for EAPOL frames */
static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
int ifindex);
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static int wpa_driver_nl80211_if_remove(void *priv,
+ enum wpa_driver_if_type type,
+ const char *ifname);
#endif /* HOSTAPD */
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+
/* nl80211 code */
static int ack_handler(struct nl_msg *msg, void *arg)
}
-#ifdef HOSTAPD
static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv,
const char *ifname, u8 *addr)
{
return 0;
}
-#endif /* HOSTAPD */
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
len - 24 - sizeof(mgmt->u.assoc_resp);
}
+ event.assoc_info.freq = drv->assoc_freq;
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
}
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+ int cancel_event, struct nlattr *tb[])
+{
+ unsigned int freq, chan_type, duration;
+ union wpa_event_data data;
+ u64 cookie;
+
+ if (tb[NL80211_ATTR_WIPHY_FREQ])
+ freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ else
+ freq = 0;
+
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+ chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ else
+ chan_type = 0;
+
+ if (tb[NL80211_ATTR_DURATION])
+ duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+ else
+ duration = 0;
+
+ if (tb[NL80211_ATTR_COOKIE])
+ cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ else
+ cookie = 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+ "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+ cancel_event, freq, chan_type, duration,
+ (long long unsigned int) cookie,
+ cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+ if (cookie != drv->remain_on_chan_cookie)
+ return; /* not for us */
+
+ drv->pending_remain_on_chan = !cancel_event;
+
+ os_memset(&data, 0, sizeof(data));
+ data.remain_on_channel.freq = freq;
+ data.remain_on_channel.duration = duration;
+ wpa_supplicant_event(drv->ctx, cancel_event ?
+ EVENT_CANCEL_REMAIN_ON_CHANNEL :
+ EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+ struct nlattr *tb[])
+{
+ union wpa_event_data event;
+ struct nlattr *nl;
+ int rem;
+ struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+ int freqs[MAX_REPORT_FREQS];
+ int num_freqs = 0;
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+
+ if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+ struct wpa_driver_scan_ssid *s =
+ &info->ssids[info->num_ssids];
+ s->ssid = nla_data(nl);
+ s->ssid_len = nla_len(nl);
+ info->num_ssids++;
+ if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+ break;
+ }
+ }
+ if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+ {
+ freqs[num_freqs] = nla_get_u32(nl);
+ num_freqs++;
+ if (num_freqs == MAX_REPORT_FREQS - 1)
+ break;
+ }
+ info->freqs = freqs;
+ info->num_freqs = num_freqs;
+ }
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
static int process_event(struct nl_msg *msg, void *arg)
{
struct wpa_driver_nl80211_data *drv = arg;
drv->scan_complete_events = 1;
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
drv->ctx);
- wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+ send_scan_event(drv, 0, tb);
break;
case NL80211_CMD_SCAN_ABORTED:
wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
*/
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
drv->ctx);
- wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+ send_scan_event(drv, 1, tb);
break;
case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE:
case NL80211_CMD_JOIN_IBSS:
mlme_event_join_ibss(drv, tb);
break;
+ case NL80211_CMD_REMAIN_ON_CHANNEL:
+ mlme_event_remain_on_channel(drv, 0, tb);
+ break;
+ case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+ mlme_event_remain_on_channel(drv, 1, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
WPA_DRIVER_CAPA_ENC_WEP104 |
WPA_DRIVER_CAPA_ENC_TKIP |
WPA_DRIVER_CAPA_ENC_CCMP;
+ drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
drv->capa.max_scan_ssids = info.max_scan_ssids;
if (info.ap_supported)
struct wpa_driver_nl80211_data *drv = priv;
nl80211_remove_monitor_interface(drv);
- if (drv->monitor_sock >= 0) {
- eloop_unregister_read_sock(drv->monitor_sock);
- close(drv->monitor_sock);
- }
if (drv->nlmode == NL80211_IFTYPE_AP)
wpa_driver_nl80211_del_beacon(drv);
nl_handle_destroy(drv->nl_handle_event);
nl_cb_put(drv->nl_cb);
+ eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
+ drv, NULL);
+
os_free(drv);
}
}
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
/**
* wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
* @priv: Pointer to private wext data from wpa_driver_nl80211_init()
static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
- wpa_alg alg, const u8 *addr, int key_idx,
- int set_tx,
+ 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)
{
}
-static int nl_add_key(struct nl_msg *msg, wpa_alg alg,
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
int key_idx, int defkey,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len)
return NL_SKIP;
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
- mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+ mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
if (!mode)
return NL_SKIP;
phy_info->modes = mode;
mode->num_channels++;
}
- mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data));
+ mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data));
if (!mode->channels)
return NL_SKIP;
mode->num_rates++;
}
- mode->rates = calloc(mode->num_rates, sizeof(int));
+ mode->rates = os_zalloc(mode->num_rates * sizeof(int));
if (!mode->rates)
return NL_SKIP;
{
struct nl_msg *msg;
+ wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
#ifdef HOSTAPD
/* stop listening for EAPOL on this interface */
del_ifidx(drv, ifidx);
static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
const char *ifname,
enum nl80211_iftype iftype,
- const u8 *addr)
+ const u8 *addr, int wds)
{
struct nl_msg *msg, *flags = NULL;
int ifidx;
if (err)
goto nla_put_failure;
+ } else if (wds) {
+ NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
}
ifidx = if_nametoindex(ifname);
+ wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+ ifname, ifidx);
if (ifidx <= 0)
return -1;
#ifdef HOSTAPD
/* start listening for EAPOL on this interface */
add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
- if (addr && iftype == NL80211_IFTYPE_AP &&
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
set_ifhwaddr(drv, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
-#endif /* HOSTAPD */
return ifidx;
}
static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
- const u8 *addr)
+ const u8 *addr, int wds)
{
int ret;
- ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
/* if error occured and interface exists already */
if (ret == -ENFILE && if_nametoindex(ifname)) {
nl80211_remove_iface(drv, if_nametoindex(ifname));
/* Try to create the interface again */
- ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+ wds);
}
return ret;
static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
- struct ieee80211_hdr *hdr, size_t len)
+ u8 *buf, size_t len)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
- event.rx_from_unknown.hdr = hdr;
+ event.rx_from_unknown.frame = buf;
event.rx_from_unknown.len = len;
wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
}
static void handle_frame(struct wpa_driver_nl80211_data *drv,
- u8 *buf, size_t len,
- struct hostapd_frame_info *hfi)
+ u8 *buf, size_t len, int datarate, int ssi_signal)
{
struct ieee80211_hdr *hdr;
u16 fc;
os_memset(&event, 0, sizeof(event));
event.rx_mgmt.frame = buf;
event.rx_mgmt.frame_len = len;
- event.rx_mgmt.fi = hfi;
+ event.rx_mgmt.datarate = datarate;
+ event.rx_mgmt.ssi_signal = ssi_signal;
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
break;
case WLAN_FC_TYPE_CTRL:
/* can only get here with PS-Poll frames */
wpa_printf(MSG_DEBUG, "CTRL");
- from_unknown_sta(drv, hdr, len);
+ from_unknown_sta(drv, buf, len);
break;
case WLAN_FC_TYPE_DATA:
- from_unknown_sta(drv, hdr, len);
+ from_unknown_sta(drv, buf, len);
break;
}
}
unsigned char buf[3000];
struct ieee80211_radiotap_iterator iter;
int ret;
- struct hostapd_frame_info hfi;
+ int datarate = 0, ssi_signal = 0;
int injected = 0, failed = 0, rxflags = 0;
len = recv(sock, buf, sizeof(buf), 0);
return;
}
+ if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
+ "frame since Probe Request reporting is disabled");
+ return;
+ }
+
if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
printf("received invalid radiotap frame\n");
return;
}
- memset(&hfi, 0, sizeof(hfi));
-
while (1) {
ret = ieee80211_radiotap_iterator_next(&iter);
if (ret == -ENOENT)
case IEEE80211_RADIOTAP_DATA_RETRIES:
break;
case IEEE80211_RADIOTAP_CHANNEL:
- /* TODO convert from freq/flags to channel number
- hfi.channel = XXX;
- */
+ /* TODO: convert from freq/flags to channel number */
break;
case IEEE80211_RADIOTAP_RATE:
- hfi.datarate = *iter.this_arg * 5;
+ datarate = *iter.this_arg * 5;
break;
case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
- hfi.ssi_signal = *iter.this_arg;
+ ssi_signal = *iter.this_arg;
break;
}
}
if (!injected)
handle_frame(drv, buf + iter.max_length,
- len - iter.max_length, &hfi);
+ len - iter.max_length, datarate, ssi_signal);
else
handle_tx_callback(drv->ctx, buf + iter.max_length,
len - iter.max_length, !failed);
#if 0
/*
- * drop non-data frames, WDS frames
+ * drop non-data frames
*/
/* load the lower byte of the frame control field */
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c),
/* drop non-data frames */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
/* load the upper byte of the frame control field */
- BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
/* mask off toDS/fromDS */
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03),
- /* drop WDS frames */
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0),
-#endif
+ /* accept WDS frames */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0),
/*
* add header length to index
nl80211_remove_iface(drv, drv->monitor_ifidx);
drv->monitor_ifidx = -1;
}
+ if (drv->monitor_sock >= 0) {
+ eloop_unregister_read_sock(drv->monitor_sock);
+ close(drv->monitor_sock);
+ drv->monitor_sock = -1;
+ }
}
buf[IFNAMSIZ - 1] = '\0';
drv->monitor_ifidx =
- nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL);
+ nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+ 0);
if (drv->monitor_ifidx < 0)
return -1;
"failed: %d (%s)",
(unsigned long) len, errno, strerror(errno));
}
- free(hdr);
+ os_free(hdr);
return res;
}
if (params->freq) {
wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
- }
+ drv->assoc_freq = params->freq;
+ } else
+ drv->assoc_freq = 0;
if (params->ssid) {
wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
params->ssid, params->ssid_len);
}
if (nlmode == drv->nlmode) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+ "requested mode - ignore error");
ret = 0;
goto done; /* Already in the requested mode */
}
ret = -1;
}
- if (!ret)
+ if (!ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+ "interface is down");
drv->nlmode = nlmode;
+ }
done:
if (!ret && nlmode == NL80211_IFTYPE_AP) {
nl80211_remove_monitor_interface(drv);
}
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+ "from %d failed", nlmode, drv->nlmode);
+
return ret;
}
else
old = NULL;
- drv->if_indices = realloc(old,
- sizeof(int) * (drv->num_if_indices + 1));
+ drv->if_indices = os_realloc(old,
+ sizeof(int) * (drv->num_if_indices + 1));
if (!drv->if_indices) {
if (!old)
drv->if_indices = drv->default_if_indices;
}
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ char name[16];
+
+ os_snprintf(name, sizeof(name), "%s.sta%d", drv->ifname, aid);
+ if (val) {
+ if (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN,
+ NULL, 1) < 0)
+ return -1;
+ hostapd_set_iface_flags(drv, name, 1);
+ return i802_set_sta_vlan(priv, addr, name, 0);
+ } else {
+ i802_set_sta_vlan(priv, addr, drv->ifname, 0);
+ return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+ name);
+ }
+}
+
+
static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
return;
}
- if (have_ifidx(drv, lladdr.sll_ifindex)) {
- void *ctx;
- ctx = hostapd_sta_get_bss(drv->ctx, lladdr.sll_addr);
- if (!ctx)
- return;
- hostapd_eapol_receive(ctx, lladdr.sll_addr, buf, len);
- }
+ if (have_ifidx(drv, lladdr.sll_ifindex))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
}
#endif /* HOSTAPD */
ifidx = nl80211_create_iface(drv, ifname,
- wpa_driver_nl80211_if_type(type), addr);
+ wpa_driver_nl80211_if_type(type), addr,
+ 0);
if (ifidx < 0) {
#ifdef HOSTAPD
os_free(bss);
}
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u64 *cookie = arg;
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_COOKIE])
+ *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+ unsigned int duration)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+ int ret;
+ u64 cookie;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+ "0x%llx for freq=%u MHz duration=%u",
+ (long long unsigned int) cookie, freq, duration);
+ drv->remain_on_chan_cookie = cookie;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+ "(freq=%d): %d (%s)", freq, ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->pending_remain_on_chan) {
+ wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+ "to cancel");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+ "0x%llx",
+ (long long unsigned int) drv->remain_on_chan_cookie);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ if (drv->monitor_ifidx < 0)
+ return; /* monitor interface already removed */
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION)
+ return; /* not in station mode anymore */
+
+ if (drv->probe_req_report)
+ return; /* reporting enabled */
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
+ "Probe Request reporting needed anymore");
+ nl80211_remove_monitor_interface(drv);
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION) {
+ wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+ "allowed in station mode (iftype=%d)",
+ drv->nlmode);
+ return -1;
+ }
+ drv->probe_req_report = report;
+
+ if (report) {
+ eloop_cancel_timeout(
+ wpa_driver_nl80211_probe_req_report_timeout,
+ drv, NULL);
+ if (drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv))
+ return -1;
+ } else {
+ /*
+ * It takes a while to remove the monitor interface, so try to
+ * avoid doing this if it is needed again shortly. Instead,
+ * schedule the interface to be removed later if no need for it
+ * is seen.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
+ "to be removed after 10 seconds of no use");
+ eloop_register_timeout(
+ 10, 0, wpa_driver_nl80211_probe_req_report_timeout,
+ drv, NULL);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_alloc_interface_addr(void *priv, u8 *addr)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+
+ if (get_ifhwaddr(drv, drv->ifname, addr) < 0)
+ return -1;
+
+ if (addr[0] & 0x02) {
+ /* TODO: add support for generating multiple addresses */
+ addr[0] ^= 0x80;
+ } else
+ addr[0] = 0x02; /* locally administered */
+
+ return 0;
+}
+
+
+static void wpa_driver_nl80211_release_interface_addr(void *priv,
+ const u8 *addr)
+{
+ /* TODO: keep list of allocated address and release them here */
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.set_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan,
+ .set_wds_sta = i802_set_wds_sta,
#endif /* HOSTAPD */
+ .remain_on_channel = wpa_driver_nl80211_remain_on_channel,
+ .cancel_remain_on_channel =
+ wpa_driver_nl80211_cancel_remain_on_channel,
+ .probe_req_report = wpa_driver_nl80211_probe_req_report,
+ .alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr,
+ .release_interface_addr = wpa_driver_nl80211_release_interface_addr,
};