/*
* 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 <netlink/genl/ctrl.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
#include "nl80211_copy.h"
-#ifndef NO_WEXT
-#include "wireless_copy.h"
-#endif /* NO_WEXT */
#include "common.h"
-#include "driver.h"
#include "eloop.h"
-#include "ieee802_11_defs.h"
-
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-#include <netpacket/packet.h>
-#include <linux/filter.h>
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
-#endif /* CONFIG_AP || HOSTAPD */
-
-#ifdef CONFIG_AP
-
-#include "../hostapd/hostapd_defs.h"
-
-#ifndef ETH_P_ALL
-#define ETH_P_ALL 0x0003
-#endif
-
-#endif /* CONFIG_AP */
-
-#ifdef HOSTAPD
-#include "../../hostapd/hostapd.h"
-#include "../../hostapd/sta_flags.h"
-#include "ieee802_11_common.h"
+#include "driver.h"
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
#define nl_handle_alloc_cb nl_socket_alloc_cb
#define nl_handle_destroy nl_socket_free
#endif /* CONFIG_LIBNL20 */
-#endif /* HOSTAPD */
-
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
#define IF_OPER_UP 6
#endif
-enum ieee80211_msg_type {
- ieee80211_msg_normal = 0,
- ieee80211_msg_tx_callback_ack = 1,
- ieee80211_msg_tx_callback_fail = 2,
-};
-
struct i802_bss {
struct i802_bss *next;
int ifindex;
struct wpa_driver_nl80211_data {
void *ctx;
- int link_event_sock;
+ struct netlink_data *netlink;
int ioctl_sock; /* socket for ioctl() use */
char ifname[IFNAMSIZ + 1];
int ifindex;
int scan_complete_events;
struct nl_handle *nl_handle;
+ struct nl_handle *nl_handle_event;
struct nl_cache *nl_cache;
+ struct nl_cache *nl_cache_event;
struct nl_cb *nl_cb;
struct genl_family *nl80211;
+ u8 auth_bssid[ETH_ALEN];
u8 bssid[ETH_ALEN];
int associated;
u8 ssid[32];
size_t ssid_len;
int nlmode;
int ap_scan_as_station;
+ unsigned int assoc_freq;
- int beacon_int;
int monitor_sock;
int monitor_ifidx;
+ int probe_req_report;
+ int disable_11b_rates;
-#ifdef CONFIG_AP
unsigned int beacon_set:1;
-#endif /* CONFIG_AP */
+ unsigned int pending_remain_on_chan:1;
-#ifdef HOSTAPD
- struct hostapd_data *hapd;
+ u64 remain_on_chan_cookie;
+#ifdef HOSTAPD
int eapol_sock; /* socket for EAPOL frames */
int default_if_indices[16];
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);
-
-#ifdef CONFIG_AP
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
- int ifidx);
-#endif /* CONFIG_AP */
+static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int cmd, u16 reason_code);
+static void nl80211_remove_monitor_interface(
+ struct wpa_driver_nl80211_data *drv);
#ifdef HOSTAPD
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
int ifindex);
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
- int ifidx);
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
-static int i802_del_beacon(struct wpa_driver_nl80211_data *drv);
+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);
+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_send_oper_ifla(
- struct wpa_driver_nl80211_data *drv,
- int linkmode, int operstate)
-{
- struct {
- struct nlmsghdr hdr;
- struct ifinfomsg ifinfo;
- char opts[16];
- } req;
- struct rtattr *rta;
- static int nl_seq;
- ssize_t ret;
-
- os_memset(&req, 0, sizeof(req));
-
- req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- req.hdr.nlmsg_type = RTM_SETLINK;
- req.hdr.nlmsg_flags = NLM_F_REQUEST;
- req.hdr.nlmsg_seq = ++nl_seq;
- req.hdr.nlmsg_pid = 0;
-
- req.ifinfo.ifi_family = AF_UNSPEC;
- req.ifinfo.ifi_type = 0;
- req.ifinfo.ifi_index = drv->ifindex;
- req.ifinfo.ifi_flags = 0;
- req.ifinfo.ifi_change = 0;
-
- if (linkmode != -1) {
- rta = (struct rtattr *)
- ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len));
- rta->rta_type = IFLA_LINKMODE;
- rta->rta_len = RTA_LENGTH(sizeof(char));
- *((char *) RTA_DATA(rta)) = linkmode;
- req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
- RTA_LENGTH(sizeof(char));
- }
- if (operstate != -1) {
- rta = (struct rtattr *)
- ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len));
- rta->rta_type = IFLA_OPERSTATE;
- rta->rta_len = RTA_LENGTH(sizeof(char));
- *((char *) RTA_DATA(rta)) = operstate;
- req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
- RTA_LENGTH(sizeof(char));
- }
-
- wpa_printf(MSG_DEBUG, "nl80211: Operstate: linkmode=%d, operstate=%d",
- linkmode, operstate);
-
- ret = send(drv->link_event_sock, &req, req.hdr.nlmsg_len, 0);
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Sending operstate IFLA failed:"
- " %s (assume operstate is not supported)",
- strerror(errno));
- }
-
- return ret < 0 ? -1 : 0;
-}
-
-
-#ifndef NO_WEXT
-static int wpa_driver_nl80211_set_auth_param(
- struct wpa_driver_nl80211_data *drv, int idx, u32 value)
-{
- struct iwreq iwr;
- int ret = 0;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- iwr.u.param.flags = idx & IW_AUTH_INDEX;
- iwr.u.param.value = value;
-
- if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) {
- if (errno != EOPNOTSUPP) {
- wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d "
- "value 0x%x) failed: %s)",
- idx, value, strerror(errno));
- }
- ret = errno == EOPNOTSUPP ? -2 : -1;
- }
-
- return ret;
-}
-#endif /* NO_WEXT */
-
-
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
{
struct wpa_driver_nl80211_data *drv = priv;
}
-#ifndef HOSTAPD
static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
- void *ctx, char *buf, size_t len,
- int del)
+ char *buf, size_t len, int del)
{
union wpa_event_data event;
drv->if_removed = 0;
}
- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
}
static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
- struct nlmsghdr *h)
+ u8 *buf, size_t len)
{
- struct ifinfomsg *ifi;
- int attrlen, _nlmsg_len, rta_len;
+ int attrlen, rta_len;
struct rtattr *attr;
- ifi = NLMSG_DATA(h);
-
- _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);
+ attrlen = len;
+ attr = (struct rtattr *) buf;
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
- int ifindex, struct nlmsghdr *h)
+ int ifindex, u8 *buf, size_t len)
{
if (drv->ifindex == ifindex)
return 1;
- if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) {
+ if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
drv->ifindex = if_nametoindex(drv->ifname);
wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
"interface");
}
-static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv,
- void *ctx, struct nlmsghdr *h,
- size_t len)
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
{
- struct ifinfomsg *ifi;
- int attrlen, _nlmsg_len, rta_len;
- struct rtattr * attr;
-
- if (len < sizeof(*ifi))
- return;
-
- ifi = NLMSG_DATA(h);
+ struct wpa_driver_nl80211_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
- if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) {
+ if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len)) {
wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
ifi->ifi_index);
return;
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING))
- wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP);
-
- _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
- attrlen = h->nlmsg_len - _nlmsg_len;
- if (attrlen < 0)
- return;
-
- attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ -1, IF_OPER_UP);
+ attrlen = len;
+ attr = (struct rtattr *) buf;
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
wpa_driver_nl80211_event_link(
- drv, ctx,
+ drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 0);
}
}
-static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv,
- void *ctx, struct nlmsghdr *h,
- size_t len)
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
{
- struct ifinfomsg *ifi;
- int attrlen, _nlmsg_len, rta_len;
- struct rtattr * attr;
-
- if (len < sizeof(*ifi))
- return;
-
- ifi = NLMSG_DATA(h);
-
- _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
- attrlen = h->nlmsg_len - _nlmsg_len;
- if (attrlen < 0)
- return;
+ struct wpa_driver_nl80211_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
- attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len);
+ attrlen = len;
+ attr = (struct rtattr *) buf;
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
wpa_driver_nl80211_event_link(
- drv, ctx,
+ drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 1);
}
}
-static void wpa_driver_nl80211_event_receive_link(int sock, void *eloop_ctx,
- void *sock_ctx)
-{
- char buf[8192];
- int left;
- struct sockaddr_nl from;
- socklen_t fromlen;
- struct nlmsghdr *h;
- int max_events = 10;
-
-try_again:
- fromlen = sizeof(from);
- left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
- (struct sockaddr *) &from, &fromlen);
- if (left < 0) {
- if (errno != EINTR && errno != EAGAIN)
- perror("recvfrom(netlink)");
- return;
- }
-
- 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:
- wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx,
- h, plen);
- break;
- case RTM_DELLINK:
- wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx,
- h, plen);
- break;
- }
-
- len = NLMSG_ALIGN(len);
- left -= len;
- h = (struct nlmsghdr *) ((char *) h + len);
- }
-
- if (left > 0) {
- wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink "
- "message", left);
- }
-
- if (--max_events > 0) {
- /*
- * Try to receive all events in one eloop call in order to
- * limit race condition on cases where AssocInfo event, Assoc
- * event, and EAPOL frames are received more or less at the
- * same time. We want to process the event messages first
- * before starting EAPOL processing.
- */
- goto try_again;
- }
-}
-
-
static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
const u8 *frame, size_t len)
{
return;
}
+ os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
os_memset(&event, 0, sizeof(event));
os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
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_connect(struct wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, struct nlattr *status,
+ struct nlattr *addr, struct nlattr *req_ie,
+ struct nlattr *resp_ie)
+{
+ union wpa_event_data event;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ /*
+ * Avoid reporting two association events that would confuse
+ * the core code.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+ "when using userspace SME", cmd);
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ if (cmd == NL80211_CMD_CONNECT &&
+ nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+ if (resp_ie) {
+ event.assoc_reject.resp_ies = nla_data(resp_ie);
+ event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+ }
+ event.assoc_reject.status_code = nla_get_u16(status);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+ return;
+ }
+
+ drv->associated = 1;
+ if (addr)
+ os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+
+ if (req_ie) {
+ event.assoc_info.req_ies = nla_data(req_ie);
+ event.assoc_info.req_ies_len = nla_len(req_ie);
+ }
+ if (resp_ie) {
+ event.assoc_info.resp_ies = nla_data(resp_ie);
+ event.assoc_info.resp_ies_len = nla_len(resp_ie);
+ }
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, struct nlattr *addr)
+{
+ union wpa_event_data event;
+ enum wpa_event_type ev;
+
+ if (nla_len(addr) != ETH_ALEN)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+ cmd, MAC2STR((u8 *) nla_data(addr)));
+
+ if (cmd == NL80211_CMD_AUTHENTICATE)
+ ev = EVENT_AUTH_TIMED_OUT;
+ else if (cmd == NL80211_CMD_ASSOCIATE)
+ ev = EVENT_ASSOC_TIMED_OUT;
+ else
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
static void mlme_event(struct wpa_driver_nl80211_data *drv,
- enum nl80211_commands cmd, struct nlattr *frame)
+ enum nl80211_commands cmd, struct nlattr *frame,
+ struct nlattr *addr, struct nlattr *timed_out)
{
+ if (timed_out && addr) {
+ mlme_timeout_event(drv, cmd, addr);
+ return;
+ }
+
if (frame == NULL) {
wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame "
"data", cmd);
}
}
-#endif /* HOSTAPD */
-
static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
struct nlattr *tb[])
}
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ if (tb[NL80211_ATTR_MAC] == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+ "event");
+ return;
+ }
+ os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+ drv->associated = 1;
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
+ MAC2STR(drv->bssid));
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+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;
}
switch (gnlh->cmd) {
+ case NL80211_CMD_TRIGGER_SCAN:
+ wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
+ break;
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
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;
-#ifndef HOSTAPD
case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE:
case NL80211_CMD_DEAUTHENTICATE:
case NL80211_CMD_DISASSOCIATE:
- mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME]);
+ mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+ tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT]);
+ break;
+ case NL80211_CMD_CONNECT:
+ case NL80211_CMD_ROAM:
+ mlme_event_connect(drv, gnlh->cmd,
+ tb[NL80211_ATTR_STATUS_CODE],
+ tb[NL80211_ATTR_MAC],
+ tb[NL80211_ATTR_REQ_IE],
+ tb[NL80211_ATTR_RESP_IE]);
+ break;
+ case NL80211_CMD_DISCONNECT:
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ /*
+ * Avoid reporting two disassociation events that could
+ * confuse the core code.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+ "event when using userspace SME");
+ break;
+ }
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
break;
-#endif /* HOSTAPD */
case NL80211_CMD_MICHAEL_MIC_FAILURE:
mlme_event_michael_mic_failure(drv, tb);
break;
+ 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);
return;
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
- nl_recvmsgs(drv->nl_handle, cb);
+ nl_recvmsgs(drv->nl_handle_event, cb);
nl_cb_put(cb);
}
-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
struct wiphy_info_data {
int max_scan_ssids;
int ap_supported;
+ int auth_supported;
+ int connect_supported;
};
}
}
+ if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
+ struct nlattr *nl_cmd;
+ int i;
+
+ nla_for_each_nested(nl_cmd,
+ tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
+ u32 cmd = nla_get_u32(nl_cmd);
+ if (cmd == NL80211_CMD_AUTHENTICATE)
+ info->auth_supported = 1;
+ else if (cmd == NL80211_CMD_CONNECT)
+ info->connect_supported = 1;
+ }
+ }
+
return NL_SKIP;
}
}
-static void wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
struct wiphy_info_data info;
if (wpa_driver_nl80211_get_info(drv, &info))
- return;
+ return -1;
drv->has_capability = 1;
/* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
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)
drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+
+ if (info.auth_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+ else if (!info.connect_supported) {
+ wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+ "authentication/association or connect commands");
+ return -1;
+ }
+
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+
+ return 0;
}
#endif /* HOSTAPD */
goto err2;
}
+ drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb);
+ if (drv->nl_handle_event == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks (event)");
+ goto err2b;
+ }
+
if (genl_connect(drv->nl_handle)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
"netlink");
goto err3;
}
+ if (genl_connect(drv->nl_handle_event)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+ "netlink (event)");
+ goto err3;
+ }
+
#ifdef CONFIG_LIBNL20
if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
"netlink cache");
goto err3;
}
+ if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) <
+ 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (event)");
+ goto err3b;
+ }
#else /* CONFIG_LIBNL20 */
drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle);
if (drv->nl_cache == NULL) {
"netlink cache");
goto err3;
}
+ drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event);
+ if (drv->nl_cache_event == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (event)");
+ goto err3b;
+ }
#endif /* CONFIG_LIBNL20 */
-
drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211");
if (drv->nl80211 == NULL) {
wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
ret = nl_get_multicast_id(drv, "nl80211", "scan");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle, ret);
+ ret = nl_socket_add_membership(drv->nl_handle_event, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for scan events: %d (%s)",
ret = nl_get_multicast_id(drv, "nl80211", "mlme");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle, ret);
+ ret = nl_socket_add_membership(drv->nl_handle_event, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for mlme events: %d (%s)",
goto err4;
}
- eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle),
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
wpa_driver_nl80211_event_receive, drv, ctx);
return 0;
err4:
+ nl_cache_free(drv->nl_cache_event);
+err3b:
nl_cache_free(drv->nl_cache);
err3:
+ nl_handle_destroy(drv->nl_handle_event);
+err2b:
nl_handle_destroy(drv->nl_handle);
err2:
nl_cb_put(drv->nl_cb);
}
-static int wpa_driver_nl80211_init_link_events(
- struct wpa_driver_nl80211_data *drv)
-{
-#ifdef HOSTAPD
- return 0;
-#else /* HOSTAPD */
- int s;
- struct sockaddr_nl local;
-
- 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;
- }
-
- eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_link, drv,
- drv->ctx);
- drv->link_event_sock = s;
-
- return 0;
-#endif /* HOSTAPD */
-}
-
-
/**
* wpa_driver_nl80211_init - Initialize nl80211 driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
{
struct wpa_driver_nl80211_data *drv;
+ struct netlink_config *cfg;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
- drv->link_event_sock = -1;
drv->ioctl_sock = -1;
if (wpa_driver_nl80211_init_nl(drv, ctx)) {
return NULL;
}
- drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
-
drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (drv->ioctl_sock < 0) {
perror("socket(PF_INET,SOCK_DGRAM)");
goto failed;
}
- if (wpa_driver_nl80211_init_link_events(drv) ||
- wpa_driver_nl80211_finish_drv_init(drv))
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto failed;
+ cfg->ctx = drv;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ drv->netlink = netlink_init(cfg);
+ if (drv->netlink == NULL) {
+ os_free(cfg);
+ goto failed;
+ }
+ if (wpa_driver_nl80211_finish_drv_init(drv))
goto failed;
return drv;
failed:
- if (drv->link_event_sock >= 0) {
- eloop_unregister_read_sock(drv->link_event_sock);
- close(drv->link_event_sock);
- }
+ netlink_deinit(drv->netlink);
if (drv->ioctl_sock >= 0)
close(drv->ioctl_sock);
"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;
}
- wpa_driver_nl80211_capa(drv);
+ if (wpa_driver_nl80211_capa(drv))
+ return -1;
- wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT);
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ 1, IF_OPER_DORMANT);
#endif /* HOSTAPD */
return 0;
#endif /* HOSTAPD */
+static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_BEACON, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
/**
* wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
* @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
{
struct wpa_driver_nl80211_data *drv = priv;
-#if defined(CONFIG_AP) || defined(HOSTAPD)
- if (drv->monitor_ifidx >= 0)
- nl80211_remove_iface(drv, drv->monitor_ifidx);
- if (drv->monitor_sock >= 0) {
- eloop_unregister_read_sock(drv->monitor_sock);
- close(drv->monitor_sock);
- }
-#endif /* CONFIG_AP || HOSTAPD */
+ nl80211_remove_monitor_interface(drv);
+
+ if (drv->nlmode == NL80211_IFTYPE_AP)
+ wpa_driver_nl80211_del_beacon(drv);
#ifdef HOSTAPD
if (drv->last_freq_ht) {
i802_set_freq(priv, &freq);
}
- i802_del_beacon(drv);
-
if (drv->eapol_sock >= 0) {
eloop_unregister_read_sock(drv->eapol_sock);
close(drv->eapol_sock);
os_free(drv->if_indices);
wpa_driver_nl80211_free_bss(drv);
-#else /* HOSTAPD */
-#ifndef NO_WEXT
- wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, 0);
-#endif /* NO_WEXT */
+#endif /* HOSTAPD */
- wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP);
+ if (drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
- if (drv->link_event_sock >= 0) {
- eloop_unregister_read_sock(drv->link_event_sock);
- close(drv->link_event_sock);
- }
-#endif /* HOSTAPD */
+ 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)
close(drv->ioctl_sock);
- eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle));
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
genl_family_put(drv->nl80211);
nl_cache_free(drv->nl_cache);
+ nl_cache_free(drv->nl_cache_event);
nl_handle_destroy(drv->nl_handle);
+ 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);
}
[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+ [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
};
struct wpa_scan_results *res = arg;
struct wpa_scan_res **tmp;
r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
if (bss[NL80211_BSS_TSF])
r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+ if (bss[NL80211_BSS_SEEN_MS_AGO])
+ r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
r->ie_len = ie_len;
if (ie)
os_memcpy(r + 1, ie, ie_len);
+ if (bss[NL80211_BSS_STATUS]) {
+ enum nl80211_bss_status status;
+ status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+ switch (status) {
+ case NL80211_BSS_STATUS_AUTHENTICATED:
+ r->flags |= WPA_SCAN_AUTHENTICATED;
+ break;
+ case NL80211_BSS_STATUS_ASSOCIATED:
+ r->flags |= WPA_SCAN_ASSOCIATED;
+ break;
+ default:
+ break;
+ }
+ }
+
tmp = os_realloc(res->res,
(res->num + 1) * sizeof(struct wpa_scan_res *));
if (tmp == NULL) {
}
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr)
+{
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+ "mismatch");
+ wpa_driver_nl80211_mlme(drv, addr,
+ NL80211_CMD_DEAUTHENTICATE,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+ struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+ size_t i;
+
+ for (i = 0; i < res->num; i++) {
+ struct wpa_scan_res *r = res->res[i];
+ if (r->flags & WPA_SCAN_AUTHENTICATED) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+ "indicates BSS status with " MACSTR
+ " as authenticated",
+ MAC2STR(r->bssid));
+ if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
+ os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID"
+ " in local state (auth=" MACSTR
+ " assoc=" MACSTR ")",
+ MAC2STR(drv->auth_bssid),
+ MAC2STR(drv->bssid));
+ }
+ }
+
+ if (r->flags & WPA_SCAN_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+ "indicate BSS status with " MACSTR
+ " as associated",
+ MAC2STR(r->bssid));
+ if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ !drv->associated) {
+ wpa_printf(MSG_DEBUG, "nl80211: Local state "
+ "(not associated) does not match "
+ "with BSS state");
+ clear_state_mismatch(drv, r->bssid);
+ } else if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Local state "
+ "(associated with " MACSTR ") does "
+ "not match with BSS state",
+ MAC2STR(drv->bssid));
+ clear_state_mismatch(drv, r->bssid);
+ clear_state_mismatch(drv, drv->bssid);
+ }
+ }
+ }
+}
+
+
+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()
res = os_zalloc(sizeof(*res));
if (res == NULL)
- return 0;
+ return NULL;
msg = nlmsg_alloc();
if (!msg)
goto nla_put_failure;
if (ret == 0) {
wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
(unsigned long) res->num);
+ wpa_driver_nl80211_check_bss_status(drv, res);
return res;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
}
-static int nl_set_encr(int ifindex, struct wpa_driver_nl80211_data *drv,
- 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 wpa_driver_nl80211_set_key(const char *ifname, void *priv,
+ 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)
{
+ struct wpa_driver_nl80211_data *drv = priv;
+ int ifindex = if_nametoindex(ifname);
struct nl_msg *msg;
int ret;
case WPA_ALG_WEP:
if (key_len == 5)
NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- 0x000FAC01);
+ WLAN_CIPHER_SUITE_WEP40);
else
NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- 0x000FAC05);
+ WLAN_CIPHER_SUITE_WEP104);
break;
case WPA_ALG_TKIP:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02);
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_TKIP);
break;
case WPA_ALG_CCMP:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04);
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_CCMP);
break;
case WPA_ALG_IGTK:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06);
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_AES_CMAC);
break;
default:
wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
}
}
- if (seq)
+ if (seq && seq_len)
NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
}
-static int wpa_driver_nl80211_set_key(void *priv, 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, enum wpa_alg alg,
+ int key_idx, int defkey,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
{
- struct wpa_driver_nl80211_data *drv = priv;
- return nl_set_encr(drv->ifindex, drv, alg, addr, key_idx, set_tx, seq,
- seq_len, key, key_len);
+ struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+ if (!key_attr)
+ return -1;
+
+ if (defkey && alg == WPA_ALG_IGTK)
+ NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT);
+ else if (defkey)
+ NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+ NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
+
+ switch (alg) {
+ case WPA_ALG_WEP:
+ if (key_len == 5)
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP40);
+ else
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP104);
+ break;
+ case WPA_ALG_TKIP:
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP);
+ break;
+ case WPA_ALG_CCMP:
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
+ break;
+ case WPA_ALG_IGTK:
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_AES_CMAC);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
+ "algorithm %d", __func__, alg);
+ return -1;
+ }
+
+ if (seq && seq_len)
+ NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
+
+ NLA_PUT(msg, NL80211_KEY_DATA, key_len, key);
+
+ nla_nest_end(msg, key_attr);
+
+ return 0;
+ nla_put_failure:
+ return -1;
+}
+
+
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
+{
+ int i, privacy = 0;
+ struct nlattr *nl_keys, *nl_key;
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ privacy = 1;
+ break;
+ }
+ if (!privacy)
+ return 0;
+
+ NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+
+ nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+ if (!nl_keys)
+ goto nla_put_failure;
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+
+ nl_key = nla_nest_start(msg, i);
+ if (!nl_key)
+ goto nla_put_failure;
+
+ NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+ params->wep_key[i]);
+ if (params->wep_key_len[i] == 5)
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP40);
+ else
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP104);
+
+ NLA_PUT_U8(msg, NL80211_KEY_IDX, i);
+
+ if (i == params->wep_tx_keyidx)
+ NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+ nla_nest_end(msg, nl_key);
+ }
+ nla_nest_end(msg, nl_keys);
+
+ return 0;
+
+nla_put_failure:
+ return -ENOBUFS;
}
}
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int reason_code)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ drv->associated = 0;
+ return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
+ reason_code);
+}
+
+
static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
int reason_code)
{
struct wpa_driver_nl80211_data *drv = priv;
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
wpa_printf(MSG_DEBUG, "%s", __func__);
+ drv->associated = 0;
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
reason_code);
}
int reason_code)
{
struct wpa_driver_nl80211_data *drv = priv;
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
wpa_printf(MSG_DEBUG, "%s", __func__);
+ drv->associated = 0;
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE,
reason_code);
}
int ret = -1, i;
struct nl_msg *msg;
enum nl80211_auth_type type;
+ int count = 0;
drv->associated = 0;
+ os_memset(drv->auth_bssid, 0, ETH_ALEN);
+
+ if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0)
+ return -1;
+retry:
msg = nlmsg_alloc();
if (!msg)
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
drv->ifindex);
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_AUTHENTICATE, 0);
+
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
- wpa_driver_nl80211_set_key(drv, WPA_ALG_WEP, NULL, i,
+ wpa_driver_nl80211_set_key(drv->ifname, drv, WPA_ALG_WEP, NULL,
+ i,
i == params->wep_tx_keyidx, NULL, 0,
params->wep_key[i],
params->wep_key_len[i]);
+ if (params->wep_tx_keyidx != i)
+ continue;
+ if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
+ params->wep_key[i], params->wep_key_len[i])) {
+ nlmsg_free(msg);
+ return -1;
+ }
}
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_AUTHENTICATE, 0);
-
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->bssid) {
wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
* 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;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
"(%s)", ret, strerror(-ret));
+ count++;
+ if (ret == -EALREADY && count == 1 && params->bssid) {
+ /*
+ * mac80211 does not currently accept new
+ * authentication if we are already authenticated. As a
+ * workaround, force deauthentication and try again.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
+ "after forced deauthentication");
+ wpa_driver_nl80211_deauthenticate(
+ drv, params->bssid,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ nlmsg_free(msg);
+ goto retry;
+ }
goto nla_put_failure;
}
ret = 0;
}
-#if defined(CONFIG_AP) || defined(HOSTAPD)
-
struct phy_info_arg {
u16 *num_modes;
struct hostapd_hw_modes *modes;
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;
tb_band[NL80211_BAND_ATTR_HT_CAPA]);
}
+ if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
+ mode->a_mpdu_params |= nla_get_u8(
+ tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) &
+ 0x03;
+ }
+
+ if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
+ mode->a_mpdu_params |= nla_get_u8(
+ tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) <<
+ 2;
+ }
+
+ if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
+ nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) {
+ u8 *mcs;
+ mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+ os_memcpy(mode->mcs_set, mcs, 16);
+ }
+
nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
nla_len(nl_freq), freq_policy);
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(struct hostapd_rate_data));
+ mode->rates = os_zalloc(mode->num_rates * sizeof(int));
if (!mode->rates)
return NL_SKIP;
nla_len(nl_rate), rate_policy);
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
continue;
- mode->rates[idx].rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
+ mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
/* crude heuristic */
if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
- mode->rates[idx].rate > 200)
+ mode->rates[idx] > 200)
mode->mode = HOSTAPD_MODE_IEEE80211G;
- if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE])
- mode->rates[idx].flags |= HOSTAPD_RATE_PREAMBLE2;
-
idx++;
}
}
mode11g->num_channels * sizeof(struct hostapd_channel_data));
mode->num_rates = 0;
- mode->rates = os_malloc(4 * sizeof(struct hostapd_rate_data));
+ mode->rates = os_malloc(4 * sizeof(int));
if (mode->rates == NULL) {
os_free(mode->channels);
(*num_modes)--;
}
for (i = 0; i < mode11g->num_rates; i++) {
- if (mode11g->rates[i].rate > 110 ||
- mode11g->rates[i].flags &
- (HOSTAPD_RATE_ERP | HOSTAPD_RATE_OFDM))
+ if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+ mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
continue;
mode->rates[mode->num_rates] = mode11g->rates[i];
mode->num_rates++;
{
struct wpa_driver_nl80211_data *drv = priv;
struct ieee80211_mgmt *mgmt;
- int do_not_encrypt = 0;
+ int encrypt = 1;
u16 fc;
mgmt = (struct ieee80211_mgmt *) data;
*/
u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
- if (auth_alg == WLAN_AUTH_OPEN ||
- (auth_alg == WLAN_AUTH_SHARED_KEY && auth_trans != 3))
- do_not_encrypt = 1;
+ if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
+ encrypt = 0;
}
- return wpa_driver_nl80211_send_frame(drv, data, data_len,
- !do_not_encrypt);
+ return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
}
-static int wpa_driver_nl80211_set_beacon_iface(int ifindex, void *priv,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len,
- int dtim_period)
+static int wpa_driver_nl80211_set_beacon(const char *ifname, void *priv,
+ const u8 *head, size_t head_len,
+ const u8 *tail, size_t tail_len,
+ int dtim_period, int beacon_int)
{
struct wpa_driver_nl80211_data *drv = priv;
struct nl_msg *msg;
u8 cmd = NL80211_CMD_NEW_BEACON;
int ret;
int beacon_set;
+ int ifindex = if_nametoindex(ifname);
#ifdef HOSTAPD
struct i802_bss *bss;
NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head);
NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
- if (!drv->beacon_int)
- drv->beacon_int = 100;
- NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int);
+ NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int);
NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
}
-static int wpa_driver_nl80211_set_beacon_int(void *priv, int value)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- struct nl_msg *msg;
-
- drv->beacon_int = value;
-
-#ifdef HOSTAPD
- if (!drv->bss.beacon_set)
- return 0;
-#else /* HOSTAPD */
- if (!drv->beacon_set)
- return 0;
-#endif /* HOSTAPD */
-
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
-
- wpa_printf(MSG_DEBUG, "nl80211: Set beacon interval %d", value);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_BEACON, 0);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
- NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value);
-
- return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
- return -ENOBUFS;
-}
-
-
static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
int freq, int ht_enabled,
int sec_channel_offset)
return -1;
}
-#endif /* CONFIG_AP || HOSTAPD */
+static int wpa_driver_nl80211_sta_add(const char *ifname, void *priv,
+ struct hostapd_sta_add_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
-#ifdef CONFIG_AP
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
-static int wpa_driver_nl80211_set_beacon(void *priv,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len,
- int dtim_period)
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_NEW_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+ NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
+ params->supp_rates);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval);
+ if (params->ht_capabilities) {
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
+ sizeof(*params->ht_capabilities),
+ params->ht_capabilities);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
+ "result: %d (%s)", ret, strerror(-ret));
+ if (ret == -EEXIST)
+ ret = 0;
+ nla_put_failure:
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
{
struct wpa_driver_nl80211_data *drv = priv;
- return wpa_driver_nl80211_set_beacon_iface(drv->ifindex, priv,
- head, head_len,
- tail, tail_len,
- dtim_period);
-}
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(drv->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-#endif /* CONFIG_AP */
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == -ENOENT)
+ return 0;
+ return ret;
+ nla_put_failure:
+ return -ENOBUFS;
+}
-#if defined(CONFIG_AP) || defined(HOSTAPD)
static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
int ifidx)
{
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(struct wpa_driver_nl80211_data *drv,
- const char *ifname, enum nl80211_iftype iftype,
- const u8 *addr)
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+ const char *ifname,
+ enum nl80211_iftype iftype,
+ 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);
if (ret) {
nla_put_failure:
- wpa_printf(MSG_ERROR, "Failed to create interface %s.",
- ifname);
+ wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+ ifname, ret, strerror(-ret));
return ret;
}
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 &&
- 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;
}
-#endif /* CONFIG_AP || HOSTAPD */
-#ifdef CONFIG_AP
+static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname, enum nl80211_iftype iftype,
+ const u8 *addr, int wds)
+{
+ int ret;
+
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
+
+ /* if error occured and interface exists already */
+ if (ret == -ENFILE && if_nametoindex(ifname)) {
+ wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
+
+ /* Try to remove the interface that was already there. */
+ nl80211_remove_iface(drv, if_nametoindex(ifname));
-void ap_tx_status(void *ctx, const u8 *addr,
- const u8 *buf, size_t len, int ack);
-void ap_rx_from_unknown_sta(void *ctx, const u8 *addr);
-void ap_mgmt_rx(void *ctx, u8 *buf, size_t len, u16 stype,
- struct hostapd_frame_info *fi);
-void ap_mgmt_tx_cb(void *ctx, u8 *buf, size_t len, u16 stype, int ok);
+ /* Try to create the interface again */
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+ wds);
+ }
+
+ if (ret >= 0 && drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, ret, 1);
-#endif /* CONFIG_AP */
+ return ret;
+}
-#if defined(CONFIG_AP) || defined(HOSTAPD)
static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
{
struct ieee80211_hdr *hdr;
- u16 fc, type, stype;
+ u16 fc;
+ union wpa_event_data event;
hdr = (struct ieee80211_hdr *) buf;
fc = le_to_host16(hdr->frame_control);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+ event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+ event.tx_status.dst = hdr->addr1;
+ event.tx_status.data = buf;
+ event.tx_status.data_len = len;
+ event.tx_status.ack = ok;
+ wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
- switch (type) {
- case WLAN_FC_TYPE_MGMT:
- wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s",
- ok ? "ACK" : "fail");
-#ifdef HOSTAPD
- hostapd_mgmt_tx_cb(ctx, buf, len, stype, ok);
-#else /* HOSTAPD */
- ap_mgmt_tx_cb(ctx, buf, len, stype, ok);
-#endif /* HOSTAPD */
- break;
- case WLAN_FC_TYPE_CTRL:
- wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s",
- ok ? "ACK" : "fail");
- break;
- case WLAN_FC_TYPE_DATA:
-#ifdef HOSTAPD
- hostapd_tx_status(ctx, hdr->addr1, buf, len, ok);
-#else /* HOSTAPD */
- ap_tx_status(ctx, hdr->addr1, buf, len, ok);
-#endif /* HOSTAPD */
- break;
- default:
- wpa_printf(MSG_DEBUG, "unknown TX callback frame type %d",
- type);
- break;
- }
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+ u8 *buf, size_t len)
+{
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ 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,
- enum ieee80211_msg_type msg_type)
+ u8 *buf, size_t len, int datarate, int ssi_signal)
{
struct ieee80211_hdr *hdr;
- u16 fc, type, stype;
- size_t data_len = len;
- u8 *bssid;
- void *ctx = drv->ctx;
-#ifdef HOSTAPD
- struct hostapd_iface *iface = drv->hapd->iface;
- struct hostapd_data *hapd = NULL;
- int broadcast_bssid = 0;
- size_t i;
-#endif /* HOSTAPD */
-
- /*
- * PS-Poll frames are 16 bytes. All other frames are
- * 24 bytes or longer.
- */
- if (len < 16)
- return;
+ u16 fc;
+ union wpa_event_data event;
hdr = (struct ieee80211_hdr *) buf;
fc = le_to_host16(hdr->frame_control);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
-
- switch (type) {
- case WLAN_FC_TYPE_DATA:
- if (len < 24)
- return;
- switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
- case WLAN_FC_TODS:
- bssid = hdr->addr1;
- break;
- case WLAN_FC_FROMDS:
- bssid = hdr->addr2;
- break;
- default:
- /* discard */
- return;
- }
- break;
- case WLAN_FC_TYPE_CTRL:
- /* discard non-ps-poll frames */
- if (stype != WLAN_FC_STYPE_PSPOLL)
- return;
- bssid = hdr->addr1;
- break;
- case WLAN_FC_TYPE_MGMT:
- bssid = hdr->addr3;
- break;
- default:
- /* discard */
- return;
- }
-
-#ifdef HOSTAPD
- /* find interface frame belongs to */
- for (i = 0; i < iface->num_bss; i++) {
- if (memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) {
- hapd = iface->bss[i];
- break;
- }
- }
-
- if (hapd == NULL) {
- hapd = iface->bss[0];
-
- if (bssid[0] != 0xff || bssid[1] != 0xff ||
- bssid[2] != 0xff || bssid[3] != 0xff ||
- bssid[4] != 0xff || bssid[5] != 0xff) {
- /*
- * Unknown BSSID - drop frame if this is not from
- * passive scanning or a beacon (at least ProbeReq
- * frames to other APs may be allowed through RX
- * filtering in the wlan hw/driver)
- */
- if ((type != WLAN_FC_TYPE_MGMT ||
- stype != WLAN_FC_STYPE_BEACON))
- return;
- } else
- broadcast_bssid = 1;
- }
- ctx = hapd;
-#endif /* HOSTAPD */
-
- switch (msg_type) {
- case ieee80211_msg_normal:
- /* continue processing */
- break;
- case ieee80211_msg_tx_callback_ack:
- handle_tx_callback(ctx, buf, data_len, 1);
- return;
- case ieee80211_msg_tx_callback_fail:
- handle_tx_callback(ctx, buf, data_len, 0);
- return;
- }
-
- switch (type) {
+ switch (WLAN_FC_GET_TYPE(fc)) {
case WLAN_FC_TYPE_MGMT:
- if (stype != WLAN_FC_STYPE_BEACON &&
- stype != WLAN_FC_STYPE_PROBE_REQ)
- wpa_printf(MSG_MSGDUMP, "MGMT");
-#ifdef HOSTAPD
- if (broadcast_bssid) {
- for (i = 0; i < iface->num_bss; i++)
- hostapd_mgmt_rx(iface->bss[i], buf, data_len,
- stype, hfi);
- } else
- hostapd_mgmt_rx(hapd, buf, data_len, stype, hfi);
-#else /* HOSTAPD */
- ap_mgmt_rx(drv->ctx, buf, data_len, stype, hfi);
-#endif /* HOSTAPD */
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ 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");
-#ifdef HOSTAPD
- hostapd_rx_from_unknown_sta(drv->hapd, hdr->addr2);
-#else /* HOSTAPD */
- ap_rx_from_unknown_sta(drv->ctx, hdr->addr2);
-#endif /* HOSTAPD */
+ from_unknown_sta(drv, buf, len);
break;
case WLAN_FC_TYPE_DATA:
-#ifdef HOSTAPD
- hostapd_rx_from_unknown_sta(drv->hapd, hdr->addr2);
-#else /* HOSTAPD */
- ap_rx_from_unknown_sta(drv->ctx, hdr->addr2);
-#endif /* HOSTAPD */
+ from_unknown_sta(drv, buf, len);
break;
}
}
unsigned char buf[3000];
struct ieee80211_radiotap_iterator iter;
int ret;
- struct hostapd_frame_info hfi;
- int injected = 0, failed = 0, msg_type, rxflags = 0;
+ int datarate = 0, ssi_signal = 0;
+ int injected = 0, failed = 0, rxflags = 0;
len = recv(sock, buf, sizeof(buf), 0);
if (len < 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;
- hfi.phytype = 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;
}
}
return;
if (!injected)
- msg_type = ieee80211_msg_normal;
- else if (failed)
- msg_type = ieee80211_msg_tx_callback_fail;
+ handle_frame(drv, buf + iter.max_length,
+ len - iter.max_length, datarate, ssi_signal);
else
- msg_type = ieee80211_msg_tx_callback_ack;
-
- handle_frame(drv, buf + iter.max_length,
- len - iter.max_length, &hfi, msg_type);
+ 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
}
+static void nl80211_remove_monitor_interface(
+ struct wpa_driver_nl80211_data *drv)
+{
+ if (drv->monitor_ifidx >= 0) {
+ 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;
+ }
+}
+
+
static int
nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
{
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;
- 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));
return 0;
error:
- nl80211_remove_iface(drv, drv->monitor_ifidx);
+ nl80211_remove_monitor_interface(drv);
return -1;
}
-#endif /* CONFIG_AP || HOSTAPD */
-#ifdef CONFIG_AP
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
- struct wpa_driver_associate_params *params)
+static int wpa_driver_nl80211_hapd_send_eapol(
+ void *priv, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt, const u8 *own_addr)
{
- if (drv->monitor_ifidx < 0 &&
- nl80211_create_monitor_interface(drv))
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct ieee80211_hdr *hdr;
+ size_t len;
+ u8 *pos;
+ int res;
+#if 0 /* FIX */
+ int qos = sta->flags & WPA_STA_WMM;
+#else
+ int qos = 0;
+#endif
+
+ len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+ data_len;
+ hdr = os_zalloc(len);
+ if (hdr == NULL) {
+ printf("malloc() failed for i802_send_data(len=%lu)\n",
+ (unsigned long) len);
return -1;
+ }
+
+ hdr->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+ hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ if (encrypt)
+ hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+#if 0 /* To be enabled if qos determination is added above */
+ if (qos) {
+ hdr->frame_control |=
+ host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
+ }
+#endif
+
+ memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+ pos = (u8 *) (hdr + 1);
+
+#if 0 /* To be enabled if qos determination is added above */
+ if (qos) {
+ /* add an empty QoS header if needed */
+ pos[0] = 0;
+ pos[1] = 0;
+ pos += 2;
+ }
+#endif
+
+ memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+ pos += sizeof(rfc1042_header);
+ WPA_PUT_BE16(pos, ETH_P_PAE);
+ pos += 2;
+ memcpy(pos, data, data_len);
+
+ res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
+ "failed: %d (%s)",
+ (unsigned long) len, errno, strerror(errno));
+ }
+ os_free(hdr);
+
+ return res;
+}
+
+
+static u32 sta_flags_nl80211(int flags)
+{
+ u32 f = 0;
+
+ if (flags & WPA_STA_AUTHORIZED)
+ f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (flags & WPA_STA_WMM)
+ f |= BIT(NL80211_STA_FLAG_WME);
+ if (flags & WPA_STA_SHORT_PREAMBLE)
+ f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+ if (flags & WPA_STA_MFP)
+ f |= BIT(NL80211_STA_FLAG_MFP);
+
+ return f;
+}
+
+
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+ int total_flags, int flags_or,
+ int flags_and)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg, *flags = NULL;
+ struct nl80211_sta_flag_update upd;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ flags = nlmsg_alloc();
+ if (!flags) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(drv->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ /*
+ * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+ * can be removed eventually.
+ */
+ if (total_flags & WPA_STA_AUTHORIZED)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED);
+
+ if (total_flags & WPA_STA_WMM)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME);
+
+ if (total_flags & WPA_STA_SHORT_PREAMBLE)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE);
+
+ if (total_flags & WPA_STA_MFP)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
+
+ if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
+ goto nla_put_failure;
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+ upd.set = sta_flags_nl80211(flags_or);
+ NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+ nlmsg_free(flags);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ nlmsg_free(flags);
+ return -ENOBUFS;
+}
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
if (wpa_driver_nl80211_set_mode(drv, params->mode) ||
wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
- nl80211_remove_iface(drv, drv->monitor_ifidx);
- drv->monitor_ifidx = -1;
+ nl80211_remove_monitor_interface(drv);
return -1;
}
return 0;
}
-#endif /* CONFIG_AP */
-static int wpa_driver_nl80211_associate(
- void *priv, struct wpa_driver_associate_params *params)
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
{
- struct wpa_driver_nl80211_data *drv = priv;
- int ret = -1;
struct nl_msg *msg;
-
-#ifdef CONFIG_AP
- if (params->mode == 2)
- return wpa_driver_nl80211_ap(drv, params);
-#endif /* CONFIG_AP */
-
-#ifndef NO_WEXT
- wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
- params->drop_unencrypted);
-#endif /* NO_WEXT */
-
- drv->associated = 0;
+ int ret = -1;
msg = nlmsg_alloc();
if (!msg)
return -1;
- wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
- drv->ifindex);
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_ASSOCIATE, 0);
-
+ NL80211_CMD_LEAVE_IBSS, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- if (params->bssid) {
- wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
- MAC2STR(params->bssid));
- NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
}
- if (params->freq) {
- wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+ int count = 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
+
+ if (wpa_driver_nl80211_set_mode(drv, params->mode)) {
+ wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+ "IBSS mode");
+ return -1;
+ }
+
+retry:
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_JOIN_IBSS, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+ goto nla_put_failure;
+
+ wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
+ params->ssid, params->ssid_len);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+ drv->ssid_len = params->ssid_len;
+
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+
+ ret = nl80211_set_conn_keys(params, msg);
+ if (ret)
+ goto nla_put_failure;
+
+ if (params->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG,
+ " * Extra IEs for Beacon/Probe Response frames",
+ params->wpa_ie, params->wpa_ie_len);
+ NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ count++;
+ if (ret == -EALREADY && count == 1) {
+ wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
+ "forced leave");
+ nl80211_leave_ibss(drv);
+ nlmsg_free(msg);
+ goto retry;
+ }
+
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_connect(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ struct nl_msg *msg;
+ enum nl80211_auth_type type;
+ int ret = 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_CONNECT, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+ }
+ if (params->freq) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+ }
+ if (params->ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
+ params->ssid, params->ssid_len);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ if (params->ssid_len > sizeof(drv->ssid))
+ goto nla_put_failure;
+ os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+ drv->ssid_len = params->ssid_len;
+ }
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len);
+ if (params->wpa_ie)
+ NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie);
+
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ type = NL80211_AUTHTYPE_SHARED_KEY;
+ else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+ type = NL80211_AUTHTYPE_NETWORK_EAP;
+ else if (params->auth_alg & WPA_AUTH_ALG_FT)
+ type = NL80211_AUTHTYPE_FT;
+ else
+ goto nla_put_failure;
+
+ wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
+ NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+
+ if (params->wpa_ie && params->wpa_ie_len) {
+ enum nl80211_wpa_versions ver;
+
+ if (params->wpa_ie[0] == WLAN_EID_RSN)
+ ver = NL80211_WPA_VERSION_2;
+ else
+ ver = NL80211_WPA_VERSION_1;
+
+ wpa_printf(MSG_DEBUG, " * WPA Version %d", ver);
+ NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+ }
+
+ if (params->pairwise_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->pairwise_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+ }
+
+ if (params->group_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->group_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+ }
+
+ if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+ params->key_mgmt_suite == KEY_MGMT_PSK) {
+ int mgmt = WLAN_AKM_SUITE_PSK;
+
+ switch (params->key_mgmt_suite) {
+ case KEY_MGMT_802_1X:
+ mgmt = WLAN_AKM_SUITE_8021X;
+ break;
+ case KEY_MGMT_PSK:
+ default:
+ mgmt = WLAN_AKM_SUITE_PSK;
+ break;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
+ }
+
+ ret = nl80211_set_conn_keys(params, msg);
+ if (ret)
+ goto nla_put_failure;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+
+}
+
+
+static int wpa_driver_nl80211_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ int ret = -1;
+ struct nl_msg *msg;
+
+ if (params->mode == IEEE80211_MODE_AP)
+ return wpa_driver_nl80211_ap(drv, params);
+
+ if (params->mode == IEEE80211_MODE_IBSS)
+ return wpa_driver_nl80211_ibss(drv, params);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ if (wpa_driver_nl80211_set_mode(drv, params->mode) < 0)
+ return -1;
+ return wpa_driver_nl80211_connect(drv, params);
+ }
+
+ drv->associated = 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+ drv->ifindex);
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_ASSOCIATE, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
}
+ 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);
NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie);
+#ifdef CONFIG_IEEE80211W
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
+ NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
+#endif /* CONFIG_IEEE80211W */
+
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+
+ if (params->prev_bssid) {
+ wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR,
+ MAC2STR(params->prev_bssid));
+ NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+ params->prev_bssid);
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
drv->nlmode = nlmode;
- return 0;
+ ret = 0;
+ goto done;
}
- if (nlmode == drv->nlmode)
- return 0; /* Already in the requested mode */
+ 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 */
+ }
/* 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) {
+ 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 (!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) {
+ /* Setup additional AP mode functionality if needed */
+ if (drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv))
+ return -1;
+ } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
+ /* Remove additional AP mode functionality */
+ 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;
}
wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
__func__, drv->operstate, state, state ? "UP" : "DORMANT");
drv->operstate = state;
- return wpa_driver_nl80211_send_oper_ifla(
- drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT);
+ return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+ state ? IF_OPER_UP : IF_OPER_DORMANT);
}
-#ifdef HOSTAPD
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+ struct nl80211_sta_flag_update upd;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(drv->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (authorized)
+ upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+ NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
-static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+#ifdef HOSTAPD
static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
int ifindex)
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_key(const char *iface, void *priv, 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)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- return nl_set_encr(if_nametoindex(iface), drv, alg, addr, key_idx,
- set_tx, seq, seq_len, key, key_len);
-}
-
-
static inline int min_int(int a, int b)
{
if (a < b)
static int i802_set_rts(void *priv, int rts)
{
-#ifdef NO_WEXT
- return -1;
-#else /* NO_WEXT */
struct wpa_driver_nl80211_data *drv = priv;
- struct iwreq iwr;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+ u32 val;
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- iwr.u.rts.value = rts;
- iwr.u.rts.fixed = 1;
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
- if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) {
- perror("ioctl[SIOCSIWRTS]");
- return -1;
- }
+ if (rts >= 2347)
+ val = (u32) -1;
+ else
+ val = rts;
- return 0;
-#endif /* NO_WEXT */
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_WIPHY, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+nla_put_failure:
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+ "%d (%s)", rts, ret, strerror(-ret));
+ return ret;
}
static int i802_set_frag(void *priv, int frag)
{
-#ifdef NO_WEXT
- return -1;
-#else /* NO_WEXT */
struct wpa_driver_nl80211_data *drv = priv;
- struct iwreq iwr;
-
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- iwr.u.frag.value = frag;
- iwr.u.frag.fixed = 1;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+ u32 val;
- if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) {
- perror("ioctl[SIOCSIWFRAG]");
- return -1;
- }
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
- return 0;
-#endif /* NO_WEXT */
-}
+ if (frag >= 2346)
+ val = (u32) -1;
+ else
+ val = frag;
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_WIPHY, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
-static int i802_set_retry(void *priv, int short_retry, int long_retry)
-{
-#ifdef NO_WEXT
- return -1;
-#else /* NO_WEXT */
- struct wpa_driver_nl80211_data *drv = priv;
- struct iwreq iwr;
-
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-
- iwr.u.retry.value = short_retry;
- iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
- if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) {
- perror("ioctl[SIOCSIWRETRY(short)]");
- return -1;
- }
-
- iwr.u.retry.value = long_retry;
- iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
- if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) {
- perror("ioctl[SIOCSIWRETRY(long)]");
- return -1;
- }
-
- return 0;
-#endif /* NO_WEXT */
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+nla_put_failure:
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+ "%d: %d (%s)", frag, ret, strerror(-ret));
+ return ret;
}
}
-static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt, const u8 *own_addr)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- struct ieee80211_hdr *hdr;
- size_t len;
- u8 *pos;
- int res;
-#if 0 /* FIX */
- int qos = sta->flags & WLAN_STA_WME;
-#else
- int qos = 0;
-#endif
-
- len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
- data_len;
- hdr = os_zalloc(len);
- if (hdr == NULL) {
- printf("malloc() failed for i802_send_data(len=%lu)\n",
- (unsigned long) len);
- return -1;
- }
-
- hdr->frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
- hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
- if (encrypt)
- hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-#if 0 /* To be enabled if qos determination is added above */
- if (qos) {
- hdr->frame_control |=
- host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
- }
-#endif
-
- memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
- memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
- memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
- pos = (u8 *) (hdr + 1);
-
-#if 0 /* To be enabled if qos determination is added above */
- if (qos) {
- /* add an empty QoS header if needed */
- pos[0] = 0;
- pos[1] = 0;
- pos += 2;
- }
-#endif
-
- memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
- pos += sizeof(rfc1042_header);
- WPA_PUT_BE16(pos, ETH_P_PAE);
- pos += 2;
- memcpy(pos, data, data_len);
-
- res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt);
- if (res < 0) {
- wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
- "failed: %d (%s)",
- (unsigned long) len, errno, strerror(errno));
- }
- free(hdr);
-
- return res;
-}
-
-
-static int i802_sta_add(const char *ifname, void *priv,
- struct hostapd_sta_add_params *params)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- 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_NEW_STATION, 0);
-
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
- if_nametoindex(drv->ifname));
- NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
- NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
- params->supp_rates);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
- params->listen_interval);
-
-#ifdef CONFIG_IEEE80211N
- if (params->ht_capabilities) {
- NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
- params->ht_capabilities->length,
- ¶ms->ht_capabilities->data);
- }
-#endif /* CONFIG_IEEE80211N */
-
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
- if (ret)
- wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
- "result: %d (%s)", ret, strerror(-ret));
- if (ret == -EEXIST)
- ret = 0;
- nla_put_failure:
- return ret;
-}
-
-
-static int i802_sta_remove(void *priv, const u8 *addr)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- struct nl_msg *msg;
- int ret;
-
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
-
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_STATION, 0);
-
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
- if_nametoindex(drv->ifname));
- NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
- if (ret == -ENOENT)
- return 0;
- return ret;
- nla_put_failure:
- return -ENOBUFS;
-}
-
-
-static int i802_sta_set_flags(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- struct nl_msg *msg, *flags = NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
-
- flags = nlmsg_alloc();
- if (!flags) {
- nlmsg_free(msg);
- return -ENOMEM;
- }
-
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
-
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
- if_nametoindex(drv->ifname));
- NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-
- if (total_flags & WLAN_STA_AUTHORIZED)
- NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED);
-
- if (total_flags & WLAN_STA_WMM)
- NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME);
-
- if (total_flags & WLAN_STA_SHORT_PREAMBLE)
- NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE);
-
- if (total_flags & WLAN_STA_MFP)
- NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
-
- if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
- goto nla_put_failure;
-
- nlmsg_free(flags);
-
- return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
- nlmsg_free(flags);
- return -ENOBUFS;
-}
-
-
static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
int cw_min, int cw_max, int burst_time)
{
}
-static int i802_bss_add(void *priv, const char *ifname, const u8 *bssid)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- int ifidx;
- struct i802_bss *bss;
-
- bss = os_zalloc(sizeof(*bss));
- if (bss == NULL)
- return -1;
- bss->ifindex = if_nametoindex(ifname);
-
- ifidx = nl80211_create_iface(priv, ifname, NL80211_IFTYPE_AP, bssid);
- if (ifidx < 0) {
- os_free(bss);
- return -1;
- }
- if (hostapd_set_iface_flags(priv, ifname, 1)) {
- nl80211_remove_iface(priv, ifidx);
- os_free(bss);
- return -1;
- }
- bss->next = drv->bss.next;
- drv->bss.next = bss;
- return 0;
-}
-
-
-static int i802_bss_remove(void *priv, const char *ifname)
-{
- struct wpa_driver_nl80211_data *drv = priv;
- struct i802_bss *bss, *prev;
- int ifindex = if_nametoindex(ifname);
- nl80211_remove_iface(priv, ifindex);
- prev = &drv->bss;
- bss = drv->bss.next;
- while (bss) {
- if (ifindex == bss->ifindex) {
- prev->next = bss->next;
- os_free(bss);
- break;
- }
- prev = bss;
- bss = bss->next;
- }
- return 0;
-}
-
-
-static int i802_set_beacon(const char *iface, void *priv,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period)
-{
- return wpa_driver_nl80211_set_beacon_iface(if_nametoindex(iface), priv,
- head, head_len,
- tail, tail_len,
- dtim_period);
-}
-
-
-static int i802_del_beacon(struct wpa_driver_nl80211_data *drv)
-{
- struct nl_msg *msg;
-
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
-
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_BEACON, 0);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname));
-
- return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
- return -ENOBUFS;
-}
-
-
static int i802_set_bss(void *priv, int cts, int preamble, int slot)
{
struct wpa_driver_nl80211_data *drv = priv;
}
-static enum nl80211_iftype i802_if_type(enum hostapd_driver_if_type type)
-{
- switch (type) {
- case HOSTAPD_IF_VLAN:
- return NL80211_IFTYPE_AP_VLAN;
- }
- return -1;
-}
-
-
-static int i802_if_add(const char *iface, void *priv,
- enum hostapd_driver_if_type type, char *ifname,
- const u8 *addr)
-{
- if (nl80211_create_iface(priv, ifname, i802_if_type(type), addr) < 0)
- return -1;
- return 0;
-}
-
-
-static int i802_if_update(void *priv, enum hostapd_driver_if_type type,
- char *ifname, const u8 *addr)
-{
- /* unused at the moment */
- return -1;
-}
-
-
-static int i802_if_remove(void *priv, enum hostapd_driver_if_type type,
- const char *ifname, const u8 *addr)
-{
- nl80211_remove_iface(priv, if_nametoindex(ifname));
- return 0;
-}
-
-
static int i802_set_sta_vlan(void *priv, const u8 *addr,
const char *ifname, int vlan_id)
{
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(drv->ifname));
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
if_nametoindex(ifname));
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
+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;
+ 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 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)) {
- struct hostapd_data *hapd;
- hapd = hostapd_sta_get_bss(drv->hapd, lladdr.sll_addr);
- if (!hapd)
- return;
- hostapd_eapol_receive(hapd, lladdr.sll_addr, buf, len);
- }
+ if (have_ifidx(drv, lladdr.sll_ifindex))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
}
if (drv == NULL)
return NULL;
- drv->hapd = hapd;
drv->bss.ifindex = drv->ifindex;
drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
/* 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 && set_ifhwaddr(drv, drv->ifname, params->bssid))
- goto failed;
-
- /* Initialise a monitor interface */
- if (nl80211_create_monitor_interface(drv))
- goto failed;
+ if (params->bssid) {
+ if (linux_set_ifhwaddr(drv->ioctl_sock, drv->ifname,
+ params->bssid))
+ goto failed;
+ }
- if (nl80211_set_mode(drv, drv->ifindex, NL80211_IFTYPE_AP)) {
+ if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_AP)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
"into AP mode", drv->ifname);
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, drv->hapd->own_addr))
+ if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, params->own_addr))
goto failed;
return drv;
failed:
- if (drv->monitor_ifidx >= 0)
- nl80211_remove_iface(drv, drv->monitor_ifidx);
+ nl80211_remove_monitor_interface(drv);
if (drv->ioctl_sock >= 0)
close(drv->ioctl_sock);
#endif /* HOSTAPD */
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+ enum wpa_driver_if_type type)
+{
+ switch (type) {
+ case WPA_IF_STATION:
+ return NL80211_IFTYPE_STATION;
+ case WPA_IF_AP_VLAN:
+ return NL80211_IFTYPE_AP_VLAN;
+ case WPA_IF_AP_BSS:
+ return NL80211_IFTYPE_AP;
+ }
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_if_add(const char *iface, void *priv,
+ enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr,
+ void *bss_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ int ifidx;
+#ifdef HOSTAPD
+ struct i802_bss *bss = NULL;
+
+ if (type == WPA_IF_AP_BSS) {
+ bss = os_zalloc(sizeof(*bss));
+ if (bss == NULL)
+ return -1;
+ }
+#endif /* HOSTAPD */
+
+ ifidx = nl80211_create_iface(drv, ifname,
+ wpa_driver_nl80211_if_type(type), addr,
+ 0);
+ if (ifidx < 0) {
+#ifdef HOSTAPD
+ os_free(bss);
+#endif /* HOSTAPD */
+ return -1;
+ }
+
+#ifdef HOSTAPD
+ if (type == WPA_IF_AP_BSS) {
+ if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ nl80211_remove_iface(drv, ifidx);
+ os_free(bss);
+ return -1;
+ }
+ bss->ifindex = ifidx;
+ bss->next = drv->bss.next;
+ drv->bss.next = bss;
+ }
+#endif /* HOSTAPD */
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_if_remove(void *priv,
+ enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ int ifindex = if_nametoindex(ifname);
+
+ nl80211_remove_iface(drv, ifindex);
+
+#ifdef HOSTAPD
+ if (type == WPA_IF_AP_BSS) {
+ struct i802_bss *bss, *prev;
+ prev = &drv->bss;
+ bss = drv->bss.next;
+ while (bss) {
+ if (ifindex == bss->ifindex) {
+ prev->next = bss->next;
+ os_free(bss);
+ break;
+ }
+ prev = bss;
+ bss = bss->next;
+ }
+ }
+#endif /* HOSTAPD */
+
+ return 0;
+}
+
+
+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",
.deinit = wpa_driver_nl80211_deinit,
.get_capa = wpa_driver_nl80211_get_capa,
.set_operstate = wpa_driver_nl80211_set_operstate,
+ .set_supp_port = wpa_driver_nl80211_set_supp_port,
.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 */
-#if defined(CONFIG_AP) || defined(HOSTAPD)
+ .if_add = wpa_driver_nl80211_if_add,
+ .if_remove = wpa_driver_nl80211_if_remove,
.send_mlme = wpa_driver_nl80211_send_mlme,
- .set_beacon_int = wpa_driver_nl80211_set_beacon_int,
.get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
-#endif /* CONFIG_AP || HOSTAPD */
+ .sta_add = wpa_driver_nl80211_sta_add,
+ .sta_remove = wpa_driver_nl80211_sta_remove,
+ .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+ .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
#ifdef HOSTAPD
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
- .hapd_set_key = i802_set_key,
.get_seqnum = i802_get_seqnum,
.flush = i802_flush,
.read_sta_data = i802_read_sta_data,
- .hapd_send_eapol = i802_send_eapol,
- .sta_set_flags = i802_sta_set_flags,
.sta_deauth = i802_sta_deauth,
.sta_disassoc = i802_sta_disassoc,
- .sta_remove = i802_sta_remove,
- .sta_add = i802_sta_add,
.get_inact_sec = i802_get_inact_sec,
.sta_clear_stats = i802_sta_clear_stats,
.set_freq = i802_set_freq,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
- .set_retry = i802_set_retry,
.set_rate_sets = i802_set_rate_sets,
- .hapd_set_beacon = i802_set_beacon,
.set_cts_protect = i802_set_cts_protect,
.set_preamble = i802_set_preamble,
.set_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params,
- .bss_add = i802_bss_add,
- .bss_remove = i802_bss_remove,
- .if_add = i802_if_add,
- .if_update = i802_if_update,
- .if_remove = i802_if_remove,
.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,
};