#include "eloop.h"
#include "ieee802_11_defs.h"
+#ifdef CONFIG_AP
+
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "../hostapd/driver.h"
+#include "../hostapd/hostapd_defs.h"
+
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+
+#endif /* CONFIG_AP */
+
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
#endif
int associated;
u8 ssid[32];
size_t ssid_len;
+
+#ifdef CONFIG_AP
+ int beacon_int;
+ unsigned int beacon_set:1;
+ int monitor_sock;
+ int monitor_ifidx;
+#endif /* CONFIG_AP */
};
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
+#ifdef CONFIG_AP
+static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
+ int ifidx);
+#endif /* CONFIG_AP */
+
/* nl80211 code */
static int ack_handler(struct nl_msg *msg, void *arg)
status = le_to_host16(mgmt->u.assoc_resp.status_code);
if (status != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "nl80211: Association failed: status "
- "code %d", status);
- /* TODO: notify SME so that things like SA Query and comeback
- * time can be implemented */
+ os_memset(&event, 0, sizeof(event));
+ if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+ event.assoc_reject.resp_ies =
+ (u8 *) mgmt->u.assoc_resp.variable;
+ event.assoc_reject.resp_ies_len =
+ len - 24 - sizeof(mgmt->u.assoc_resp);
+ }
+ event.assoc_reject.status_code = status;
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
return;
}
if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
event.assoc_info.resp_ies_len =
- len - 24 - sizeof(mgmt->u.assoc_req);
+ len - 24 - sizeof(mgmt->u.assoc_resp);
}
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
return NULL;
drv->ctx = ctx;
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+#ifdef CONFIG_AP
+ drv->monitor_ifidx = -1;
+ drv->monitor_sock = -1;
+#endif /* CONFIG_AP */
drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (drv->nl_cb == NULL) {
struct wpa_driver_nl80211_data *drv = priv;
int flags;
+#ifdef CONFIG_AP
+ if (drv->monitor_ifidx >= 0)
+ nl80211_remove_iface(drv, drv->monitor_ifidx);
+ if (drv->monitor_sock >= 0) {
+ eloop_unregister_read_sock(drv->monitor_sock);
+ close(drv->monitor_sock);
+ }
+#endif /* CONFIG_AP */
+
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, 0);
case WPA_ALG_CCMP:
NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04);
break;
+#ifdef CONFIG_IEEE80211W
+ case WPA_ALG_IGTK:
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06);
+ break;
+#endif /* CONFIG_IEEE80211W */
default:
nlmsg_free(msg);
return -1;
void *priv, struct wpa_driver_auth_params *params)
{
struct wpa_driver_nl80211_data *drv = priv;
- int ret = -1;
+ int ret = -1, i;
struct nl_msg *msg;
enum nl80211_auth_type type;
wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
drv->ifindex);
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ wpa_driver_nl80211_set_key(drv, WPA_ALG_WEP, NULL, i,
+ i == params->wep_tx_keyidx, NULL, 0,
+ params->wep_key[i],
+ params->wep_key_len[i]);
+ }
+
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
NL80211_CMD_AUTHENTICATE, 0);
#ifdef CONFIG_AP
+
+struct phy_info_arg {
+ u16 *num_modes;
+ struct hostapd_hw_modes *modes;
+};
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct phy_info_arg *phy_info = arg;
+
+ struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+
+ struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+ static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+ [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+ };
+
+ struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+ [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+ [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
+ };
+
+ struct nlattr *nl_band;
+ struct nlattr *nl_freq;
+ struct nlattr *nl_rate;
+ int rem_band, rem_freq, rem_rate;
+ struct hostapd_hw_modes *mode;
+ int idx, mode_is_set;
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+ return NL_SKIP;
+
+ nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+ mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+ if (!mode)
+ return NL_SKIP;
+ phy_info->modes = mode;
+
+ mode_is_set = 0;
+
+ mode = &phy_info->modes[*(phy_info->num_modes)];
+ memset(mode, 0, sizeof(*mode));
+ *(phy_info->num_modes) += 1;
+
+ nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+ nla_len(nl_band), NULL);
+
+ if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
+ mode->ht_capab = nla_get_u16(
+ tb_band[NL80211_BAND_ATTR_HT_CAPA]);
+ }
+
+ nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+ nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+ nla_len(nl_freq), freq_policy);
+ if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+ continue;
+ mode->num_channels++;
+ }
+
+ mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data));
+ if (!mode->channels)
+ return NL_SKIP;
+
+ idx = 0;
+
+ nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+ nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+ nla_len(nl_freq), freq_policy);
+ if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+ continue;
+
+ mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+ mode->channels[idx].flag = 0;
+
+ if (!mode_is_set) {
+ /* crude heuristic */
+ if (mode->channels[idx].freq < 4000)
+ mode->mode = HOSTAPD_MODE_IEEE80211B;
+ else
+ mode->mode = HOSTAPD_MODE_IEEE80211A;
+ mode_is_set = 1;
+ }
+
+ /* crude heuristic */
+ if (mode->channels[idx].freq < 4000)
+ if (mode->channels[idx].freq == 2848)
+ mode->channels[idx].chan = 14;
+ else
+ mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5;
+ else
+ mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_DISABLED;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_PASSIVE_SCAN;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_NO_IBSS;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_RADAR;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
+ !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ mode->channels[idx].max_tx_power =
+ nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
+
+ idx++;
+ }
+
+ nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+ nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+ nla_len(nl_rate), rate_policy);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ mode->num_rates++;
+ }
+
+ mode->rates = calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
+ if (!mode->rates)
+ return NL_SKIP;
+
+ idx = 0;
+
+ nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+ nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+ nla_len(nl_rate), rate_policy);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ mode->rates[idx].rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
+
+ /* crude heuristic */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
+ mode->rates[idx].rate > 200)
+ mode->mode = HOSTAPD_MODE_IEEE80211G;
+
+ if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE])
+ mode->rates[idx].flags |= HOSTAPD_RATE_PREAMBLE2;
+
+ idx++;
+ }
+ }
+
+ return NL_SKIP;
+}
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes)
+{
+ u16 m;
+ struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+ int i, mode11g_idx = -1;
+
+ /* If only 802.11g mode is included, use it to construct matching
+ * 802.11b mode data. */
+
+ for (m = 0; m < *num_modes; m++) {
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+ return modes; /* 802.11b already included */
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+ mode11g_idx = m;
+ }
+
+ if (mode11g_idx < 0)
+ return modes; /* 2.4 GHz band not supported at all */
+
+ nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes));
+ if (nmodes == NULL)
+ return modes; /* Could not add 802.11b mode */
+
+ mode = &nmodes[*num_modes];
+ os_memset(mode, 0, sizeof(*mode));
+ (*num_modes)++;
+ modes = nmodes;
+
+ mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+ mode11g = &modes[mode11g_idx];
+ mode->num_channels = mode11g->num_channels;
+ mode->channels = os_malloc(mode11g->num_channels *
+ sizeof(struct hostapd_channel_data));
+ if (mode->channels == NULL) {
+ (*num_modes)--;
+ return modes; /* Could not add 802.11b mode */
+ }
+ os_memcpy(mode->channels, mode11g->channels,
+ mode11g->num_channels * sizeof(struct hostapd_channel_data));
+
+ mode->num_rates = 0;
+ mode->rates = os_malloc(4 * sizeof(struct hostapd_rate_data));
+ if (mode->rates == NULL) {
+ os_free(mode->channels);
+ (*num_modes)--;
+ return modes; /* Could not add 802.11b mode */
+ }
+
+ for (i = 0; i < mode11g->num_rates; i++) {
+ if (mode11g->rates[i].rate > 110 ||
+ mode11g->rates[i].flags &
+ (HOSTAPD_RATE_ERP | HOSTAPD_RATE_OFDM))
+ continue;
+ mode->rates[mode->num_rates] = mode11g->rates[i];
+ mode->num_rates++;
+ if (mode->num_rates == 4)
+ break;
+ }
+
+ if (mode->num_rates == 0) {
+ os_free(mode->channels);
+ os_free(mode->rates);
+ (*num_modes)--;
+ return modes; /* No 802.11b rates */
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+ "information");
+
+ return modes;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+ struct phy_info_arg result = {
+ .num_modes = num_modes,
+ .modes = NULL,
+ };
+
+ *num_modes = 0;
+ *flags = 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return NULL;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0)
+ return wpa_driver_nl80211_add_11b(result.modes, num_modes);
+ nla_put_failure:
+ return NULL;
+}
+
+
+static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt)
+{
+ __u8 rtap_hdr[] = {
+ 0x00, 0x00, /* radiotap version */
+ 0x0e, 0x00, /* radiotap length */
+ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+ IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+ 0x00, /* padding */
+ 0x00, 0x00, /* RX and TX flags to indicate that */
+ 0x00, 0x00, /* this is the injected frame directly */
+ };
+ struct iovec iov[2] = {
+ {
+ .iov_base = &rtap_hdr,
+ .iov_len = sizeof(rtap_hdr),
+ },
+ {
+ .iov_base = (void *) data,
+ .iov_len = len,
+ }
+ };
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+
+ if (encrypt)
+ rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+ return sendmsg(drv->monitor_sock, &msg, 0);
+}
+
+
+static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
+ size_t data_len)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct ieee80211_mgmt *mgmt;
+ int do_not_encrypt = 0;
+ u16 fc;
+
+ mgmt = (struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+ /*
+ * Only one of the authentication frame types is encrypted.
+ * In order for static WEP encryption to work properly (i.e.,
+ * to not encrypt the frame), we need to tell mac80211 about
+ * the frames that must not be encrypted.
+ */
+ u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
+ if (auth_alg == WLAN_AUTH_OPEN ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_trans != 3))
+ do_not_encrypt = 1;
+ }
+
+ return wpa_driver_nl80211_send_frame(drv, data, data_len,
+ !do_not_encrypt);
+}
+
+
+static int wpa_driver_nl80211_set_beacon(void *priv,
+ const u8 *head, size_t head_len,
+ const u8 *tail, size_t tail_len,
+ int dtim_period)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+ u8 cmd = NL80211_CMD_NEW_BEACON;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+ drv->beacon_set);
+ if (drv->beacon_set)
+ cmd = NL80211_CMD_SET_BEACON;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, cmd, 0);
+ NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head);
+ NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ if (!drv->beacon_int)
+ drv->beacon_int = 100;
+ NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int);
+ NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+ ret, strerror(-ret));
+ } else
+ drv->beacon_set = 1;
+ return ret;
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_set_beacon_int(void *priv, int value)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ struct nl_msg *msg;
+
+ drv->beacon_int = value;
+
+ if (!drv->beacon_set)
+ return 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set beacon interval %d "
+ "(beacon_set=%d)", value, drv->beacon_set);
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_BEACON, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
static int wpa_driver_nl80211_set_freq2(
struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
struct nl_msg *msg;
+ int ret;
msg = nlmsg_alloc();
if (!msg)
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- /* TODO: proper channel configuration */
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, 2437);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
- if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: MLME Failed to set channel (freq=%d): "
+ "%d (%s)", params->freq, ret, strerror(-ret));
nla_put_failure:
return -1;
}
+static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
+ int ifidx)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_INTERFACE, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
+
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return;
+ nla_put_failure:
+ wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d).\n",
+ ifidx);
+}
+
+
+static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname, enum nl80211_iftype iftype)
+{
+ struct nl_msg *msg, *flags = NULL;
+ int ifidx;
+ int ret = -ENOBUFS;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_NEW_INTERFACE, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
+
+ if (iftype == NL80211_IFTYPE_MONITOR) {
+ int err;
+
+ flags = nlmsg_alloc();
+ if (!flags)
+ goto nla_put_failure;
+
+ NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES);
+
+ err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags);
+
+ nlmsg_free(flags);
+
+ if (err)
+ goto nla_put_failure;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ nla_put_failure:
+ wpa_printf(MSG_ERROR, "Failed to create interface %s.",
+ ifname);
+ return ret;
+ }
+
+ ifidx = if_nametoindex(ifname);
+
+ if (ifidx <= 0)
+ return -1;
+
+ return ifidx;
+}
+
+
+enum ieee80211_msg_type {
+ ieee80211_msg_normal = 0,
+ ieee80211_msg_tx_callback_ack = 1,
+ ieee80211_msg_tx_callback_fail = 2,
+};
+
+
+void ap_tx_status(void *ctx, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr);
+void ap_mgmt_rx(void *ctx, u8 *buf, size_t len, u16 stype,
+ struct hostapd_frame_info *fi);
+void ap_mgmt_tx_cb(void *ctx, u8 *buf, size_t len, u16 stype, int ok);
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc, type, stype;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ switch (type) {
+ case WLAN_FC_TYPE_MGMT:
+ wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s",
+ ok ? "ACK" : "fail");
+ ap_mgmt_tx_cb(ctx, buf, len, stype, ok);
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s",
+ ok ? "ACK" : "fail");
+ break;
+ case WLAN_FC_TYPE_DATA:
+ ap_tx_status(ctx, hdr->addr1, buf, len, ok);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "unknown TX callback frame type %d",
+ type);
+ break;
+ }
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+ u8 *buf, size_t len,
+ struct hostapd_frame_info *hfi,
+ enum ieee80211_msg_type msg_type)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc, type, stype;
+ size_t data_len = len;
+
+ /*
+ * PS-Poll frames are 16 bytes. All other frames are
+ * 24 bytes or longer.
+ */
+ if (len < 16)
+ return;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ switch (type) {
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ return;
+ switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_TODS:
+ break;
+ case WLAN_FC_FROMDS:
+ break;
+ default:
+ /* discard */
+ return;
+ }
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ /* discard non-ps-poll frames */
+ if (stype != WLAN_FC_STYPE_PSPOLL)
+ return;
+ break;
+ case WLAN_FC_TYPE_MGMT:
+ break;
+ default:
+ /* discard */
+ return;
+ }
+
+ switch (msg_type) {
+ case ieee80211_msg_normal:
+ /* continue processing */
+ break;
+ case ieee80211_msg_tx_callback_ack:
+ handle_tx_callback(drv->ctx, buf, data_len, 1);
+ return;
+ case ieee80211_msg_tx_callback_fail:
+ handle_tx_callback(drv->ctx, buf, data_len, 0);
+ return;
+ }
+
+ switch (type) {
+ case WLAN_FC_TYPE_MGMT:
+ if (stype != WLAN_FC_STYPE_BEACON &&
+ stype != WLAN_FC_STYPE_PROBE_REQ)
+ wpa_printf(MSG_MSGDUMP, "MGMT");
+ ap_mgmt_rx(drv->ctx, buf, data_len, stype, hfi);
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ /* can only get here with PS-Poll frames */
+ wpa_printf(MSG_DEBUG, "CTRL");
+ ap_rx_from_unknown_sta(drv->ctx, hdr->addr2);
+ break;
+ case WLAN_FC_TYPE_DATA:
+ ap_rx_from_unknown_sta(drv->ctx, hdr->addr2);
+ break;
+ }
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ int len;
+ unsigned char buf[3000];
+ struct ieee80211_radiotap_iterator iter;
+ int ret;
+ struct hostapd_frame_info hfi;
+ int injected = 0, failed = 0, msg_type, rxflags = 0;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
+ printf("received invalid radiotap frame\n");
+ return;
+ }
+
+ memset(&hfi, 0, sizeof(hfi));
+
+ while (1) {
+ ret = ieee80211_radiotap_iterator_next(&iter);
+ if (ret == -ENOENT)
+ break;
+ if (ret) {
+ printf("received invalid radiotap frame (%d)\n", ret);
+ return;
+ }
+ switch (iter.this_arg_index) {
+ case IEEE80211_RADIOTAP_FLAGS:
+ if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+ len -= 4;
+ break;
+ case IEEE80211_RADIOTAP_RX_FLAGS:
+ rxflags = 1;
+ break;
+ case IEEE80211_RADIOTAP_TX_FLAGS:
+ injected = 1;
+ failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+ IEEE80211_RADIOTAP_F_TX_FAIL;
+ break;
+ case IEEE80211_RADIOTAP_DATA_RETRIES:
+ break;
+ case IEEE80211_RADIOTAP_CHANNEL:
+ /* TODO convert from freq/flags to channel number
+ hfi.channel = XXX;
+ hfi.phytype = XXX;
+ */
+ break;
+ case IEEE80211_RADIOTAP_RATE:
+ hfi.datarate = *iter.this_arg * 5;
+ break;
+ case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
+ hfi.ssi_signal = *iter.this_arg;
+ break;
+ }
+ }
+
+ if (rxflags && injected)
+ return;
+
+ if (!injected)
+ msg_type = ieee80211_msg_normal;
+ else if (failed)
+ msg_type = ieee80211_msg_tx_callback_fail;
+ else
+ msg_type = ieee80211_msg_tx_callback_ack;
+
+ handle_frame(drv, buf + iter.max_length,
+ len - iter.max_length, &hfi, msg_type);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS 0xFF
+#define FAIL 0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+ /*
+ * do a little-endian load of the radiotap length field
+ */
+ /* load lower byte into A */
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+ /* put it into X (== index register) */
+ BPF_STMT(BPF_MISC| BPF_TAX, 0),
+ /* load upper byte into A */
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3),
+ /* left-shift it by 8 */
+ BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+ /* or with X */
+ BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+ /* put result into X */
+ BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+ /*
+ * Allow management frames through, this also gives us those
+ * management frames that we sent ourselves with status
+ */
+ /* load the lower byte of the IEEE 802.11 frame control field */
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ /* mask off frame type and version */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+ /* accept frame if it's both 0, fall through otherwise */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+ /*
+ * TODO: add a bit to radiotap RX flags that indicates
+ * that the sending station is not associated, then
+ * add a filter here that filters on our DA and that flag
+ * to allow us to deauth frames to that bad station.
+ *
+ * Not a regression -- we didn't do it before either.
+ */
+
+#if 0
+ /*
+ * drop non-data frames, WDS frames
+ */
+ /* load the lower byte of the frame control field */
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ /* mask off QoS bit */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c),
+ /* drop non-data frames */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL),
+ /* load the upper byte of the frame control field */
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ /* mask off toDS/fromDS */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03),
+ /* drop WDS frames */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0),
+#endif
+
+ /*
+ * add header length to index
+ */
+ /* load the lower byte of the frame control field */
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ /* mask off QoS bit */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80),
+ /* right shift it by 6 to give 0 or 2 */
+ BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6),
+ /* add data frame header length */
+ BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24),
+ /* add index, was start of 802.11 header */
+ BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+ /* move to index, now start of LL header */
+ BPF_STMT(BPF_MISC | BPF_TAX, 0),
+
+ /*
+ * Accept empty data frames, we use those for
+ * polling activity.
+ */
+ BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+
+ /*
+ * Accept EAPOL frames
+ */
+ BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
+ BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
+
+ /* keep these last two statements or change the code below */
+ /* return 0 == "DROP" */
+ BPF_STMT(BPF_RET | BPF_K, 0),
+ /* return ~0 == "keep all" */
+ BPF_STMT(BPF_RET | BPF_K, ~0),
+};
+
+static struct sock_fprog msock_filter = {
+ .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]),
+ .filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+ int idx;
+
+ /* rewrite all PASS/FAIL jump offsets */
+ for (idx = 0; idx < msock_filter.len; idx++) {
+ struct sock_filter *insn = &msock_filter_insns[idx];
+
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ if (insn->code == (BPF_JMP|BPF_JA)) {
+ if (insn->k == PASS)
+ insn->k = msock_filter.len - idx - 2;
+ else if (insn->k == FAIL)
+ insn->k = msock_filter.len - idx - 3;
+ }
+
+ if (insn->jt == PASS)
+ insn->jt = msock_filter.len - idx - 2;
+ else if (insn->jt == FAIL)
+ insn->jt = msock_filter.len - idx - 3;
+
+ if (insn->jf == PASS)
+ insn->jf = msock_filter.len - idx - 2;
+ else if (insn->jf == FAIL)
+ insn->jf = msock_filter.len - idx - 3;
+ }
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+ &msock_filter, sizeof(msock_filter))) {
+ perror("SO_ATTACH_FILTER");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+ char buf[IFNAMSIZ];
+ struct sockaddr_ll ll;
+ int optval;
+ socklen_t optlen;
+ int flags;
+
+ snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname);
+ buf[IFNAMSIZ - 1] = '\0';
+
+ drv->monitor_ifidx =
+ nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR);
+
+ if (drv->monitor_ifidx < 0)
+ return -1;
+
+ if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0) {
+ wpa_printf(MSG_ERROR, "Could not get interface '%s' flags",
+ buf);
+ goto error;
+ }
+ if (!(flags & IFF_UP)) {
+ if (wpa_driver_nl80211_set_ifflags_ifname(drv, buf,
+ flags | IFF_UP) != 0)
+ {
+ wpa_printf(MSG_ERROR, "Could not set interface '%s' "
+ "UP", buf);
+ goto error;
+ }
+ }
+
+ memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = drv->monitor_ifidx;
+ drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (drv->monitor_sock < 0) {
+ perror("socket[PF_PACKET,SOCK_RAW]");
+ goto error;
+ }
+
+ if (add_monitor_filter(drv->monitor_sock)) {
+ wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+ "interface; do filtering in user space");
+ /* This works, but will cost in performance. */
+ }
+
+ if (bind(drv->monitor_sock, (struct sockaddr *) &ll,
+ sizeof(ll)) < 0) {
+ perror("monitor socket bind");
+ goto error;
+ }
+
+ optlen = sizeof(optval);
+ optval = 20;
+ if (setsockopt
+ (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+ perror("Failed to set socket priority");
+ goto error;
+ }
+
+ if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+ drv, NULL)) {
+ printf("Could not register monitor read socket\n");
+ goto error;
+ }
+
+ return 0;
+ error:
+ nl80211_remove_iface(drv, drv->monitor_ifidx);
+ return -1;
+}
+
+
static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
+ if (drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv))
+ return -1;
+
if (wpa_driver_nl80211_set_mode(drv, params->mode) ||
- wpa_driver_nl80211_set_freq2(drv, params))
+ wpa_driver_nl80211_set_freq2(drv, params)) {
+ nl80211_remove_iface(drv, drv->monitor_ifidx);
+ drv->monitor_ifidx = -1;
return -1;
+ }
/* TODO: setup monitor interface (and add code somewhere to remove this
* when AP mode is stopped; associate with mode != 2 or drv_deinit) */
- /* TODO: setup beacon */
return 0;
}
.get_capa = wpa_driver_nl80211_get_capa,
.set_operstate = wpa_driver_nl80211_set_operstate,
.set_country = wpa_driver_nl80211_set_country,
+#ifdef CONFIG_AP
+ .set_beacon = wpa_driver_nl80211_set_beacon,
+ .set_beacon_int = wpa_driver_nl80211_set_beacon_int,
+ .send_mlme = wpa_driver_nl80211_send_mlme,
+ .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
+#endif /* CONFIG_AP */
};