hostap/src/drivers/driver_bsd.c
Roy Marples a579642bc3 BSD: If route socket overflows, sync drivers to system interfaces
Messages such as RTM_IFNFO or RTM_IFANNOUNCE could have been lost.
As such, sync the state of our internal driver to the state of the
system interfaces as reports by getifaddrs(2).

This change requires the routing socket be placed in non-blocking
mode. While here, set the routing and inet sockets to close on exec.

BSDs that support SO_RERROR include NetBSD and DragonFly.
There is a review underway to add this to FreeBSD.

Signed-off-by: Roy Marples <roy@marples.name>
2021-02-06 13:27:24 +02:00

1767 lines
46 KiB
C

/*
* WPA Supplicant - driver interaction with BSD net80211 layer
* Copyright (c) 2004, Sam Leffler <sam@errno.com>
* Copyright (c) 2004, 2Wire, Inc
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <sys/ioctl.h>
#include "common.h"
#include "driver.h"
#include "eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
#include <ifaddrs.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#ifdef __NetBSD__
#include <net/if_ether.h>
#else
#include <net/ethernet.h>
#endif
#include <net/route.h>
#ifdef __DragonFly__
#include <netproto/802_11/ieee80211_ioctl.h>
#include <netproto/802_11/ieee80211_dragonfly.h>
#else /* __DragonFly__ */
#ifdef __GLIBC__
#include <netinet/ether.h>
#endif /* __GLIBC__ */
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
#include <net80211/ieee80211_crypto.h>
#endif /* __DragonFly__ || __GLIBC__ */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <net80211/ieee80211_freebsd.h>
#endif
#if __NetBSD__
#include <net80211/ieee80211_netbsd.h>
#endif
#include "l2_packet/l2_packet.h"
struct bsd_driver_global {
void *ctx;
int sock; /* socket for 802.11 ioctls */
int route; /* routing socket for events */
struct dl_list ifaces; /* list of interfaces */
};
struct bsd_driver_data {
struct dl_list list;
struct bsd_driver_global *global;
void *ctx;
struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
char ifname[IFNAMSIZ+1]; /* interface name */
int flags;
unsigned int ifindex; /* interface index */
int if_removed; /* has the interface been removed? */
struct wpa_driver_capa capa; /* driver capability */
int is_ap; /* Access point mode */
int prev_roaming; /* roaming state to restore on deinit */
int prev_privacy; /* privacy state to restore on deinit */
int prev_wpa; /* wpa state to restore on deinit */
enum ieee80211_opmode opmode; /* operation mode */
};
/* Generic functions for hostapd and wpa_supplicant */
static struct bsd_driver_data *
bsd_get_drvindex(void *priv, unsigned int ifindex)
{
struct bsd_driver_global *global = priv;
struct bsd_driver_data *drv;
dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
if (drv->ifindex == ifindex)
return drv;
}
return NULL;
}
static struct bsd_driver_data *
bsd_get_drvname(void *priv, const char *ifname)
{
struct bsd_driver_global *global = priv;
struct bsd_driver_data *drv;
dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
if (os_strcmp(drv->ifname, ifname) == 0)
return drv;
}
return NULL;
}
static int
bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
{
struct bsd_driver_data *drv = priv;
struct ieee80211req ireq;
if (drv->ifindex == 0 || drv->if_removed)
return -1;
os_memset(&ireq, 0, sizeof(ireq));
os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
ireq.i_type = op;
ireq.i_val = val;
ireq.i_data = (void *) arg;
ireq.i_len = arg_len;
if (ioctl(drv->global->sock, SIOCS80211, &ireq) < 0) {
wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
"arg_len=%u]: %s", op, val, arg_len,
strerror(errno));
return -1;
}
return 0;
}
static int
bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
int arg_len)
{
struct bsd_driver_data *drv = priv;
os_memset(ireq, 0, sizeof(*ireq));
os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
ireq->i_type = op;
ireq->i_len = arg_len;
ireq->i_data = arg;
if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
int level = drv->if_removed ? MSG_DEBUG : MSG_ERROR;
wpa_printf(level, "ioctl[SIOCG80211, op=%u, "
"arg_len=%u]: %s", op, arg_len, strerror(errno));
return -1;
}
return 0;
}
static int
get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
{
struct ieee80211req ireq;
if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
return -1;
return ireq.i_len;
}
static int
set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
{
return bsd_set80211(drv, op, 0, arg, arg_len);
}
static int
set80211param(struct bsd_driver_data *drv, int op, int arg)
{
return bsd_set80211(drv, op, arg, NULL, 0);
}
static int
bsd_get_ssid(void *priv, u8 *ssid, int len)
{
struct bsd_driver_data *drv = priv;
#ifdef SIOCG80211NWID
struct ieee80211_nwid nwid;
struct ifreq ifr;
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *)&nwid;
if (ioctl(drv->global->sock, SIOCG80211NWID, &ifr) < 0 ||
nwid.i_len > IEEE80211_NWID_LEN)
return -1;
os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
return nwid.i_len;
#else
return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
#endif
}
static int
bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
{
struct bsd_driver_data *drv = priv;
#ifdef SIOCS80211NWID
struct ieee80211_nwid nwid;
struct ifreq ifr;
os_memcpy(nwid.i_nwid, ssid, ssid_len);
nwid.i_len = ssid_len;
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *)&nwid;
return ioctl(drv->global->sock, SIOCS80211NWID, &ifr);
#else
return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
#endif
}
static int
bsd_get_if_media(void *priv)
{
struct bsd_driver_data *drv = priv;
struct ifmediareq ifmr;
os_memset(&ifmr, 0, sizeof(ifmr));
os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
if (ioctl(drv->global->sock, SIOCGIFMEDIA, &ifmr) < 0) {
wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
strerror(errno));
return -1;
}
return ifmr.ifm_current;
}
static int
bsd_set_if_media(void *priv, int media)
{
struct bsd_driver_data *drv = priv;
struct ifreq ifr;
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
ifr.ifr_media = media;
if (ioctl(drv->global->sock, SIOCSIFMEDIA, &ifr) < 0) {
wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
strerror(errno));
return -1;
}
return 0;
}
static int
bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
{
int media = bsd_get_if_media(priv);
if (media < 0)
return -1;
media &= ~mask;
media |= mode;
if (bsd_set_if_media(priv, media) < 0)
return -1;
return 0;
}
static int
bsd_del_key(void *priv, const u8 *addr, int key_idx)
{
struct ieee80211req_del_key wk;
os_memset(&wk, 0, sizeof(wk));
if (addr == NULL) {
wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
wk.idk_keyix = key_idx;
} else {
wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
MAC2STR(addr));
os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
}
return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
}
static int
bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
{
struct ieee80211req_mlme mlme;
os_memset(&mlme, 0, sizeof(mlme));
mlme.im_op = op;
mlme.im_reason = reason;
os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
}
static int
bsd_get_iface_flags(struct bsd_driver_data *drv)
{
struct ifreq ifr;
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) {
wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
strerror(errno));
return -1;
}
drv->flags = ifr.ifr_flags;
return 0;
}
static int
bsd_set_key(void *priv, struct wpa_driver_set_key_params *params)
{
struct ieee80211req_key wk;
#ifdef IEEE80211_KEY_NOREPLAY
struct bsd_driver_data *drv = priv;
#endif /* IEEE80211_KEY_NOREPLAY */
enum wpa_alg alg = params->alg;
const u8 *addr = params->addr;
int key_idx = params->key_idx;
int set_tx = params->set_tx;
const u8 *seq = params->seq;
size_t seq_len = params->seq_len;
const u8 *key = params->key;
size_t key_len = params->key_len;
wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
"seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
set_tx, seq_len, key_len);
if (alg == WPA_ALG_NONE) {
#ifndef HOSTAPD
if (addr == NULL || is_broadcast_ether_addr(addr))
return bsd_del_key(priv, NULL, key_idx);
else
#endif /* HOSTAPD */
return bsd_del_key(priv, addr, key_idx);
}
os_memset(&wk, 0, sizeof(wk));
switch (alg) {
case WPA_ALG_WEP:
wk.ik_type = IEEE80211_CIPHER_WEP;
break;
case WPA_ALG_TKIP:
wk.ik_type = IEEE80211_CIPHER_TKIP;
break;
case WPA_ALG_CCMP:
wk.ik_type = IEEE80211_CIPHER_AES_CCM;
break;
default:
wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
return -1;
}
wk.ik_flags = IEEE80211_KEY_RECV;
if (set_tx)
wk.ik_flags |= IEEE80211_KEY_XMIT;
if (addr == NULL) {
os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
wk.ik_keyix = key_idx;
} else {
os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
/*
* Deduce whether group/global or unicast key by checking
* the address (yech). Note also that we can only mark global
* keys default; doing this for a unicast key is an error.
*/
if (is_broadcast_ether_addr(addr)) {
wk.ik_flags |= IEEE80211_KEY_GROUP;
wk.ik_keyix = key_idx;
} else {
wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
key_idx;
}
}
if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
wk.ik_flags |= IEEE80211_KEY_DEFAULT;
#ifndef HOSTAPD
#ifdef IEEE80211_KEY_NOREPLAY
/*
* Ignore replay failures in IBSS and AHDEMO mode.
*/
if (drv->opmode == IEEE80211_M_IBSS ||
drv->opmode == IEEE80211_M_AHDEMO)
wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
#endif /* IEEE80211_KEY_NOREPLAY */
#endif /* HOSTAPD */
wk.ik_keylen = key_len;
if (seq) {
#ifdef WORDS_BIGENDIAN
/*
* wk.ik_keyrsc is in host byte order (big endian), need to
* swap it to match with the byte order used in WPA.
*/
int i;
u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
for (i = 0; i < seq_len; i++)
keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
#else /* WORDS_BIGENDIAN */
os_memcpy(&wk.ik_keyrsc, seq, seq_len);
#endif /* WORDS_BIGENDIAN */
}
os_memcpy(wk.ik_keydata, key, key_len);
return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
}
static int
bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
{
#ifndef IEEE80211_IOC_APPIE
static const char *ciphernames[] =
{ "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
int v;
switch (params->wpa_group) {
case WPA_CIPHER_CCMP:
v = IEEE80211_CIPHER_AES_CCM;
break;
case WPA_CIPHER_TKIP:
v = IEEE80211_CIPHER_TKIP;
break;
case WPA_CIPHER_WEP104:
v = IEEE80211_CIPHER_WEP;
break;
case WPA_CIPHER_WEP40:
v = IEEE80211_CIPHER_WEP;
break;
case WPA_CIPHER_NONE:
v = IEEE80211_CIPHER_NONE;
break;
default:
wpa_printf(MSG_INFO, "Unknown group key cipher %u",
params->wpa_group);
return -1;
}
wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
__func__, ciphernames[v], v);
if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
wpa_printf(MSG_INFO,
"Unable to set group key cipher to %u (%s)",
v, ciphernames[v]);
return -1;
}
if (v == IEEE80211_CIPHER_WEP) {
/* key length is done only for specific ciphers */
v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
wpa_printf(MSG_INFO,
"Unable to set group key length to %u", v);
return -1;
}
}
v = 0;
if (params->wpa_pairwise & WPA_CIPHER_CCMP)
v |= 1<<IEEE80211_CIPHER_AES_CCM;
if (params->wpa_pairwise & WPA_CIPHER_TKIP)
v |= 1<<IEEE80211_CIPHER_TKIP;
if (params->wpa_pairwise & WPA_CIPHER_NONE)
v |= 1<<IEEE80211_CIPHER_NONE;
wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
wpa_printf(MSG_INFO,
"Unable to set pairwise key ciphers to 0x%x", v);
return -1;
}
wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
__func__, params->wpa_key_mgmt);
if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
params->wpa_key_mgmt)) {
wpa_printf(MSG_INFO,
"Unable to set key management algorithms to 0x%x",
params->wpa_key_mgmt);
return -1;
}
v = 0;
if (params->rsn_preauth)
v |= BIT(0);
wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
__func__, params->rsn_preauth);
if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
v);
return -1;
}
#endif /* IEEE80211_IOC_APPIE */
wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
return -1;
}
return 0;
}
static int
bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
{
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
if (!params->enabled) {
/* XXX restore state */
return set80211param(priv, IEEE80211_IOC_AUTHMODE,
IEEE80211_AUTH_AUTO);
}
if (!params->wpa && !params->ieee802_1x) {
wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
__func__);
return -1;
}
if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
__func__);
return -1;
}
if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
__func__);
return -1;
}
return 0;
}
static void
bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
{
struct ieee80211req_wpaie ie;
int ielen = 0;
u8 *iebuf = NULL;
/*
* Fetch and validate any negotiated WPA/RSN parameters.
*/
memset(&ie, 0, sizeof(ie));
memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
wpa_printf(MSG_INFO,
"Failed to get WPA/RSN information element");
goto no_ie;
}
iebuf = ie.wpa_ie;
ielen = ie.wpa_ie[1];
if (ielen == 0)
iebuf = NULL;
else
ielen += 2;
no_ie:
drv_event_assoc(ctx, addr, iebuf, ielen, 0);
}
static int
bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
int encrypt, const u8 *own_addr, u32 flags)
{
struct bsd_driver_data *drv = priv;
wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
data_len);
}
static int
bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
{
struct bsd_driver_data *drv = priv;
#ifdef SIOCS80211CHANNEL
struct ieee80211chanreq creq;
#endif /* SIOCS80211CHANNEL */
u32 mode;
int channel = freq->channel;
if (channel < 14) {
mode =
freq->ht_enabled ? IFM_IEEE80211_11NG :
IFM_IEEE80211_11G;
} else if (channel == 14) {
mode = IFM_IEEE80211_11B;
} else {
mode =
freq->ht_enabled ? IFM_IEEE80211_11NA :
IFM_IEEE80211_11A;
}
if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
__func__);
return -1;
}
#ifdef SIOCS80211CHANNEL
os_memset(&creq, 0, sizeof(creq));
os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
creq.i_channel = (u_int16_t)channel;
return ioctl(drv->global->sock, SIOCS80211CHANNEL, &creq);
#else /* SIOCS80211CHANNEL */
return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
#endif /* SIOCS80211CHANNEL */
}
static int
bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
{
#ifdef IEEE80211_IOC_APPIE
wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
(unsigned long)ie_len);
return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
ie, ie_len);
#endif /* IEEE80211_IOC_APPIE */
return 0;
}
#ifdef SO_RERROR
static void
bsd_route_overflow(int sock, void *ctx, struct bsd_driver_global *global)
{
char event_buf[2048]; /* max size of a single route(4) msg */
int n;
struct ifaddrs *ifaddrs, *ifa;
struct bsd_driver_data *drv;
struct sockaddr_dl *sdl;
union wpa_event_data event;
/* We need to match the system state, so drain the route
* socket to avoid stale messages. */
do {
n = read(sock, event_buf, sizeof(event_buf));
} while (n != -1 || errno == ENOBUFS);
if (getifaddrs(&ifaddrs) == -1) {
wpa_printf(MSG_ERROR, "%s getifaddrs() failed: %s",
__func__, strerror(errno));
return;
}
/* add or update existing interfaces */
for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL ||
ifa->ifa_addr->sa_family != AF_LINK)
continue;
sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
drv = bsd_get_drvname(global, ifa->ifa_name);
if (drv != NULL &&
(drv->ifindex != sdl->sdl_index || drv->if_removed)) {
wpa_printf(MSG_DEBUG,
"RTM_IFANNOUNCE: Interface '%s' added",
drv->ifname);
drv->ifindex = sdl->sdl_index;
drv->if_removed = 0;
event.interface_status.ievent = EVENT_INTERFACE_ADDED;
os_strlcpy(event.interface_status.ifname, ifa->ifa_name,
sizeof(event.interface_status.ifname));
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
&event);
}
if (!drv &&
(drv = bsd_get_drvindex(global, sdl->sdl_index)) != NULL) {
/* Driver name is invalid */
wpa_printf(MSG_DEBUG,
"RTM_IFANNOUNCE: Interface '%s' removed",
drv->ifname);
drv->if_removed = 1;
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
os_strlcpy(event.interface_status.ifname, drv->ifname,
sizeof(event.interface_status.ifname));
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
&event);
}
}
/* punt missing interfaces and update flags */
dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL ||
ifa->ifa_addr->sa_family != AF_LINK)
continue;
sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
if (os_strcmp(drv->ifname, ifa->ifa_name) == 0)
break;
}
if (ifa == NULL && !drv->if_removed) {
wpa_printf(MSG_DEBUG,
"RTM_IFANNOUNCE: Interface '%s' removed",
drv->ifname);
drv->if_removed = 1;
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
os_strlcpy(event.interface_status.ifname, drv->ifname,
sizeof(event.interface_status.ifname));
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
&event);
}
if (!ifa)
continue;
if ((ifa->ifa_flags & IFF_UP) == 0 &&
(drv->flags & IFF_UP) != 0) {
wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
drv->ifname);
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
NULL);
} else if ((ifa->ifa_flags & IFF_UP) != 0 &&
(drv->flags & IFF_UP) == 0) {
wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
drv->ifname);
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
NULL);
}
drv->flags = ifa->ifa_flags;
}
freeifaddrs(ifaddrs);
}
#endif /* SO_RERROR */
static void
bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
{
char event_buf[2048]; /* max size of a single route(4) msg */
struct bsd_driver_global *global = sock_ctx;
struct bsd_driver_data *drv;
struct if_announcemsghdr *ifan;
struct if_msghdr *ifm;
struct rt_msghdr *rtm;
union wpa_event_data event;
struct ieee80211_michael_event *mic;
struct ieee80211_leave_event *leave;
struct ieee80211_join_event *join;
int n;
n = read(sock, event_buf, sizeof(event_buf));
if (n < 0) {
if (errno != EINTR && errno != EAGAIN)
wpa_printf(MSG_ERROR, "%s read() failed: %s",
__func__, strerror(errno));
#ifdef SO_RERROR
if (errno == ENOBUFS)
bsd_route_overflow(sock, ctx, sock_ctx);
#endif /* SO_RERROR */
return;
}
rtm = (struct rt_msghdr *) event_buf;
if (rtm->rtm_version != RTM_VERSION) {
wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
rtm->rtm_version);
return;
}
os_memset(&event, 0, sizeof(event));
switch (rtm->rtm_type) {
case RTM_IEEE80211:
ifan = (struct if_announcemsghdr *) rtm;
drv = bsd_get_drvindex(global, ifan->ifan_index);
if (drv == NULL)
return;
switch (ifan->ifan_what) {
case RTM_IEEE80211_ASSOC:
case RTM_IEEE80211_REASSOC:
if (drv->is_ap)
break;
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
break;
case RTM_IEEE80211_DISASSOC:
if (drv->is_ap)
break;
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
break;
case RTM_IEEE80211_SCAN:
if (drv->is_ap)
break;
wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
NULL);
break;
case RTM_IEEE80211_LEAVE:
leave = (struct ieee80211_leave_event *) &ifan[1];
drv_event_disassoc(drv->ctx, leave->iev_addr);
break;
case RTM_IEEE80211_JOIN:
#ifdef RTM_IEEE80211_REJOIN
case RTM_IEEE80211_REJOIN:
#endif
join = (struct ieee80211_join_event *) &ifan[1];
bsd_new_sta(drv, drv->ctx, join->iev_addr);
break;
case RTM_IEEE80211_REPLAY:
/* ignore */
break;
case RTM_IEEE80211_MICHAEL:
mic = (struct ieee80211_michael_event *) &ifan[1];
wpa_printf(MSG_DEBUG,
"Michael MIC failure wireless event: "
"keyix=%u src_addr=" MACSTR, mic->iev_keyix,
MAC2STR(mic->iev_src));
os_memset(&event, 0, sizeof(event));
event.michael_mic_failure.unicast =
!IEEE80211_IS_MULTICAST(mic->iev_dst);
event.michael_mic_failure.src = mic->iev_src;
wpa_supplicant_event(drv->ctx,
EVENT_MICHAEL_MIC_FAILURE, &event);
break;
}
break;
case RTM_IFANNOUNCE:
ifan = (struct if_announcemsghdr *) rtm;
switch (ifan->ifan_what) {
case IFAN_DEPARTURE:
drv = bsd_get_drvindex(global, ifan->ifan_index);
if (drv)
drv->if_removed = 1;
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
break;
case IFAN_ARRIVAL:
drv = bsd_get_drvname(global, ifan->ifan_name);
if (drv) {
drv->ifindex = ifan->ifan_index;
drv->if_removed = 0;
}
event.interface_status.ievent = EVENT_INTERFACE_ADDED;
break;
default:
wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action");
return;
}
wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
ifan->ifan_name,
ifan->ifan_what == IFAN_DEPARTURE ?
"removed" : "added");
os_strlcpy(event.interface_status.ifname, ifan->ifan_name,
sizeof(event.interface_status.ifname));
if (drv) {
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
&event);
/*
* Set ifindex to zero after sending the event as the
* event might query the driver to ensure a match.
*/
if (ifan->ifan_what == IFAN_DEPARTURE)
drv->ifindex = 0;
} else {
wpa_supplicant_event_global(global->ctx,
EVENT_INTERFACE_STATUS,
&event);
}
break;
case RTM_IFINFO:
ifm = (struct if_msghdr *) rtm;
drv = bsd_get_drvindex(global, ifm->ifm_index);
if (drv == NULL)
return;
if ((ifm->ifm_flags & IFF_UP) == 0 &&
(drv->flags & IFF_UP) != 0) {
wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
drv->ifname);
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
NULL);
} else if ((ifm->ifm_flags & IFF_UP) != 0 &&
(drv->flags & IFF_UP) == 0) {
wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
drv->ifname);
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
NULL);
}
drv->flags = ifm->ifm_flags;
break;
}
}
#ifdef HOSTAPD
/*
* Avoid conflicts with hostapd definitions by undefining couple of defines
* from net80211 header files.
*/
#undef RSN_VERSION
#undef WPA_VERSION
#undef WPA_OUI_TYPE
static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason_code);
static const char *
ether_sprintf(const u8 *addr)
{
static char buf[sizeof(MACSTR)];
if (addr != NULL)
snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
else
snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
return buf;
}
static int
bsd_set_privacy(void *priv, int enabled)
{
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
}
static int
bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
u8 *seq)
{
struct ieee80211req_key wk;
wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
__func__, ether_sprintf(addr), idx);
memset(&wk, 0, sizeof(wk));
if (addr == NULL)
memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
else
memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
wk.ik_keyix = idx;
if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
wpa_printf(MSG_INFO, "Failed to get encryption");
return -1;
}
#ifdef WORDS_BIGENDIAN
{
/*
* wk.ik_keytsc is in host byte order (big endian), need to
* swap it to match with the byte order used in WPA.
*/
int i;
u8 tmp[WPA_KEY_RSC_LEN];
memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
}
}
#else /* WORDS_BIGENDIAN */
memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
#endif /* WORDS_BIGENDIAN */
return 0;
}
static int
bsd_flush(void *priv)
{
u8 allsta[IEEE80211_ADDR_LEN];
memset(allsta, 0xff, IEEE80211_ADDR_LEN);
return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
}
static int
bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
const u8 *addr)
{
struct ieee80211req_sta_stats stats;
memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
> 0) {
/* XXX? do packets counts include non-data frames? */
data->rx_packets = stats.is_stats.ns_rx_data;
data->rx_bytes = stats.is_stats.ns_rx_bytes;
data->tx_packets = stats.is_stats.ns_tx_data;
data->tx_bytes = stats.is_stats.ns_tx_bytes;
}
return 0;
}
static int
bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code)
{
return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
addr);
}
static int
bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason_code)
{
return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
addr);
}
static void
handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
{
struct bsd_driver_data *drv = ctx;
drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
}
static void *
bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
{
struct bsd_driver_data *drv;
drv = os_zalloc(sizeof(struct bsd_driver_data));
if (drv == NULL) {
wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data");
return NULL;
}
drv->ifindex = if_nametoindex(params->ifname);
if (drv->ifindex == 0) {
wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
__func__, params->ifname);
goto bad;
}
drv->ctx = hapd;
drv->is_ap = 1;
drv->global = params->global_priv;
os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
handle_read, drv, 0);
if (drv->sock_xmit == NULL)
goto bad;
if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
goto bad;
if (bsd_get_iface_flags(drv) < 0)
goto bad;
if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
__func__);
goto bad;
}
dl_list_add(&drv->global->ifaces, &drv->list);
return drv;
bad:
if (drv->sock_xmit != NULL)
l2_packet_deinit(drv->sock_xmit);
os_free(drv);
return NULL;
}
static void
bsd_deinit(void *priv)
{
struct bsd_driver_data *drv = priv;
if (drv->sock_xmit != NULL)
l2_packet_deinit(drv->sock_xmit);
os_free(drv);
}
static int
bsd_set_sta_authorized(void *priv, const u8 *addr,
unsigned int total_flags, unsigned int flags_or,
unsigned int flags_and)
{
int authorized = -1;
/* For now, only support setting Authorized flag */
if (flags_or & WPA_STA_AUTHORIZED)
authorized = 1;
if (!(flags_and & WPA_STA_AUTHORIZED))
authorized = 0;
if (authorized < 0)
return 0;
return bsd_send_mlme_param(priv, authorized ?
IEEE80211_MLME_AUTHORIZE :
IEEE80211_MLME_UNAUTHORIZE, 0, addr);
}
#else /* HOSTAPD */
static int
get80211param(struct bsd_driver_data *drv, int op)
{
struct ieee80211req ireq;
if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
return -1;
return ireq.i_val;
}
static int
wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
{
struct bsd_driver_data *drv = priv;
#ifdef SIOCG80211BSSID
struct ieee80211_bssid bs;
os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
if (ioctl(drv->global->sock, SIOCG80211BSSID, &bs) < 0)
return -1;
os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
return 0;
#else
return get80211var(drv, IEEE80211_IOC_BSSID,
bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
#endif
}
static int
wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
{
struct bsd_driver_data *drv = priv;
return bsd_get_ssid(drv, ssid, 0);
}
static int
wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
size_t wpa_ie_len)
{
#ifdef IEEE80211_IOC_APPIE
return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
#else /* IEEE80211_IOC_APPIE */
return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
#endif /* IEEE80211_IOC_APPIE */
}
static int
wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
{
int ret = 0;
wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
__func__, wpa, privacy);
if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
ret = -1;
if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
ret = -1;
if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
ret = -1;
return ret;
}
static int
wpa_driver_bsd_set_wpa(void *priv, int enabled)
{
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
}
static int
wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
{
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
}
static int
wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
{
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
}
static int
wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, u16 reason_code)
{
return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
addr);
}
static int
wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
{
int authmode;
if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
(auth_alg & WPA_AUTH_ALG_SHARED))
authmode = IEEE80211_AUTH_AUTO;
else if (auth_alg & WPA_AUTH_ALG_SHARED)
authmode = IEEE80211_AUTH_SHARED;
else
authmode = IEEE80211_AUTH_OPEN;
return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
}
static void
handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
{
struct bsd_driver_data *drv = ctx;
drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
}
static int
wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
{
struct bsd_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
u32 mode;
int privacy;
int ret = 0;
wpa_printf(MSG_DEBUG,
"%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
, __func__
, (unsigned int) params->ssid_len, params->ssid
, (unsigned int) params->wpa_ie_len
, params->pairwise_suite
, params->group_suite
, params->key_mgmt_suite
);
switch (params->mode) {
case IEEE80211_MODE_INFRA:
mode = 0 /* STA */;
break;
case IEEE80211_MODE_IBSS:
mode = IFM_IEEE80211_IBSS;
break;
case IEEE80211_MODE_AP:
mode = IFM_IEEE80211_HOSTAP;
break;
default:
wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
return -1;
}
if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
__func__);
return -1;
}
if (params->mode == IEEE80211_MODE_AP) {
drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
handle_read, drv, 0);
if (drv->sock_xmit == NULL)
return -1;
drv->is_ap = 1;
return 0;
}
if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
< 0)
ret = -1;
if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
ret = -1;
/* XXX error handling is wrong but unclear what to do... */
if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
return -1;
privacy = !(params->pairwise_suite == WPA_CIPHER_NONE &&
params->group_suite == WPA_CIPHER_NONE &&
params->key_mgmt_suite == WPA_KEY_MGMT_NONE &&
params->wpa_ie_len == 0);
wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
return -1;
if (params->wpa_ie_len &&
set80211param(drv, IEEE80211_IOC_WPA,
params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
return -1;
os_memset(&mlme, 0, sizeof(mlme));
mlme.im_op = IEEE80211_MLME_ASSOC;
if (params->ssid != NULL)
os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
mlme.im_ssid_len = params->ssid_len;
if (params->bssid != NULL)
os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
return -1;
return ret;
}
static int
wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
{
struct bsd_driver_data *drv = priv;
#ifdef IEEE80211_IOC_SCAN_MAX_SSID
struct ieee80211_scan_req sr;
int i;
#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
__func__);
return -1;
}
if (set80211param(drv, IEEE80211_IOC_ROAMING,
IEEE80211_ROAMING_MANUAL) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set "
"wpa_supplicant-based roaming: %s", __func__,
strerror(errno));
return -1;
}
if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
strerror(errno));
return -1;
}
/* NB: interface must be marked UP to do a scan */
if (!(drv->flags & IFF_UP)) {
wpa_printf(MSG_DEBUG, "%s: interface is not up, cannot scan",
__func__);
return -1;
}
#ifdef IEEE80211_IOC_SCAN_MAX_SSID
os_memset(&sr, 0, sizeof(sr));
sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
IEEE80211_IOC_SCAN_NOJOIN;
sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
if (params->num_ssids > 0) {
sr.sr_nssid = params->num_ssids;
#if 0
/* Boundary check is done by upper layer */
if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
#endif
/* NB: check scan cache first */
sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
}
for (i = 0; i < sr.sr_nssid; i++) {
sr.sr_ssid[i].len = params->ssids[i].ssid_len;
os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
sr.sr_ssid[i].len);
}
/* NB: net80211 delivers a scan complete event so no need to poll */
return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
#else /* IEEE80211_IOC_SCAN_MAX_SSID */
/* set desired ssid before scan */
if (bsd_set_ssid(drv, params->ssids[0].ssid,
params->ssids[0].ssid_len) < 0)
return -1;
/* NB: net80211 delivers a scan complete event so no need to poll */
return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
}
static void
wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
struct ieee80211req_scan_result *sr)
{
struct wpa_scan_res *result, **tmp;
size_t extra_len;
u8 *pos;
extra_len = 2 + sr->isr_ssid_len;
extra_len += 2 + sr->isr_nrates;
extra_len += 3; /* ERP IE */
extra_len += sr->isr_ie_len;
result = os_zalloc(sizeof(*result) + extra_len);
if (result == NULL)
return;
os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
result->freq = sr->isr_freq;
result->beacon_int = sr->isr_intval;
result->caps = sr->isr_capinfo;
result->qual = sr->isr_rssi;
result->noise = sr->isr_noise;
#ifdef __FreeBSD__
/*
* the rssi value reported by the kernel is in 0.5dB steps relative to
* the reported noise floor. see ieee80211_node.h for details.
*/
result->level = sr->isr_rssi / 2 + sr->isr_noise;
#else
result->level = sr->isr_rssi;
#endif
pos = (u8 *)(result + 1);
*pos++ = WLAN_EID_SSID;
*pos++ = sr->isr_ssid_len;
os_memcpy(pos, sr + 1, sr->isr_ssid_len);
pos += sr->isr_ssid_len;
/*
* Deal all rates as supported rate.
* Because net80211 doesn't report extended supported rate or not.
*/
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = sr->isr_nrates;
os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
pos += sr->isr_nrates;
*pos++ = WLAN_EID_ERP_INFO;
*pos++ = 1;
*pos++ = sr->isr_erp;
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
sr->isr_ie_len);
#else
os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
#endif
pos += sr->isr_ie_len;
result->ie_len = pos - (u8 *)(result + 1);
tmp = os_realloc_array(res->res, res->num + 1,
sizeof(struct wpa_scan_res *));
if (tmp == NULL) {
os_free(result);
return;
}
tmp[res->num++] = result;
res->res = tmp;
}
struct wpa_scan_results *
wpa_driver_bsd_get_scan_results2(void *priv)
{
struct ieee80211req_scan_result *sr;
struct wpa_scan_results *res;
int len, rest;
uint8_t buf[24*1024], *pos;
len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
if (len < 0)
return NULL;
res = os_zalloc(sizeof(*res));
if (res == NULL)
return NULL;
pos = buf;
rest = len;
while (rest >= sizeof(struct ieee80211req_scan_result)) {
sr = (struct ieee80211req_scan_result *)pos;
wpa_driver_bsd_add_scan_entry(res, sr);
pos += sr->isr_len;
rest -= sr->isr_len;
}
wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
len, (unsigned long)res->num);
return res;
}
static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
{
#ifdef IEEE80211_IOC_DEVCAPS
/* kernel definitions copied from net80211/ieee80211_var.h */
#define IEEE80211_CIPHER_WEP 0
#define IEEE80211_CIPHER_TKIP 1
#define IEEE80211_CIPHER_AES_CCM 3
#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */
#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
struct ieee80211_devcaps_req devcaps;
if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
sizeof(devcaps)) < 0) {
wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
strerror(errno));
return -1;
}
wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
__func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
WPA_DRIVER_CAPA_ENC_WEP104;
if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
#undef IEEE80211_CIPHER_WEP
#undef IEEE80211_CIPHER_TKIP
#undef IEEE80211_CIPHER_AES_CCM
#undef IEEE80211_CRYPTO_WEP
#undef IEEE80211_CRYPTO_TKIP
#undef IEEE80211_CRYPTO_AES_CCM
#undef IEEE80211_C_HOSTAP
#undef IEEE80211_C_WPA1
#undef IEEE80211_C_WPA2
#else /* IEEE80211_IOC_DEVCAPS */
/* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
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 |
WPA_DRIVER_CAPA_ENC_TKIP |
WPA_DRIVER_CAPA_ENC_CCMP;
drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
#endif /* IEEE80211_IOC_DEVCAPS */
#ifdef IEEE80211_IOC_SCAN_MAX_SSID
drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
#else /* IEEE80211_IOC_SCAN_MAX_SSID */
drv->capa.max_scan_ssids = 1;
#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
return 0;
}
static enum ieee80211_opmode
get80211opmode(struct bsd_driver_data *drv)
{
struct ifmediareq ifmr;
(void) memset(&ifmr, 0, sizeof(ifmr));
(void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
if (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
if (ifmr.ifm_current & IFM_FLAG0)
return IEEE80211_M_AHDEMO;
else
return IEEE80211_M_IBSS;
}
if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
return IEEE80211_M_HOSTAP;
if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
return IEEE80211_M_MONITOR;
#ifdef IEEE80211_M_MBSS
if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
return IEEE80211_M_MBSS;
#endif /* IEEE80211_M_MBSS */
}
return IEEE80211_M_STA;
}
static void *
wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv)
{
#define GETPARAM(drv, param, v) \
(((v) = get80211param(drv, param)) != -1)
struct bsd_driver_data *drv;
int i;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
drv->ifindex = if_nametoindex(ifname);
if (drv->ifindex == 0) {
wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
__func__, ifname);
goto fail;
}
drv->ctx = ctx;
drv->global = priv;
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
/* Set the interface as removed until proven to work. */
drv->if_removed = 1;
if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
__func__, strerror(errno));
goto fail;
}
if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
__func__, strerror(errno));
goto fail;
}
if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
__func__, strerror(errno));
goto fail;
}
if (wpa_driver_bsd_capa(drv))
goto fail;
/* Update per interface supported AKMs */
for (i = 0; i < WPA_IF_MAX; i++)
drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
/* Down interface during setup. */
if (bsd_get_iface_flags(drv) < 0)
goto fail;
/* Proven to work, lets go! */
drv->if_removed = 0;
drv->opmode = get80211opmode(drv);
dl_list_add(&drv->global->ifaces, &drv->list);
return drv;
fail:
os_free(drv);
return NULL;
#undef GETPARAM
}
static void
wpa_driver_bsd_deinit(void *priv)
{
struct bsd_driver_data *drv = priv;
if (drv->ifindex != 0 && !drv->if_removed) {
wpa_driver_bsd_set_wpa(drv, 0);
wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa,
drv->prev_privacy);
if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)
< 0)
wpa_printf(MSG_DEBUG,
"%s: failed to restore roaming state",
__func__);
}
if (drv->sock_xmit != NULL)
l2_packet_deinit(drv->sock_xmit);
dl_list_del(&drv->list);
os_free(drv);
}
static int
wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
{
struct bsd_driver_data *drv = priv;
os_memcpy(capa, &drv->capa, sizeof(*capa));
return 0;
}
#endif /* HOSTAPD */
static void *
bsd_global_init(void *ctx)
{
struct bsd_driver_global *global;
#if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)
unsigned char msgfilter[] = {
RTM_IEEE80211,
RTM_IFINFO, RTM_IFANNOUNCE,
};
#endif
#ifdef ROUTE_MSGFILTER
unsigned int i, msgfilter_mask;
#endif
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
global->ctx = ctx;
dl_list_init(&global->ifaces);
global->sock = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (global->sock < 0) {
wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
strerror(errno));
goto fail1;
}
global->route = socket(PF_ROUTE,
SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (global->route < 0) {
wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s",
strerror(errno));
goto fail;
}
#if defined(RO_MSGFILTER)
if (setsockopt(global->route, PF_ROUTE, RO_MSGFILTER,
&msgfilter, sizeof(msgfilter)) < 0)
wpa_printf(MSG_ERROR, "socket[PF_ROUTE,RO_MSGFILTER]: %s",
strerror(errno));
#elif defined(ROUTE_MSGFILTER)
msgfilter_mask = 0;
for (i = 0; i < (sizeof(msgfilter) / sizeof(msgfilter[0])); i++)
msgfilter_mask |= ROUTE_FILTER(msgfilter[i]);
if (setsockopt(global->route, PF_ROUTE, ROUTE_MSGFILTER,
&msgfilter_mask, sizeof(msgfilter_mask)) < 0)
wpa_printf(MSG_ERROR, "socket[PF_ROUTE,ROUTE_MSGFILTER]: %s",
strerror(errno));
#endif
eloop_register_read_sock(global->route, bsd_wireless_event_receive,
NULL, global);
return global;
fail:
close(global->sock);
fail1:
os_free(global);
return NULL;
}
static void
bsd_global_deinit(void *priv)
{
struct bsd_driver_global *global = priv;
eloop_unregister_read_sock(global->route);
(void) close(global->route);
(void) close(global->sock);
os_free(global);
}
const struct wpa_driver_ops wpa_driver_bsd_ops = {
.name = "bsd",
.desc = "BSD 802.11 support",
.global_init = bsd_global_init,
.global_deinit = bsd_global_deinit,
#ifdef HOSTAPD
.hapd_init = bsd_init,
.hapd_deinit = bsd_deinit,
.set_privacy = bsd_set_privacy,
.get_seqnum = bsd_get_seqnum,
.flush = bsd_flush,
.read_sta_data = bsd_read_sta_driver_data,
.sta_disassoc = bsd_sta_disassoc,
.sta_deauth = bsd_sta_deauth,
.sta_set_flags = bsd_set_sta_authorized,
#else /* HOSTAPD */
.init2 = wpa_driver_bsd_init,
.deinit = wpa_driver_bsd_deinit,
.get_bssid = wpa_driver_bsd_get_bssid,
.get_ssid = wpa_driver_bsd_get_ssid,
.set_countermeasures = wpa_driver_bsd_set_countermeasures,
.scan2 = wpa_driver_bsd_scan,
.get_scan_results2 = wpa_driver_bsd_get_scan_results2,
.deauthenticate = wpa_driver_bsd_deauthenticate,
.associate = wpa_driver_bsd_associate,
.get_capa = wpa_driver_bsd_get_capa,
#endif /* HOSTAPD */
.set_freq = bsd_set_freq,
.set_key = bsd_set_key,
.set_ieee8021x = bsd_set_ieee8021x,
.hapd_set_ssid = bsd_set_ssid,
.hapd_get_ssid = bsd_get_ssid,
.hapd_send_eapol = bsd_send_eapol,
.set_generic_elem = bsd_set_opt_ie,
};