hostapd/src/drivers/driver_wext.c
Jouni Malinen 3fbda8f943 WEXT: Fixed re-initialization of removed and re-inserted interface
Network device ifindex will change when the interface is re-inserted.
driver_wext.c will need to accept netlink events from "unknown" (based on
ifindex) interfaces when a previously used card was removed earlier. If the
previously removed interface is added back, the driver_wext data need to be
updated to match with the new ifindex value. In addition, the initial setup
tasks for the card (set interface up, update ifindex, set mode, etc.) from
wpa_driver_wext_init() need to be run again.
2008-07-25 01:30:53 +03:00

2778 lines
70 KiB
C

/*
* WPA Supplicant - driver interaction with generic Linux Wireless Extensions
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
* This file implements a driver interface for the Linux Wireless Extensions.
* When used with WE-18 or newer, this interface can be used as-is with number
* of drivers. In addition to this, some of the common functions in this file
* can be used by other driver interface implementations that use generic WE
* ioctls, but require private ioctls for some of the functionality.
*/
#include "includes.h"
#include <sys/ioctl.h>
#include <net/if_arp.h>
#include "wireless_copy.h"
#include "common.h"
#include "driver.h"
#include "eloop.h"
#include "priv_netlink.h"
#include "driver_wext.h"
#include "ieee802_11_defs.h"
#include "wpa_common.h"
#ifdef CONFIG_CLIENT_MLME
#include <netpacket/packet.h>
/* old definitions from net/mac80211 */
typedef u32 __bitwise __be32;
typedef u64 __bitwise __be64;
#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
#define PRISM2_IOCTL_HOSTAPD (SIOCIWFIRSTPRIV + 3)
#define PRISM2_PARAM_USER_SPACE_MLME 1045
#define PRISM2_PARAM_MGMT_IF 1046
#define PRISM2_HOSTAPD_ADD_STA 2
#define PRISM2_HOSTAPD_REMOVE_STA 3
#define PRISM2_HOSTAPD_GET_HW_FEATURES 1002
#define PRISM2_HOSTAPD_MAX_BUF_SIZE 2048
#ifndef ALIGNED
#define ALIGNED __attribute__ ((aligned))
#endif
struct prism2_hostapd_param {
u32 cmd;
u8 sta_addr[ETH_ALEN];
u8 pad[2];
union {
struct {
u16 aid;
u16 capability;
u8 supp_rates[32];
u8 wds_flags;
#define IEEE80211_STA_DYNAMIC_ENC BIT(0)
u8 enc_flags;
u16 listen_interval;
} add_sta;
struct {
u16 num_modes;
u16 flags;
u8 data[0] ALIGNED; /* num_modes * feature data */
} hw_features;
struct {
u16 mode; /* MODE_* */
u16 num_supported_rates;
u16 num_basic_rates;
u8 data[0] ALIGNED; /* num_supported_rates * u16 +
* num_basic_rates * u16 */
} set_rate_sets;
struct {
u16 mode; /* MODE_* */
u16 chan;
u32 flag;
u8 power_level; /* regulatory limit in dBm */
u8 antenna_max;
} set_channel_flag;
struct {
u32 rd;
} set_regulatory_domain;
struct {
u32 queue;
s32 aifs;
u32 cw_min;
u32 cw_max;
u32 burst_time; /* maximum burst time in 0.1 ms, i.e.,
* 10 = 1 ms */
} tx_queue_params;
} u;
};
struct hostapd_ioctl_hw_modes_hdr {
int mode;
int num_channels;
int num_rates;
};
/*
* frame format for the management interface that is slated
* to be replaced by "cooked monitor" with radiotap
*/
#define IEEE80211_FI_VERSION 0x80211001
struct ieee80211_frame_info {
__be32 version;
__be32 length;
__be64 mactime;
__be64 hosttime;
__be32 phytype;
__be32 channel;
__be32 datarate;
__be32 antenna;
__be32 priority;
__be32 ssi_type;
__be32 ssi_signal;
__be32 ssi_noise;
__be32 preamble;
__be32 encoding;
/* Note: this structure is otherwise identical to capture format used
* in linux-wlan-ng, but this additional field is used to provide meta
* data about the frame to hostapd. This was the easiest method for
* providing this information, but this might change in the future. */
__be32 msg_type;
} __attribute__ ((packed));
/* old mode definitions */
enum {
MODE_IEEE80211A = 0 /* IEEE 802.11a */,
MODE_IEEE80211B = 1 /* IEEE 802.11b only */,
MODE_ATHEROS_TURBO = 2 /* Atheros Turbo mode (2x.11a at 5 GHz) */,
MODE_IEEE80211G = 3 /* IEEE 802.11g (and 802.11b compatibility) */,
MODE_ATHEROS_TURBOG = 4 /* Atheros Turbo mode (2x.11g at 2.4 GHz) */,
NUM_IEEE80211_MODES = 5
};
#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif
#endif /* CONFIG_CLIENT_MLME */
static int wpa_driver_wext_flush_pmkid(void *priv);
static int wpa_driver_wext_get_range(void *priv);
static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
static int wpa_driver_wext_send_oper_ifla(struct wpa_driver_wext_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, "WEXT: Operstate: linkmode=%d, operstate=%d",
linkmode, operstate);
ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: "
"%s (assume operstate is not supported)",
strerror(errno));
}
return ret < 0 ? -1 : 0;
}
int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_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;
}
/**
* wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @bssid: Buffer for BSSID
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
perror("ioctl[SIOCGIWAP]");
ret = -1;
}
os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
return ret;
}
/**
* wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @bssid: BSSID
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.ap_addr.sa_family = ARPHRD_ETHER;
if (bssid)
os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN);
else
os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
perror("ioctl[SIOCSIWAP]");
ret = -1;
}
return ret;
}
/**
* wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @ssid: Buffer for the SSID; must be at least 32 bytes long
* Returns: SSID length on success, -1 on failure
*/
int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.essid.pointer = (caddr_t) ssid;
iwr.u.essid.length = 32;
if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
perror("ioctl[SIOCGIWESSID]");
ret = -1;
} else {
ret = iwr.u.essid.length;
if (ret > 32)
ret = 32;
/* Some drivers include nul termination in the SSID, so let's
* remove it here before further processing. WE-21 changes this
* to explicitly require the length _not_ to include nul
* termination. */
if (ret > 0 && ssid[ret - 1] == '\0' &&
drv->we_version_compiled < 21)
ret--;
}
return ret;
}
/**
* wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @ssid: SSID
* @ssid_len: Length of SSID (0..32)
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
char buf[33];
if (ssid_len > 32)
return -1;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
/* flags: 1 = ESSID is active, 0 = not (promiscuous) */
iwr.u.essid.flags = (ssid_len != 0);
os_memset(buf, 0, sizeof(buf));
os_memcpy(buf, ssid, ssid_len);
iwr.u.essid.pointer = (caddr_t) buf;
if (drv->we_version_compiled < 21) {
/* For historic reasons, set SSID length to include one extra
* character, C string nul termination, even though SSID is
* really an octet string that should not be presented as a C
* string. Some Linux drivers decrement the length by one and
* can thus end up missing the last octet of the SSID if the
* length is not incremented here. WE-21 changes this to
* explicitly require the length _not_ to include nul
* termination. */
if (ssid_len)
ssid_len++;
}
iwr.u.essid.length = ssid_len;
if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
perror("ioctl[SIOCSIWESSID]");
ret = -1;
}
return ret;
}
/**
* wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @freq: Frequency in MHz
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_set_freq(void *priv, int freq)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.freq.m = freq * 100000;
iwr.u.freq.e = 1;
if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
perror("ioctl[SIOCSIWFREQ]");
ret = -1;
}
return ret;
}
static void
wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
{
union wpa_event_data data;
wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'",
custom);
os_memset(&data, 0, sizeof(data));
/* Host AP driver */
if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
data.michael_mic_failure.unicast =
os_strstr(custom, " unicast ") != NULL;
/* TODO: parse parameters(?) */
wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
} else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
char *spos;
int bytes;
spos = custom + 17;
bytes = strspn(spos, "0123456789abcdefABCDEF");
if (!bytes || (bytes & 1))
return;
bytes /= 2;
data.assoc_info.req_ies = os_malloc(bytes);
if (data.assoc_info.req_ies == NULL)
return;
data.assoc_info.req_ies_len = bytes;
hexstr2bin(spos, data.assoc_info.req_ies, bytes);
spos += bytes * 2;
data.assoc_info.resp_ies = NULL;
data.assoc_info.resp_ies_len = 0;
if (os_strncmp(spos, " RespIEs=", 9) == 0) {
spos += 9;
bytes = strspn(spos, "0123456789abcdefABCDEF");
if (!bytes || (bytes & 1))
goto done;
bytes /= 2;
data.assoc_info.resp_ies = os_malloc(bytes);
if (data.assoc_info.resp_ies == NULL)
goto done;
data.assoc_info.resp_ies_len = bytes;
hexstr2bin(spos, data.assoc_info.resp_ies, bytes);
}
wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
done:
os_free(data.assoc_info.resp_ies);
os_free(data.assoc_info.req_ies);
#ifdef CONFIG_PEERKEY
} else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
"STKSTART.request '%s'", custom + 17);
return;
}
wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
#endif /* CONFIG_PEERKEY */
}
}
static int wpa_driver_wext_event_wireless_michaelmicfailure(
void *ctx, const char *ev, size_t len)
{
const struct iw_michaelmicfailure *mic;
union wpa_event_data data;
if (len < sizeof(*mic))
return -1;
mic = (const struct iw_michaelmicfailure *) ev;
wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
"flags=0x%x src_addr=" MACSTR, mic->flags,
MAC2STR(mic->src_addr.sa_data));
os_memset(&data, 0, sizeof(data));
data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP);
wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
return 0;
}
static int wpa_driver_wext_event_wireless_pmkidcand(
struct wpa_driver_wext_data *drv, const char *ev, size_t len)
{
const struct iw_pmkid_cand *cand;
union wpa_event_data data;
const u8 *addr;
if (len < sizeof(*cand))
return -1;
cand = (const struct iw_pmkid_cand *) ev;
addr = (const u8 *) cand->bssid.sa_data;
wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
"flags=0x%x index=%d bssid=" MACSTR, cand->flags,
cand->index, MAC2STR(addr));
os_memset(&data, 0, sizeof(data));
os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN);
data.pmkid_candidate.index = cand->index;
data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH;
wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
return 0;
}
static int wpa_driver_wext_event_wireless_assocreqie(
struct wpa_driver_wext_data *drv, const char *ev, int len)
{
if (len < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
len);
os_free(drv->assoc_req_ies);
drv->assoc_req_ies = os_malloc(len);
if (drv->assoc_req_ies == NULL) {
drv->assoc_req_ies_len = 0;
return -1;
}
os_memcpy(drv->assoc_req_ies, ev, len);
drv->assoc_req_ies_len = len;
return 0;
}
static int wpa_driver_wext_event_wireless_assocrespie(
struct wpa_driver_wext_data *drv, const char *ev, int len)
{
if (len < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
len);
os_free(drv->assoc_resp_ies);
drv->assoc_resp_ies = os_malloc(len);
if (drv->assoc_resp_ies == NULL) {
drv->assoc_resp_ies_len = 0;
return -1;
}
os_memcpy(drv->assoc_resp_ies, ev, len);
drv->assoc_resp_ies_len = len;
return 0;
}
static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
{
union wpa_event_data data;
if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
return;
os_memset(&data, 0, sizeof(data));
if (drv->assoc_req_ies) {
data.assoc_info.req_ies = drv->assoc_req_ies;
drv->assoc_req_ies = NULL;
data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
}
if (drv->assoc_resp_ies) {
data.assoc_info.resp_ies = drv->assoc_resp_ies;
drv->assoc_resp_ies = NULL;
data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
}
wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
os_free(data.assoc_info.req_ies);
os_free(data.assoc_info.resp_ies);
}
static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
void *ctx, char *data, int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
pos = data;
end = data + len;
while (pos + IW_EV_LCP_LEN <= end) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
iwe->cmd, iwe->len);
if (iwe->len <= IW_EV_LCP_LEN)
return;
custom = pos + IW_EV_POINT_LEN;
if (drv->we_version_compiled > 18 &&
(iwe->cmd == IWEVMICHAELMICFAILURE ||
iwe->cmd == IWEVCUSTOM ||
iwe->cmd == IWEVASSOCREQIE ||
iwe->cmd == IWEVASSOCRESPIE ||
iwe->cmd == IWEVPMKIDCAND)) {
/* WE-19 removed the pointer from struct iw_point */
char *dpos = (char *) &iwe_buf.u.data.length;
int dlen = dpos - (char *) &iwe_buf;
os_memcpy(dpos, pos + IW_EV_LCP_LEN,
sizeof(struct iw_event) - dlen);
} else {
os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
custom += IW_EV_POINT_OFF;
}
switch (iwe->cmd) {
case SIOCGIWAP:
wpa_printf(MSG_DEBUG, "Wireless event: new AP: "
MACSTR,
MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
if (is_zero_ether_addr(
(const u8 *) iwe->u.ap_addr.sa_data) ||
os_memcmp(iwe->u.ap_addr.sa_data,
"\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
0) {
os_free(drv->assoc_req_ies);
drv->assoc_req_ies = NULL;
os_free(drv->assoc_resp_ies);
drv->assoc_resp_ies = NULL;
wpa_supplicant_event(ctx, EVENT_DISASSOC,
NULL);
} else {
wpa_driver_wext_event_assoc_ies(drv);
wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
}
break;
case IWEVMICHAELMICFAILURE:
wpa_driver_wext_event_wireless_michaelmicfailure(
ctx, custom, iwe->u.data.length);
break;
case IWEVCUSTOM:
if (custom + iwe->u.data.length > end)
return;
buf = os_malloc(iwe->u.data.length + 1);
if (buf == NULL)
return;
os_memcpy(buf, custom, iwe->u.data.length);
buf[iwe->u.data.length] = '\0';
wpa_driver_wext_event_wireless_custom(ctx, buf);
os_free(buf);
break;
case SIOCGIWSCAN:
drv->scan_complete_events = 1;
eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
drv, ctx);
wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
break;
case IWEVASSOCREQIE:
wpa_driver_wext_event_wireless_assocreqie(
drv, custom, iwe->u.data.length);
break;
case IWEVASSOCRESPIE:
wpa_driver_wext_event_wireless_assocrespie(
drv, custom, iwe->u.data.length);
break;
case IWEVPMKIDCAND:
wpa_driver_wext_event_wireless_pmkidcand(
drv, custom, iwe->u.data.length);
break;
}
pos += iwe->len;
}
}
static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
void *ctx, char *buf, size_t len,
int del)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
if (len > sizeof(event.interface_status.ifname))
len = sizeof(event.interface_status.ifname) - 1;
os_memcpy(event.interface_status.ifname, buf, len);
event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
EVENT_INTERFACE_ADDED;
wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
del ? "DEL" : "NEW",
event.interface_status.ifname,
del ? "removed" : "added");
if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
if (del)
drv->if_removed = 1;
else
drv->if_removed = 0;
}
wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
}
static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
struct nlmsghdr *h)
{
struct ifinfomsg *ifi;
int attrlen, nlmsg_len, 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);
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
if (os_strcmp(((char *) attr) + rta_len, drv->ifname)
== 0)
return 1;
else
break;
}
attr = RTA_NEXT(attr, attrlen);
}
return 0;
}
static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
int ifindex, struct nlmsghdr *h)
{
if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
return 1;
if (drv->if_removed && wpa_driver_wext_own_ifname(drv, h)) {
drv->ifindex = if_nametoindex(drv->ifname);
wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
"interface");
wpa_driver_wext_finish_drv_init(drv);
return 1;
}
return 0;
}
static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv,
void *ctx, struct nlmsghdr *h,
size_t len)
{
struct ifinfomsg *ifi;
int attrlen, nlmsg_len, rta_len;
struct rtattr * attr;
if (len < sizeof(*ifi))
return;
ifi = NLMSG_DATA(h);
if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) {
wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
ifi->ifi_index);
return;
}
wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
"(%s%s%s%s)",
drv->operstate, ifi->ifi_flags,
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
/*
* Some drivers send the association event before the operup event--in
* this case, lifting operstate in wpa_driver_wext_set_operstate()
* fails. This will hit us when wpa_supplicant does not need to do
* IEEE 802.1X authentication
*/
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING))
wpa_driver_wext_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);
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_WIRELESS) {
wpa_driver_wext_event_wireless(
drv, ctx, ((char *) attr) + rta_len,
attr->rta_len - rta_len);
} else if (attr->rta_type == IFLA_IFNAME) {
wpa_driver_wext_event_link(drv, ctx,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 0);
}
attr = RTA_NEXT(attr, attrlen);
}
}
static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv,
void *ctx, struct nlmsghdr *h,
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;
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
wpa_driver_wext_event_link(drv, ctx,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 1);
}
attr = RTA_NEXT(attr, attrlen);
}
}
static void wpa_driver_wext_event_receive(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_wext_event_rtm_newlink(eloop_ctx, sock_ctx,
h, plen);
break;
case RTM_DELLINK:
wpa_driver_wext_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 int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv,
const char *ifname, int *flags)
{
struct ifreq ifr;
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
perror("ioctl[SIOCGIFFLAGS]");
return -1;
}
*flags = ifr.ifr_flags & 0xffff;
return 0;
}
/**
* wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS)
* @drv: driver_wext private data
* @flags: Pointer to returned flags value
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags)
{
return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags);
}
static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv,
const char *ifname, int flags)
{
struct ifreq ifr;
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_flags = flags & 0xffff;
if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
perror("SIOCSIFFLAGS");
return -1;
}
return 0;
}
#ifdef CONFIG_CLIENT_MLME
static int wpa_driver_prism2_param_set(struct wpa_driver_wext_data *drv,
int param, int value)
{
struct iwreq iwr;
int *i;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
i = (int *) iwr.u.name;
*i++ = param;
*i++ = value;
return ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr);
}
static int wpa_driver_prism2_param_get(struct wpa_driver_wext_data *drv,
int param)
{
struct iwreq iwr;
int *i;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
i = (int *) iwr.u.name;
*i = param;
if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_GET_PRISM2_PARAM, &iwr) < 0) {
perror("ioctl[PRISM2_IOCTL_GET_PRISM2_PARAM]");
return -1;
}
return *i;
}
#endif /* CONFIG_CLIENT_MLME */
/**
* wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS)
* @drv: driver_wext private data
* @flags: New value for flags
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags)
{
return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags);
}
/**
* wpa_driver_wext_init - Initialize WE driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
* e.g., wpa_supplicant_event()
* @ifname: interface name, e.g., wlan0
* Returns: Pointer to private data, %NULL on failure
*/
void * wpa_driver_wext_init(void *ctx, const char *ifname)
{
int s;
struct sockaddr_nl local;
struct wpa_driver_wext_data *drv;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
drv->ctx = ctx;
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (drv->ioctl_sock < 0) {
perror("socket(PF_INET,SOCK_DGRAM)");
os_free(drv);
return NULL;
}
s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (s < 0) {
perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
close(drv->ioctl_sock);
os_free(drv);
return NULL;
}
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);
close(drv->ioctl_sock);
os_free(drv);
return NULL;
}
eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx);
drv->event_sock = s;
drv->mlme_sock = -1;
wpa_driver_wext_finish_drv_init(drv);
return drv;
}
static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
{
int flags;
if (wpa_driver_wext_get_ifflags(drv, &flags) != 0)
printf("Could not get interface '%s' flags\n", drv->ifname);
else if (!(flags & IFF_UP)) {
if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) {
printf("Could not set interface '%s' UP\n",
drv->ifname);
} else {
/*
* Wait some time to allow driver to initialize before
* starting configuring the driver. This seems to be
* needed at least some drivers that load firmware etc.
* when the interface is set up.
*/
wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting "
"a second for the driver to complete "
"initialization", drv->ifname);
sleep(1);
}
}
/*
* Make sure that the driver does not have any obsolete PMKID entries.
*/
wpa_driver_wext_flush_pmkid(drv);
if (wpa_driver_wext_set_mode(drv, 0) < 0) {
printf("Could not configure driver to use managed mode\n");
}
wpa_driver_wext_get_range(drv);
drv->ifindex = if_nametoindex(drv->ifname);
if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
/*
* Host AP driver may use both wlan# and wifi# interface in
* wireless events. Since some of the versions included WE-18
* support, let's add the alternative ifindex also from
* driver_wext.c for the time being. This may be removed at
* some point once it is believed that old versions of the
* driver are not in use anymore.
*/
char ifname2[IFNAMSIZ + 1];
os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
os_memcpy(ifname2, "wifi", 4);
wpa_driver_wext_alternative_ifindex(drv, ifname2);
}
wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT);
}
/**
* wpa_driver_wext_deinit - Deinitialize WE driver interface
* @priv: Pointer to private wext data from wpa_driver_wext_init()
*
* Shut down driver interface and processing of driver events. Free
* private data buffer if one was allocated in wpa_driver_wext_init().
*/
void wpa_driver_wext_deinit(void *priv)
{
struct wpa_driver_wext_data *drv = priv;
int flags;
eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
/*
* Clear possibly configured driver parameters in order to make it
* easier to use the driver after wpa_supplicant has been terminated.
*/
(void) wpa_driver_wext_set_bssid(drv,
(u8 *) "\x00\x00\x00\x00\x00\x00");
wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP);
eloop_unregister_read_sock(drv->event_sock);
if (drv->mlme_sock >= 0)
eloop_unregister_read_sock(drv->mlme_sock);
if (wpa_driver_wext_get_ifflags(drv, &flags) == 0)
(void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP);
#ifdef CONFIG_CLIENT_MLME
if (drv->mlmedev[0]) {
if (wpa_driver_wext_get_ifflags_ifname(drv, drv->mlmedev,
&flags) == 0)
(void) wpa_driver_wext_set_ifflags_ifname(
drv, drv->mlmedev, flags & ~IFF_UP);
wpa_driver_prism2_param_set(drv, PRISM2_PARAM_MGMT_IF, 0);
wpa_driver_prism2_param_set(drv, PRISM2_PARAM_USER_SPACE_MLME,
0);
}
#endif /* CONFIG_CLIENT_MLME */
close(drv->event_sock);
close(drv->ioctl_sock);
if (drv->mlme_sock >= 0)
close(drv->mlme_sock);
os_free(drv->assoc_req_ies);
os_free(drv->assoc_resp_ies);
os_free(drv);
}
/**
* wpa_driver_wext_scan_timeout - Scan timeout to report scan completion
* @eloop_ctx: Unused
* @timeout_ctx: ctx argument given to wpa_driver_wext_init()
*
* This function can be used as registered timeout when starting a scan to
* generate a scan completed event if the driver does not report this.
*/
void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
}
/**
* wpa_driver_wext_scan - Request the driver to initiate scan
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for
* all SSIDs (either active scan with broadcast SSID or passive
* scan
* @ssid_len: Length of the SSID
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0, timeout;
struct iw_scan_req req;
if (ssid_len > IW_ESSID_MAX_SIZE) {
wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
__FUNCTION__, (unsigned long) ssid_len);
return -1;
}
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
if (ssid && ssid_len) {
os_memset(&req, 0, sizeof(req));
req.essid_len = ssid_len;
req.bssid.sa_family = ARPHRD_ETHER;
os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
os_memcpy(req.essid, ssid, ssid_len);
iwr.u.data.pointer = (caddr_t) &req;
iwr.u.data.length = sizeof(req);
iwr.u.data.flags = IW_SCAN_THIS_ESSID;
}
if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
perror("ioctl[SIOCSIWSCAN]");
ret = -1;
}
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
timeout = 5;
if (drv->scan_complete_events) {
/*
* The driver seems to deliver SIOCGIWSCAN events to notify
* when scan is complete, so use longer timeout to avoid race
* conditions with scanning and following association request.
*/
timeout = 30;
}
wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
"seconds", ret, timeout);
eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
drv->ctx);
return ret;
}
static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
size_t *len)
{
struct iwreq iwr;
u8 *res_buf;
size_t res_buf_len;
res_buf_len = IW_SCAN_MAX_DATA;
for (;;) {
res_buf = os_malloc(res_buf_len);
if (res_buf == NULL)
return NULL;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.data.pointer = res_buf;
iwr.u.data.length = res_buf_len;
if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
break;
if (errno == E2BIG && res_buf_len < 100000) {
os_free(res_buf);
res_buf = NULL;
res_buf_len *= 2;
wpa_printf(MSG_DEBUG, "Scan results did not fit - "
"trying larger buffer (%lu bytes)",
(unsigned long) res_buf_len);
} else {
perror("ioctl[SIOCGIWSCAN]");
os_free(res_buf);
return NULL;
}
}
if (iwr.u.data.length > res_buf_len) {
os_free(res_buf);
return NULL;
}
*len = iwr.u.data.length;
return res_buf;
}
/*
* Data structure for collecting WEXT scan results. This is needed to allow
* the various methods of reporting IEs to be combined into a single IE buffer.
*/
struct wext_scan_data {
struct wpa_scan_res res;
u8 *ie;
size_t ie_len;
u8 ssid[32];
size_t ssid_len;
int maxrate;
};
static void wext_get_scan_mode(struct iw_event *iwe,
struct wext_scan_data *res)
{
if (iwe->u.mode == IW_MODE_ADHOC)
res->res.caps |= IEEE80211_CAP_IBSS;
else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
res->res.caps |= IEEE80211_CAP_ESS;
}
static void wext_get_scan_ssid(struct iw_event *iwe,
struct wext_scan_data *res, char *custom,
char *end)
{
int ssid_len = iwe->u.essid.length;
if (custom + ssid_len > end)
return;
if (iwe->u.essid.flags &&
ssid_len > 0 &&
ssid_len <= IW_ESSID_MAX_SIZE) {
os_memcpy(res->ssid, custom, ssid_len);
res->ssid_len = ssid_len;
}
}
static void wext_get_scan_freq(struct iw_event *iwe,
struct wext_scan_data *res)
{
int divi = 1000000, i;
if (iwe->u.freq.e == 0) {
/*
* Some drivers do not report frequency, but a channel.
* Try to map this to frequency by assuming they are using
* IEEE 802.11b/g. But don't overwrite a previously parsed
* frequency if the driver sends both frequency and channel,
* since the driver may be sending an A-band channel that we
* don't handle here.
*/
if (res->res.freq)
return;
if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
res->res.freq = 2407 + 5 * iwe->u.freq.m;
return;
} else if (iwe->u.freq.m == 14) {
res->res.freq = 2484;
return;
}
}
if (iwe->u.freq.e > 6) {
wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
MACSTR " m=%d e=%d)",
MAC2STR(res->res.bssid), iwe->u.freq.m,
iwe->u.freq.e);
return;
}
for (i = 0; i < iwe->u.freq.e; i++)
divi /= 10;
res->res.freq = iwe->u.freq.m / divi;
}
static void wext_get_scan_qual(struct iw_event *iwe,
struct wext_scan_data *res)
{
res->res.qual = iwe->u.qual.qual;
res->res.noise = iwe->u.qual.noise;
res->res.level = iwe->u.qual.level;
}
static void wext_get_scan_encode(struct iw_event *iwe,
struct wext_scan_data *res)
{
if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
res->res.caps |= IEEE80211_CAP_PRIVACY;
}
static void wext_get_scan_rate(struct iw_event *iwe,
struct wext_scan_data *res, char *pos,
char *end)
{
int maxrate;
char *custom = pos + IW_EV_LCP_LEN;
struct iw_param p;
size_t clen;
clen = iwe->len;
if (custom + clen > end)
return;
maxrate = 0;
while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
/* Note: may be misaligned, make a local, aligned copy */
os_memcpy(&p, custom, sizeof(struct iw_param));
if (p.value > maxrate)
maxrate = p.value;
clen -= sizeof(struct iw_param);
custom += sizeof(struct iw_param);
}
/* Convert the maxrate from WE-style (b/s units) to
* 802.11 rates (500000 b/s units).
*/
res->maxrate = maxrate / 500000;
}
static void wext_get_scan_iwevgenie(struct iw_event *iwe,
struct wext_scan_data *res, char *custom,
char *end)
{
char *genie, *gpos, *gend;
u8 *tmp;
gpos = genie = custom;
gend = genie + iwe->u.data.length;
if (gend > end) {
wpa_printf(MSG_INFO, "IWEVGENIE overflow");
return;
}
tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
if (tmp == NULL)
return;
os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
res->ie = tmp;
res->ie_len += gend - gpos;
}
static void wext_get_scan_custom(struct iw_event *iwe,
struct wext_scan_data *res, char *custom,
char *end)
{
size_t clen;
u8 *tmp;
clen = iwe->u.data.length;
if (custom + clen > end)
return;
if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
char *spos;
int bytes;
spos = custom + 7;
bytes = custom + clen - spos;
if (bytes & 1)
return;
bytes /= 2;
tmp = os_realloc(res->ie, res->ie_len + bytes);
if (tmp == NULL)
return;
hexstr2bin(spos, tmp + res->ie_len, bytes);
res->ie = tmp;
res->ie_len += bytes;
} else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
char *spos;
int bytes;
spos = custom + 7;
bytes = custom + clen - spos;
if (bytes & 1)
return;
bytes /= 2;
tmp = os_realloc(res->ie, res->ie_len + bytes);
if (tmp == NULL)
return;
hexstr2bin(spos, tmp + res->ie_len, bytes);
res->ie = tmp;
res->ie_len += bytes;
} else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
char *spos;
int bytes;
u8 bin[8];
spos = custom + 4;
bytes = custom + clen - spos;
if (bytes != 16) {
wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
return;
}
bytes /= 2;
hexstr2bin(spos, bin, bytes);
res->res.tsf += WPA_GET_BE64(bin);
}
}
static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd)
{
return drv->we_version_compiled > 18 &&
(cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
cmd == IWEVGENIE || cmd == IWEVCUSTOM);
}
static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
struct wext_scan_data *data)
{
struct wpa_scan_res **tmp;
struct wpa_scan_res *r;
size_t extra_len;
u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
/* Figure out whether we need to fake any IEs */
pos = data->ie;
end = pos + data->ie_len;
while (pos && pos + 1 < end) {
if (pos + 2 + pos[1] > end)
break;
if (pos[0] == WLAN_EID_SSID)
ssid_ie = pos;
else if (pos[0] == WLAN_EID_SUPP_RATES)
rate_ie = pos;
else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
rate_ie = pos;
pos += 2 + pos[1];
}
extra_len = 0;
if (ssid_ie == NULL)
extra_len += 2 + data->ssid_len;
if (rate_ie == NULL && data->maxrate)
extra_len += 3;
r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
if (r == NULL)
return;
os_memcpy(r, &data->res, sizeof(*r));
r->ie_len = extra_len + data->ie_len;
pos = (u8 *) (r + 1);
if (ssid_ie == NULL) {
/*
* Generate a fake SSID IE since the driver did not report
* a full IE list.
*/
*pos++ = WLAN_EID_SSID;
*pos++ = data->ssid_len;
os_memcpy(pos, data->ssid, data->ssid_len);
pos += data->ssid_len;
}
if (rate_ie == NULL && data->maxrate) {
/*
* Generate a fake Supported Rates IE since the driver did not
* report a full IE list.
*/
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = 1;
*pos++ = data->maxrate;
}
if (data->ie)
os_memcpy(pos, data->ie, data->ie_len);
tmp = os_realloc(res->res,
(res->num + 1) * sizeof(struct wpa_scan_res *));
if (tmp == NULL) {
os_free(r);
return;
}
tmp[res->num++] = r;
res->res = tmp;
}
/**
* wpa_driver_wext_get_scan_results - Fetch the latest scan results
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* Returns: Scan results on success, -1 on failure
*/
struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
{
struct wpa_driver_wext_data *drv = priv;
size_t ap_num = 0, len;
int first;
u8 *res_buf;
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom;
struct wpa_scan_results *res;
struct wext_scan_data data;
res_buf = wpa_driver_wext_giwscan(drv, &len);
if (res_buf == NULL)
return NULL;
ap_num = 0;
first = 1;
res = os_zalloc(sizeof(*res));
if (res == NULL) {
os_free(res_buf);
return NULL;
}
pos = (char *) res_buf;
end = (char *) res_buf + len;
os_memset(&data, 0, sizeof(data));
while (pos + IW_EV_LCP_LEN <= end) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
if (iwe->len <= IW_EV_LCP_LEN)
break;
custom = pos + IW_EV_POINT_LEN;
if (wext_19_iw_point(drv, iwe->cmd)) {
/* WE-19 removed the pointer from struct iw_point */
char *dpos = (char *) &iwe_buf.u.data.length;
int dlen = dpos - (char *) &iwe_buf;
os_memcpy(dpos, pos + IW_EV_LCP_LEN,
sizeof(struct iw_event) - dlen);
} else {
os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
custom += IW_EV_POINT_OFF;
}
switch (iwe->cmd) {
case SIOCGIWAP:
if (!first)
wpa_driver_wext_add_scan_entry(res, &data);
first = 0;
os_free(data.ie);
os_memset(&data, 0, sizeof(data));
os_memcpy(data.res.bssid,
iwe->u.ap_addr.sa_data, ETH_ALEN);
break;
case SIOCGIWMODE:
wext_get_scan_mode(iwe, &data);
break;
case SIOCGIWESSID:
wext_get_scan_ssid(iwe, &data, custom, end);
break;
case SIOCGIWFREQ:
wext_get_scan_freq(iwe, &data);
break;
case IWEVQUAL:
wext_get_scan_qual(iwe, &data);
break;
case SIOCGIWENCODE:
wext_get_scan_encode(iwe, &data);
break;
case SIOCGIWRATE:
wext_get_scan_rate(iwe, &data, pos, end);
break;
case IWEVGENIE:
wext_get_scan_iwevgenie(iwe, &data, custom, end);
break;
case IWEVCUSTOM:
wext_get_scan_custom(iwe, &data, custom, end);
break;
}
pos += iwe->len;
}
os_free(res_buf);
res_buf = NULL;
if (!first)
wpa_driver_wext_add_scan_entry(res, &data);
os_free(data.ie);
wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
(unsigned long) len, (unsigned long) res->num);
return res;
}
static int wpa_driver_wext_get_range(void *priv)
{
struct wpa_driver_wext_data *drv = priv;
struct iw_range *range;
struct iwreq iwr;
int minlen;
size_t buflen;
/*
* Use larger buffer than struct iw_range in order to allow the
* structure to grow in the future.
*/
buflen = sizeof(struct iw_range) + 500;
range = os_zalloc(buflen);
if (range == NULL)
return -1;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.data.pointer = (caddr_t) range;
iwr.u.data.length = buflen;
minlen = ((char *) &range->enc_capa) - (char *) range +
sizeof(range->enc_capa);
if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
perror("ioctl[SIOCGIWRANGE]");
os_free(range);
return -1;
} else if (iwr.u.data.length >= minlen &&
range->we_version_compiled >= 18) {
wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
"WE(source)=%d enc_capa=0x%x",
range->we_version_compiled,
range->we_version_source,
range->enc_capa);
drv->has_capability = 1;
drv->we_version_compiled = range->we_version_compiled;
if (range->enc_capa & IW_ENC_CAPA_WPA) {
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
}
if (range->enc_capa & IW_ENC_CAPA_WPA2) {
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
}
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
WPA_DRIVER_CAPA_ENC_WEP104;
if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x "
"flags 0x%x",
drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
} else {
wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
"assuming WPA is not supported");
}
os_free(range);
return 0;
}
static int wpa_driver_wext_set_wpa(void *priv, int enabled)
{
struct wpa_driver_wext_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
return wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED,
enabled);
}
static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
const u8 *psk)
{
struct iw_encode_ext *ext;
struct iwreq iwr;
int ret;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
return 0;
if (!psk)
return 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
ext = os_zalloc(sizeof(*ext) + PMK_LEN);
if (ext == NULL)
return -1;
iwr.u.encoding.pointer = (caddr_t) ext;
iwr.u.encoding.length = sizeof(*ext) + PMK_LEN;
ext->key_len = PMK_LEN;
os_memcpy(&ext->key, psk, ext->key_len);
ext->alg = IW_ENCODE_ALG_PMK;
ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
if (ret < 0)
perror("ioctl[SIOCSIWENCODEEXT] PMK");
os_free(ext);
return ret;
}
static int wpa_driver_wext_set_key_ext(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_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
struct iw_encode_ext *ext;
if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) {
wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu",
__FUNCTION__, (unsigned long) seq_len);
return -1;
}
ext = os_zalloc(sizeof(*ext) + key_len);
if (ext == NULL)
return -1;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.encoding.flags = key_idx + 1;
if (alg == WPA_ALG_NONE)
iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
iwr.u.encoding.pointer = (caddr_t) ext;
iwr.u.encoding.length = sizeof(*ext) + key_len;
if (addr == NULL ||
os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
if (set_tx)
ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
ext->addr.sa_family = ARPHRD_ETHER;
if (addr)
os_memcpy(ext->addr.sa_data, addr, ETH_ALEN);
else
os_memset(ext->addr.sa_data, 0xff, ETH_ALEN);
if (key && key_len) {
os_memcpy(ext + 1, key, key_len);
ext->key_len = key_len;
}
switch (alg) {
case WPA_ALG_NONE:
ext->alg = IW_ENCODE_ALG_NONE;
break;
case WPA_ALG_WEP:
ext->alg = IW_ENCODE_ALG_WEP;
break;
case WPA_ALG_TKIP:
ext->alg = IW_ENCODE_ALG_TKIP;
break;
case WPA_ALG_CCMP:
ext->alg = IW_ENCODE_ALG_CCMP;
break;
case WPA_ALG_PMK:
ext->alg = IW_ENCODE_ALG_PMK;
break;
#ifdef WEXT_MFP_PENDING
#ifdef CONFIG_IEEE80211W
case WPA_ALG_IGTK:
ext->alg = IW_ENCODE_ALG_AES_CMAC;
break;
#endif /* CONFIG_IEEE80211W */
#endif /* WEXT_MFP_PENDING */
default:
wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
__FUNCTION__, alg);
os_free(ext);
return -1;
}
if (seq && seq_len) {
ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID;
os_memcpy(ext->rx_seq, seq, seq_len);
}
if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) {
ret = errno == EOPNOTSUPP ? -2 : -1;
if (errno == ENODEV) {
/*
* ndiswrapper seems to be returning incorrect error
* code.. */
ret = -2;
}
perror("ioctl[SIOCSIWENCODEEXT]");
}
os_free(ext);
return ret;
}
/**
* wpa_driver_wext_set_key - Configure encryption key
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @priv: Private driver interface data
* @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
* %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
* @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for
* broadcast/default keys
* @key_idx: key index (0..3), usually 0 for unicast keys
* @set_tx: Configure this key as the default Tx key (only used when
* driver does not support separate unicast/individual key
* @seq: Sequence number/packet number, seq_len octets, the next
* packet number to be used for in replay protection; configured
* for Rx keys (in most cases, this is only used with broadcast
* keys and set to zero for unicast keys)
* @seq_len: Length of the seq, depends on the algorithm:
* TKIP: 6 octets, CCMP: 6 octets
* @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
* 8-byte Rx Mic Key
* @key_len: Length of the key buffer in octets (WEP: 5 or 13,
* TKIP: 32, CCMP: 16)
* Returns: 0 on success, -1 on failure
*
* This function uses SIOCSIWENCODEEXT by default, but tries to use
* SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key.
*/
int wpa_driver_wext_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)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
"key_len=%lu",
__FUNCTION__, alg, key_idx, set_tx,
(unsigned long) seq_len, (unsigned long) key_len);
ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
seq, seq_len, key, key_len);
if (ret == 0)
return 0;
if (ret == -2 &&
(alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) {
wpa_printf(MSG_DEBUG, "Driver did not support "
"SIOCSIWENCODEEXT, trying SIOCSIWENCODE");
ret = 0;
} else {
wpa_printf(MSG_DEBUG, "Driver did not support "
"SIOCSIWENCODEEXT");
return ret;
}
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.encoding.flags = key_idx + 1;
if (alg == WPA_ALG_NONE)
iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
iwr.u.encoding.pointer = (caddr_t) key;
iwr.u.encoding.length = key_len;
if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
perror("ioctl[SIOCSIWENCODE]");
ret = -1;
}
if (set_tx && alg != WPA_ALG_NONE) {
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.encoding.flags = key_idx + 1;
iwr.u.encoding.pointer = (caddr_t) NULL;
iwr.u.encoding.length = 0;
if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
perror("ioctl[SIOCSIWENCODE] (set_tx)");
ret = -1;
}
}
return ret;
}
static int wpa_driver_wext_set_countermeasures(void *priv,
int enabled)
{
struct wpa_driver_wext_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
return wpa_driver_wext_set_auth_param(drv,
IW_AUTH_TKIP_COUNTERMEASURES,
enabled);
}
static int wpa_driver_wext_set_drop_unencrypted(void *priv,
int enabled)
{
struct wpa_driver_wext_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
drv->use_crypt = enabled;
return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
enabled);
}
static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
const u8 *addr, int cmd, int reason_code)
{
struct iwreq iwr;
struct iw_mlme mlme;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
os_memset(&mlme, 0, sizeof(mlme));
mlme.cmd = cmd;
mlme.reason_code = reason_code;
mlme.addr.sa_family = ARPHRD_ETHER;
os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN);
iwr.u.data.pointer = (caddr_t) &mlme;
iwr.u.data.length = sizeof(mlme);
if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
perror("ioctl[SIOCSIWMLME]");
ret = -1;
}
return ret;
}
static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
int reason_code)
{
struct wpa_driver_wext_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
return wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
}
static int wpa_driver_wext_disassociate(void *priv, const u8 *addr,
int reason_code)
{
struct wpa_driver_wext_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
return wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC,
reason_code);
}
static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
size_t ie_len)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.data.pointer = (caddr_t) ie;
iwr.u.data.length = ie_len;
if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
perror("ioctl[SIOCSIWGENIE]");
ret = -1;
}
return ret;
}
int wpa_driver_wext_cipher2wext(int cipher)
{
switch (cipher) {
case CIPHER_NONE:
return IW_AUTH_CIPHER_NONE;
case CIPHER_WEP40:
return IW_AUTH_CIPHER_WEP40;
case CIPHER_TKIP:
return IW_AUTH_CIPHER_TKIP;
case CIPHER_CCMP:
return IW_AUTH_CIPHER_CCMP;
case CIPHER_WEP104:
return IW_AUTH_CIPHER_WEP104;
default:
return 0;
}
}
int wpa_driver_wext_keymgmt2wext(int keymgmt)
{
switch (keymgmt) {
case KEY_MGMT_802_1X:
case KEY_MGMT_802_1X_NO_WPA:
return IW_AUTH_KEY_MGMT_802_1X;
case KEY_MGMT_PSK:
return IW_AUTH_KEY_MGMT_PSK;
default:
return 0;
}
}
static int
wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
struct wpa_driver_associate_params *params)
{
struct iwreq iwr;
int ret = 0;
wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
"SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
/* Just changing mode, not actual keys */
iwr.u.encoding.flags = 0;
iwr.u.encoding.pointer = (caddr_t) NULL;
iwr.u.encoding.length = 0;
/*
* Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two
* different things. Here they are used to indicate Open System vs.
* Shared Key authentication algorithm. However, some drivers may use
* them to select between open/restricted WEP encrypted (open = allow
* both unencrypted and encrypted frames; restricted = only allow
* encrypted frames).
*/
if (!drv->use_crypt) {
iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
} else {
if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
iwr.u.encoding.flags |= IW_ENCODE_OPEN;
if (params->auth_alg & AUTH_ALG_SHARED_KEY)
iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
}
if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
perror("ioctl[SIOCSIWENCODE]");
ret = -1;
}
return ret;
}
int wpa_driver_wext_associate(void *priv,
struct wpa_driver_associate_params *params)
{
struct wpa_driver_wext_data *drv = priv;
int ret = 0;
int allow_unencrypted_eapol;
int value;
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
/*
* If the driver did not support SIOCSIWAUTH, fallback to
* SIOCSIWENCODE here.
*/
if (drv->auth_alg_fallback &&
wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
ret = -1;
if (!params->bssid &&
wpa_driver_wext_set_bssid(drv, NULL) < 0)
ret = -1;
if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
ret = -1;
/* TODO: should consider getting wpa version and cipher/key_mgmt suites
* from configuration, not from here, where only the selected suite is
* available */
if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
< 0)
ret = -1;
if (params->wpa_ie == NULL || params->wpa_ie_len == 0)
value = IW_AUTH_WPA_VERSION_DISABLED;
else if (params->wpa_ie[0] == WLAN_EID_RSN)
value = IW_AUTH_WPA_VERSION_WPA2;
else
value = IW_AUTH_WPA_VERSION_WPA;
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_WPA_VERSION, value) < 0)
ret = -1;
value = wpa_driver_wext_cipher2wext(params->pairwise_suite);
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_CIPHER_PAIRWISE, value) < 0)
ret = -1;
value = wpa_driver_wext_cipher2wext(params->group_suite);
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_CIPHER_GROUP, value) < 0)
ret = -1;
value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite);
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_KEY_MGMT, value) < 0)
ret = -1;
value = params->key_mgmt_suite != KEY_MGMT_NONE ||
params->pairwise_suite != CIPHER_NONE ||
params->group_suite != CIPHER_NONE ||
params->wpa_ie_len;
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_PRIVACY_INVOKED, value) < 0)
ret = -1;
/* Allow unencrypted EAPOL messages even if pairwise keys are set when
* not using WPA. IEEE 802.1X specifies that these frames are not
* encrypted, but WPA encrypts them when pairwise keys are in use. */
if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
params->key_mgmt_suite == KEY_MGMT_PSK)
allow_unencrypted_eapol = 0;
else
allow_unencrypted_eapol = 1;
if (wpa_driver_wext_set_psk(drv, params->psk) < 0)
ret = -1;
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_RX_UNENCRYPTED_EAPOL,
allow_unencrypted_eapol) < 0)
ret = -1;
#ifdef WEXT_MFP_PENDING
#ifdef CONFIG_IEEE80211W
switch (params->mgmt_frame_protection) {
case NO_MGMT_FRAME_PROTECTION:
value = IW_AUTH_MFP_DISABLED;
break;
case MGMT_FRAME_PROTECTION_OPTIONAL:
value = IW_AUTH_MFP_OPTIONAL;
break;
case MGMT_FRAME_PROTECTION_REQUIRED:
value = IW_AUTH_MFP_REQUIRED;
break;
};
if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
ret = -1;
#endif /* CONFIG_IEEE80211W */
#endif /* WEXT_MFP_PENDING */
if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0)
ret = -1;
if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
ret = -1;
if (params->bssid &&
wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
ret = -1;
return ret;
}
static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
{
struct wpa_driver_wext_data *drv = priv;
int algs = 0, res;
if (auth_alg & AUTH_ALG_OPEN_SYSTEM)
algs |= IW_AUTH_ALG_OPEN_SYSTEM;
if (auth_alg & AUTH_ALG_SHARED_KEY)
algs |= IW_AUTH_ALG_SHARED_KEY;
if (auth_alg & AUTH_ALG_LEAP)
algs |= IW_AUTH_ALG_LEAP;
if (algs == 0) {
/* at least one algorithm should be set */
algs = IW_AUTH_ALG_OPEN_SYSTEM;
}
res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
algs);
drv->auth_alg_fallback = res == -2;
return res;
}
/**
* wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_set_mode(void *priv, int mode)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret = -1, flags;
unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.mode = new_mode;
if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
ret = 0;
goto done;
}
if (errno != EBUSY) {
perror("ioctl[SIOCSIWMODE]");
goto done;
}
/* mac80211 doesn't allow mode changes while the device is up, so if
* the device isn't in the mode we're about to change to, take device
* down, try to set the mode again, and bring it back up.
*/
if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
perror("ioctl[SIOCGIWMODE]");
goto done;
}
if (iwr.u.mode == new_mode) {
ret = 0;
goto done;
}
if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) {
(void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP);
/* Try to set the mode again while the interface is down */
iwr.u.mode = new_mode;
if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
perror("ioctl[SIOCSIWMODE]");
else
ret = 0;
/* Ignore return value of get_ifflags to ensure that the device
* is always up like it was before this function was called.
*/
(void) wpa_driver_wext_get_ifflags(drv, &flags);
(void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP);
}
done:
return ret;
}
static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
u32 cmd, const u8 *bssid, const u8 *pmkid)
{
struct iwreq iwr;
struct iw_pmksa pmksa;
int ret = 0;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
os_memset(&pmksa, 0, sizeof(pmksa));
pmksa.cmd = cmd;
pmksa.bssid.sa_family = ARPHRD_ETHER;
if (bssid)
os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN);
if (pmkid)
os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN);
iwr.u.data.pointer = (caddr_t) &pmksa;
iwr.u.data.length = sizeof(pmksa);
if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
if (errno != EOPNOTSUPP)
perror("ioctl[SIOCSIWPMKSA]");
ret = -1;
}
return ret;
}
static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
const u8 *pmkid)
{
struct wpa_driver_wext_data *drv = priv;
return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
}
static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
const u8 *pmkid)
{
struct wpa_driver_wext_data *drv = priv;
return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
}
static int wpa_driver_wext_flush_pmkid(void *priv)
{
struct wpa_driver_wext_data *drv = priv;
return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
}
int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
{
struct wpa_driver_wext_data *drv = priv;
if (!drv->has_capability)
return -1;
os_memcpy(capa, &drv->capa, sizeof(*capa));
return 0;
}
int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
const char *ifname)
{
if (ifname == NULL) {
drv->ifindex2 = -1;
return 0;
}
drv->ifindex2 = if_nametoindex(ifname);
if (drv->ifindex2 <= 0)
return -1;
wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
"wireless events", drv->ifindex2, ifname);
return 0;
}
int wpa_driver_wext_set_operstate(void *priv, int state)
{
struct wpa_driver_wext_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
__func__, drv->operstate, state, state ? "UP" : "DORMANT");
drv->operstate = state;
return wpa_driver_wext_send_oper_ifla(
drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT);
}
#ifdef CONFIG_CLIENT_MLME
static int hostapd_ioctl(struct wpa_driver_wext_data *drv,
struct prism2_hostapd_param *param, int len)
{
struct iwreq iwr;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
iwr.u.data.pointer = (caddr_t) param;
iwr.u.data.length = len;
if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
return -1;
}
return 0;
}
static struct wpa_hw_modes *
wpa_driver_wext_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
{
struct wpa_driver_wext_data *drv = priv;
struct prism2_hostapd_param *param;
u8 *pos, *end;
struct wpa_hw_modes *modes = NULL;
int i;
param = os_zalloc(PRISM2_HOSTAPD_MAX_BUF_SIZE);
if (param == NULL)
return NULL;
param->cmd = PRISM2_HOSTAPD_GET_HW_FEATURES;
if (hostapd_ioctl(drv, param, PRISM2_HOSTAPD_MAX_BUF_SIZE) < 0) {
perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
goto out;
}
*num_modes = param->u.hw_features.num_modes;
*flags = param->u.hw_features.flags;
pos = param->u.hw_features.data;
end = pos + PRISM2_HOSTAPD_MAX_BUF_SIZE -
(param->u.hw_features.data - (u8 *) param);
modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes));
if (modes == NULL)
goto out;
for (i = 0; i < *num_modes; i++) {
struct hostapd_ioctl_hw_modes_hdr *hdr;
struct wpa_hw_modes *feature;
int clen, rlen;
hdr = (struct hostapd_ioctl_hw_modes_hdr *) pos;
pos = (u8 *) (hdr + 1);
clen = hdr->num_channels * sizeof(struct wpa_channel_data);
rlen = hdr->num_rates * sizeof(struct wpa_rate_data);
feature = &modes[i];
switch (hdr->mode) {
case MODE_IEEE80211A:
feature->mode = WPA_MODE_IEEE80211A;
break;
case MODE_IEEE80211B:
feature->mode = WPA_MODE_IEEE80211B;
break;
case MODE_IEEE80211G:
feature->mode = WPA_MODE_IEEE80211G;
break;
case MODE_ATHEROS_TURBO:
case MODE_ATHEROS_TURBOG:
wpa_printf(MSG_ERROR, "Skip unsupported hw_mode=%d in "
"get_hw_features data", hdr->mode);
pos += clen + rlen;
continue;
default:
wpa_printf(MSG_ERROR, "Unknown hw_mode=%d in "
"get_hw_features data", hdr->mode);
wpa_supplicant_sta_free_hw_features(modes, *num_modes);
modes = NULL;
break;
}
feature->num_channels = hdr->num_channels;
feature->num_rates = hdr->num_rates;
feature->channels = os_malloc(clen);
feature->rates = os_malloc(rlen);
if (!feature->channels || !feature->rates ||
pos + clen + rlen > end) {
wpa_supplicant_sta_free_hw_features(modes, *num_modes);
modes = NULL;
break;
}
os_memcpy(feature->channels, pos, clen);
pos += clen;
os_memcpy(feature->rates, pos, rlen);
pos += rlen;
}
out:
os_free(param);
return modes;
}
int wpa_driver_wext_set_channel(void *priv, wpa_hw_mode phymode, int chan,
int freq)
{
return wpa_driver_wext_set_freq(priv, freq);
}
static void wpa_driver_wext_mlme_read(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_driver_wext_data *drv = eloop_ctx;
int len;
unsigned char buf[3000];
struct ieee80211_frame_info *fi;
struct ieee80211_rx_status rx_status;
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0) {
perror("recv[MLME]");
return;
}
if (len < (int) sizeof(struct ieee80211_frame_info)) {
wpa_printf(MSG_DEBUG, "WEXT: Too short MLME frame (len=%d)",
len);
return;
}
fi = (struct ieee80211_frame_info *) buf;
if (ntohl(fi->version) != IEEE80211_FI_VERSION) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid MLME frame info version "
"0x%x", ntohl(fi->version));
return;
}
os_memset(&rx_status, 0, sizeof(rx_status));
rx_status.ssi = ntohl(fi->ssi_signal);
rx_status.channel = ntohl(fi->channel);
wpa_supplicant_sta_rx(drv->ctx,
buf + sizeof(struct ieee80211_frame_info),
len - sizeof(struct ieee80211_frame_info),
&rx_status);
}
static int wpa_driver_wext_open_mlme(struct wpa_driver_wext_data *drv)
{
int flags, ifindex, s;
struct sockaddr_ll addr;
struct ifreq ifr;
if (wpa_driver_prism2_param_set(drv, PRISM2_PARAM_USER_SPACE_MLME, 1) <
0) {
wpa_printf(MSG_ERROR, "WEXT: Failed to configure driver to "
"use user space MLME");
return -1;
}
if (wpa_driver_prism2_param_set(drv, PRISM2_PARAM_MGMT_IF, 1) < 0) {
wpa_printf(MSG_ERROR, "WEXT: Failed to add management "
"interface for user space MLME");
return -1;
}
ifindex = wpa_driver_prism2_param_get(drv, PRISM2_PARAM_MGMT_IF);
if (ifindex <= 0) {
wpa_printf(MSG_ERROR, "WEXT: MLME management device not "
"found");
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = ifindex;
if (ioctl(drv->ioctl_sock, SIOCGIFNAME, &ifr) != 0) {
perror("ioctl(SIOCGIFNAME)");
return -1;
}
os_strlcpy(drv->mlmedev, ifr.ifr_name, sizeof(drv->mlmedev));
wpa_printf(MSG_DEBUG, "WEXT: MLME management device '%s'",
drv->mlmedev);
if (wpa_driver_wext_get_ifflags_ifname(drv, drv->mlmedev, &flags) != 0
|| wpa_driver_wext_set_ifflags_ifname(drv, drv->mlmedev,
flags | IFF_UP) != 0) {
wpa_printf(MSG_ERROR, "WEXT: Could not set interface "
"'%s' UP", drv->mlmedev);
return -1;
}
s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0) {
perror("socket[PF_PACKET,SOCK_RAW]");
return -1;
}
os_memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifindex;
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind(MLME)");
return -1;
}
if (eloop_register_read_sock(s, wpa_driver_wext_mlme_read, drv, NULL))
{
wpa_printf(MSG_ERROR, "WEXT: Could not register MLME read "
"socket");
close(s);
return -1;
}
return s;
}
static int wpa_driver_wext_send_mlme(void *priv, const u8 *data,
size_t data_len)
{
struct wpa_driver_wext_data *drv = priv;
int ret;
ret = send(drv->mlme_sock, data, data_len, 0);
if (ret < 0) {
perror("send[MLME]");
return -1;
}
return 0;
}
static int wpa_driver_wext_mlme_add_sta(void *priv, const u8 *addr,
const u8 *supp_rates,
size_t supp_rates_len)
{
struct wpa_driver_wext_data *drv = priv;
struct prism2_hostapd_param param;
size_t len;
os_memset(&param, 0, sizeof(param));
param.cmd = PRISM2_HOSTAPD_ADD_STA;
os_memcpy(param.sta_addr, addr, ETH_ALEN);
len = supp_rates_len;
if (len > sizeof(param.u.add_sta.supp_rates))
len = sizeof(param.u.add_sta.supp_rates);
os_memcpy(param.u.add_sta.supp_rates, supp_rates, len);
return hostapd_ioctl(drv, &param, sizeof(param));
}
static int wpa_driver_wext_mlme_remove_sta(void *priv, const u8 *addr)
{
struct wpa_driver_wext_data *drv = priv;
struct prism2_hostapd_param param;
os_memset(&param, 0, sizeof(param));
param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
os_memcpy(param.sta_addr, addr, ETH_ALEN);
return hostapd_ioctl(drv, &param, sizeof(param));
}
#endif /* CONFIG_CLIENT_MLME */
static int wpa_driver_wext_set_param(void *priv, const char *param)
{
#ifdef CONFIG_CLIENT_MLME
struct wpa_driver_wext_data *drv = priv;
if (param == NULL)
return 0;
wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
if (os_strstr(param, "use_mlme=1")) {
wpa_printf(MSG_DEBUG, "WEXT: Using user space MLME");
drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
drv->mlme_sock = wpa_driver_wext_open_mlme(drv);
if (drv->mlme_sock < 0)
return -1;
}
#endif /* CONFIG_CLIENT_MLME */
return 0;
}
int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
{
return drv->we_version_compiled;
}
const struct wpa_driver_ops wpa_driver_wext_ops = {
.name = "wext",
.desc = "Linux wireless extensions (generic)",
.get_bssid = wpa_driver_wext_get_bssid,
.get_ssid = wpa_driver_wext_get_ssid,
.set_wpa = wpa_driver_wext_set_wpa,
.set_key = wpa_driver_wext_set_key,
.set_countermeasures = wpa_driver_wext_set_countermeasures,
.set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted,
.scan = wpa_driver_wext_scan,
.get_scan_results2 = wpa_driver_wext_get_scan_results,
.deauthenticate = wpa_driver_wext_deauthenticate,
.disassociate = wpa_driver_wext_disassociate,
.associate = wpa_driver_wext_associate,
.set_auth_alg = wpa_driver_wext_set_auth_alg,
.init = wpa_driver_wext_init,
.deinit = wpa_driver_wext_deinit,
.set_param = wpa_driver_wext_set_param,
.add_pmkid = wpa_driver_wext_add_pmkid,
.remove_pmkid = wpa_driver_wext_remove_pmkid,
.flush_pmkid = wpa_driver_wext_flush_pmkid,
.get_capa = wpa_driver_wext_get_capa,
.set_operstate = wpa_driver_wext_set_operstate,
#ifdef CONFIG_CLIENT_MLME
.get_hw_feature_data = wpa_driver_wext_get_hw_feature_data,
.set_channel = wpa_driver_wext_set_channel,
.set_ssid = wpa_driver_wext_set_ssid,
.set_bssid = wpa_driver_wext_set_bssid,
.send_mlme = wpa_driver_wext_send_mlme,
.mlme_add_sta = wpa_driver_wext_mlme_add_sta,
.mlme_remove_sta = wpa_driver_wext_mlme_remove_sta,
#endif /* CONFIG_CLIENT_MLME */
};