/*
* 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>
#include "includes.h"
#include <sys/ioctl.h>
-#include <net/if_arp.h>
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include "eloop.h"
#include "common/ieee802_11_defs.h"
#include "netlink.h"
+#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
#include "driver.h"
int monitor_sock;
int monitor_ifidx;
+ int probe_req_report;
+ int disable_11b_rates;
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 */
const char *ifname);
#endif /* HOSTAPD */
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int disabled);
+
/* 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)
-{
- struct ifreq ifr;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr)) {
- wpa_printf(MSG_ERROR, "%s: ioctl(SIOCGIFHWADDR): %d (%s)",
- ifname, errno, strerror(errno));
- return -1;
- }
-
- if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
- wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
- ifname, ifr.ifr_hwaddr.sa_family);
- return -1;
- }
- os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
-
- return 0;
-}
-
-
-static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv,
- const char *ifname, const u8 *addr)
-{
- struct ifreq ifr;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
- ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
-
- if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
- wpa_printf(MSG_DEBUG, "%s: ioctl(SIOCSIFHWADDR): %d (%s)",
- ifname, errno, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-#endif /* HOSTAPD */
-
-
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
{
struct wpa_driver_nl80211_data *drv = priv;
}
+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);
}
-static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv,
- const char *ifname, int dev_up)
-{
- struct ifreq ifr;
-
- if (drv->ioctl_sock < 0)
- return -1;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
- if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) {
- perror("ioctl[SIOCGIFFLAGS]");
- wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)",
- ifname);
- return -1;
- }
-
- if (dev_up) {
- if (ifr.ifr_flags & IFF_UP)
- return 0;
- ifr.ifr_flags |= IFF_UP;
- } else {
- if (!(ifr.ifr_flags & IFF_UP))
- return 0;
- ifr.ifr_flags &= ~IFF_UP;
- }
-
- if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) {
- perror("ioctl[SIOCSIFFLAGS]");
- return -1;
- }
-
- return 0;
-}
-
-
/**
* wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
* @priv: driver_nl80211 private data
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)
"use managed mode");
}
- if (hostapd_set_iface_flags(drv, drv->ifname, 1)) {
- wpa_printf(MSG_ERROR, "Could not set interface '%s' "
- "UP", drv->ifname);
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+ wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
+ drv->ifname);
return -1;
}
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);
wpa_driver_nl80211_free_bss(drv);
#endif /* HOSTAPD */
+ if (drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
netlink_deinit(drv->netlink);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- (void) hostapd_set_iface_flags(drv, drv->ifname, 0);
+ (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
if (drv->ioctl_sock >= 0)
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()
* TODO: if multiple auth_alg options enabled, try them one by one if
* the AP rejects authentication due to unknown auth alg
*/
- if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+ else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
type = NL80211_AUTHTYPE_SHARED_KEY;
- else if (params->auth_alg & AUTH_ALG_LEAP)
+ else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
type = NL80211_AUTHTYPE_NETWORK_EAP;
- else if (params->auth_alg & AUTH_ALG_FT)
+ else if (params->auth_alg & WPA_AUTH_ALG_FT)
type = NL80211_AUTHTYPE_FT;
else
goto nla_put_failure;
#ifdef HOSTAPD
/* start listening for EAPOL on this interface */
add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
- if (addr && iftype == NL80211_IFTYPE_AP &&
- set_ifhwaddr(drv, ifname, addr)) {
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+ linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
-#endif /* HOSTAPD */
return ifidx;
}
wds);
}
+ if (ret >= 0 && drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, ret, 1);
+
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);
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;
+ }
}
if (drv->monitor_ifidx < 0)
return -1;
- if (hostapd_set_iface_flags(drv, buf, 1))
+ if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
goto error;
memset(&ll, 0, sizeof(ll));
NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie);
- if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+ else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
type = NL80211_AUTHTYPE_SHARED_KEY;
- else if (params->auth_alg & AUTH_ALG_LEAP)
+ else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
type = NL80211_AUTHTYPE_NETWORK_EAP;
- else if (params->auth_alg & AUTH_ALG_FT)
+ else if (params->auth_alg & WPA_AUTH_ALG_FT)
type = NL80211_AUTHTYPE_FT;
else
goto nla_put_failure;
* 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) {
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
/* Try to set the mode again while the interface is down */
ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
- if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
ret = -1;
}
if (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN,
NULL, 1) < 0)
return -1;
- hostapd_set_iface_flags(drv, name, 1);
+ linux_set_iface_flags(drv->ioctl_sock, name, 1);
return i802_set_sta_vlan(priv, addr, name, 0);
} else {
i802_set_sta_vlan(priv, addr, drv->ifname, 0);
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);
}
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (hostapd_set_iface_flags(drv, drv->ifname, 0))
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0))
goto failed;
if (params->bssid) {
- if (set_ifhwaddr(drv, drv->ifname, params->bssid))
+ if (linux_set_ifhwaddr(drv->ioctl_sock, drv->ifname,
+ params->bssid))
goto failed;
}
goto failed;
}
- if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
goto failed;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
goto failed;
}
- if (get_ifhwaddr(drv, drv->ifname, params->own_addr))
+ if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, params->own_addr))
goto failed;
return drv;
#ifdef HOSTAPD
if (type == WPA_IF_AP_BSS) {
- if (hostapd_set_iface_flags(priv, ifname, 1)) {
- nl80211_remove_iface(priv, ifidx);
+ if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ nl80211_remove_iface(drv, ifidx);
os_free(bss);
return -1;
}
}
+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 (linux_get_ifhwaddr(drv->ioctl_sock, 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 */
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int disabled)
+{
+ struct nl_msg *msg;
+ struct nlattr *bands, *band;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ goto nla_put_failure;
+
+ /*
+ * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+ * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+ * rates. All 5 GHz rates are left enabled.
+ */
+ band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+ if (!band)
+ goto nla_put_failure;
+ NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ nla_nest_end(msg, band);
+
+ nla_nest_end(msg, bands);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ }
+
+ return ret;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ drv->disable_11b_rates = disabled;
+ return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.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,
+ .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
};