#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/filter.h>
#include "nl80211_copy.h"
#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
+#include "rfkill.h"
#include "driver.h"
#ifdef CONFIG_LIBNL20
char brname[IFNAMSIZ];
int ifindex;
int if_removed;
+ int if_disabled;
+ struct rfkill_data *rfkill;
struct wpa_driver_capa capa;
int has_capability;
int disable_11b_rates;
unsigned int pending_remain_on_chan:1;
- unsigned int pending_send_action:1;
unsigned int added_bridge:1;
unsigned int added_if_into_bridge:1;
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
- const u8 *addr, int cmd, u16 reason_code);
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change);
static void nl80211_remove_monitor_interface(
struct wpa_driver_nl80211_data *drv);
#ifdef HOSTAPD
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
static int wpa_driver_nl80211_if_remove(void *priv,
enum wpa_driver_if_type type,
const char *ifname);
+#else /* HOSTAPD */
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ return 0;
+}
#endif /* HOSTAPD */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
void *timeout_ctx);
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_nl80211_data *drv = ctx;
int attrlen, rta_len;
struct rtattr *attr;
+ u32 brid = 0;
- if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len)) {
- wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
- ifi->ifi_index);
+ if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
+ !have_ifidx(drv, ifi->ifi_index)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
+ "ifindex %d", ifi->ifi_index);
return;
}
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down");
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+ drv->if_disabled = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+ }
+
/*
* Some drivers send the association event before the operup event--in
* this case, lifting operstate in wpa_driver_nl80211_set_operstate()
drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 0);
- }
+ } else if (attr->rta_type == IFLA_MASTER)
+ brid = nla_get_u32((struct nlattr *) attr);
attr = RTA_NEXT(attr, attrlen);
}
+
+#ifdef HOSTAPD
+ if (ifi->ifi_family == AF_BRIDGE && brid) {
+ /* device has been added to bridge */
+ char namebuf[IFNAMSIZ];
+ if_indextoname(brid, namebuf);
+ wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+ brid, namebuf);
+ add_ifidx(drv, brid);
+ }
+#endif /* HOSTAPD */
}
struct wpa_driver_nl80211_data *drv = ctx;
int attrlen, rta_len;
struct rtattr *attr;
+ u32 brid = 0;
attrlen = len;
attr = (struct rtattr *) buf;
drv,
((char *) attr) + rta_len,
attr->rta_len - rta_len, 1);
- }
+ } else if (attr->rta_type == IFLA_MASTER)
+ brid = nla_get_u32((struct nlattr *) attr);
attr = RTA_NEXT(attr, attrlen);
}
+
+#ifdef HOSTAPD
+ if (ifi->ifi_family == AF_BRIDGE && brid) {
+ /* device has been removed from bridge */
+ char namebuf[IFNAMSIZ];
+ if_indextoname(brid, namebuf);
+ wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge "
+ "%s", brid, namebuf);
+ del_ifidx(drv, brid);
+ }
+#endif /* HOSTAPD */
}
return;
cookie_val = nla_get_u64(cookie);
- wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s",
+ wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s "
+ "(ack=%d)",
(long long unsigned int) cookie_val,
cookie_val == drv->send_action_cookie ?
- " (match)" : " (unknown)");
+ " (match)" : " (unknown)", ack != NULL);
if (cookie_val != drv->send_action_cookie)
return;
}
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+ enum wpa_event_type type,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ const u8 *bssid = NULL;
+ u16 reason_code = 0;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len >= 24) {
+ bssid = mgmt->bssid;
+
+ if (drv->associated != 0 &&
+ os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+ os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+ /*
+ * We have presumably received this deauth as a
+ * response to a clear_state_mismatch() outgoing
+ * deauth. Don't let it take us offline!
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+ "from Unknown BSSID " MACSTR " -- ignoring",
+ MAC2STR(bssid));
+ return;
+ }
+ }
+
+ drv->associated = 0;
+ os_memset(&event, 0, sizeof(event));
+
+ /* Note: Same offset for Reason Code in both frame subtypes */
+ if (len >= 24 + sizeof(mgmt->u.deauth))
+ reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+ if (type == EVENT_DISASSOC) {
+ event.disassoc_info.addr = bssid;
+ event.disassoc_info.reason_code = reason_code;
+ if (frame + len > mgmt->u.disassoc.variable) {
+ event.disassoc_info.ie = mgmt->u.disassoc.variable;
+ event.disassoc_info.ie_len = frame + len -
+ mgmt->u.disassoc.variable;
+ }
+ } else {
+ event.deauth_info.addr = bssid;
+ event.deauth_info.reason_code = reason_code;
+ if (frame + len > mgmt->u.deauth.variable) {
+ event.deauth_info.ie = mgmt->u.deauth.variable;
+ event.deauth_info.ie_len = frame + len -
+ mgmt->u.deauth.variable;
+ }
+ }
+
+ wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
static void mlme_event(struct wpa_driver_nl80211_data *drv,
enum nl80211_commands cmd, struct nlattr *frame,
struct nlattr *addr, struct nlattr *timed_out,
mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_DEAUTHENTICATE:
- drv->associated = 0;
- wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL);
+ mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+ nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_DISASSOCIATE:
- drv->associated = 0;
- wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+ nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_ACTION:
mlme_event_action(drv, freq, nla_data(frame), nla_len(frame));
}
+static int get_link_signal(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+ static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+ };
+ int *sig = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_STA_INFO] ||
+ nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+ tb[NL80211_ATTR_STA_INFO], policy))
+ return NL_SKIP;
+ if (!sinfo[NL80211_STA_INFO_SIGNAL])
+ return NL_SKIP;
+
+ *sig = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ int *sig)
+{
+ struct nl_msg *msg;
+
+ *sig = -9999;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+ return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+ [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+ [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+ };
+ struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+ enum nl80211_cqm_rssi_threshold_event event;
+ union wpa_event_data ed;
+ int sig, res;
+
+ if (tb[NL80211_ATTR_CQM] == NULL ||
+ nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+ cqm_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+ return;
+ event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+ os_memset(&ed, 0, sizeof(ed));
+
+ if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: RSSI high");
+ ed.signal_change.above_threshold = 1;
+ } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: RSSI low");
+ ed.signal_change.above_threshold = 0;
+ } else
+ return;
+
+ res = nl80211_get_link_signal(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_signal = sig;
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm", sig);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
static int process_event(struct nl_msg *msg, void *arg)
{
struct wpa_driver_nl80211_data *drv = arg;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ union wpa_event_data data;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
- if (ifindex != drv->ifindex) {
+ if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
" for foreign interface (ifindex %d)",
gnlh->cmd, ifindex);
break;
}
drv->associated = 0;
- wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ os_memset(&data, 0, sizeof(data));
+ if (tb[NL80211_ATTR_REASON_CODE])
+ data.disassoc_info.reason_code =
+ nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
break;
case NL80211_CMD_MICHAEL_MIC_FAILURE:
mlme_event_michael_mic_failure(drv, tb);
case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
mlme_event_remain_on_channel(drv, 1, tb);
break;
+ case NL80211_CMD_NOTIFY_CQM:
+ nl80211_cqm_event(drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
}
drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ drv->capa.max_remain_on_chan = 5000;
return 0;
}
#endif /* HOSTAPD */
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
- void *ctx)
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
{
int ret;
}
eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
- wpa_driver_nl80211_event_receive, drv, ctx);
+ wpa_driver_nl80211_event_receive, drv, NULL);
return 0;
}
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+ /*
+ * This may be for any interface; use ifdown event to disable
+ * interface.
+ */
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+ "after rfkill unblock");
+ return;
+ }
+ /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
/**
* wpa_driver_nl80211_init - Initialize nl80211 driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
{
struct wpa_driver_nl80211_data *drv;
struct netlink_config *cfg;
+ struct rfkill_config *rcfg;
struct i802_bss *bss;
drv = os_zalloc(sizeof(*drv));
drv->monitor_sock = -1;
drv->ioctl_sock = -1;
- if (wpa_driver_nl80211_init_nl(drv, ctx)) {
+ if (wpa_driver_nl80211_init_nl(drv)) {
os_free(drv);
return NULL;
}
os_free(cfg);
goto failed;
}
+
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (rcfg == NULL)
+ goto failed;
+ rcfg->ctx = drv;
+ os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+ rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+ rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+ drv->rfkill = rfkill_init(rcfg);
+ if (drv->rfkill == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+ os_free(rcfg);
+ }
+
if (wpa_driver_nl80211_finish_drv_init(drv))
goto failed;
return bss;
failed:
+ rfkill_deinit(drv->rfkill);
netlink_deinit(drv->netlink);
if (drv->ioctl_sock >= 0)
close(drv->ioctl_sock);
static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
{
- if (0) {
- /* Public Action frames */
- return nl80211_register_action_frame(drv, (u8 *) "\x04", 1);
- }
+#ifdef CONFIG_P2P
+ /* GAS Initial Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+ return -1;
+ /* GAS Initial Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+ return -1;
+ /* GAS Comeback Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+ return -1;
+ /* GAS Comeback Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+ return -1;
+ /* P2P Public Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+ 6) < 0)
+ return -1;
+ /* P2P Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x7f\x50\x6f\x9a\x09",
+ 5) < 0)
+ return -1;
+#endif /* CONFIG_P2P */
/* FT Action frames */
if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
}
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
{
struct i802_bss *bss = &drv->first_bss;
+ int send_rfkill_event = 0;
drv->ifindex = if_nametoindex(bss->ifname);
drv->first_bss.ifindex = drv->ifindex;
}
if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
- wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
- bss->ifname);
- return -1;
+ if (rfkill_is_blocked(drv->rfkill)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+ "interface '%s' due to rfkill",
+ bss->ifname);
+ drv->if_disabled = 1;
+ send_rfkill_event = 1;
+ } else {
+ wpa_printf(MSG_ERROR, "nl80211: Could not set "
+ "interface '%s' UP", bss->ifname);
+ return -1;
+ }
}
if (wpa_driver_nl80211_capa(drv))
*/
}
+ if (send_rfkill_event) {
+ eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+ drv, drv->ctx);
+ }
+
return 0;
}
netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
netlink_deinit(drv->netlink);
+ rfkill_deinit(drv->rfkill);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
"mismatch (" MACSTR ")", MAC2STR(addr));
wpa_driver_nl80211_mlme(drv, addr,
NL80211_CMD_DEAUTHENTICATE,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
}
}
*/
if (ret || !set_tx || alg == WPA_ALG_NONE)
return ret;
-#ifdef HOSTAPD /* FIX: is this needed? */
+#ifdef HOSTAPD
if (addr)
return ret;
+#else /* HOSTAPD */
+ if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+ return ret;
#endif /* HOSTAPD */
msg = nlmsg_alloc();
static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
- const u8 *addr, int cmd, u16 reason_code)
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change)
{
int ret = -1;
struct nl_msg *msg;
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ if (local_state_change)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int reason_code)
{
- wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
drv->associated = 0;
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
- reason_code);
+ reason_code, 0);
}
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
- wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
drv->associated = 0;
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
- reason_code);
+ reason_code, 0);
}
wpa_printf(MSG_DEBUG, "%s", __func__);
drv->associated = 0;
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE,
- reason_code);
+ reason_code, 0);
}
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
- wpa_driver_nl80211_set_key(bss->ifname, drv, WPA_ALG_WEP, NULL,
- i,
+ wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP,
+ NULL, i,
i == params->wep_tx_keyidx, NULL, 0,
params->wep_key[i],
params->wep_key_len[i]);
wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len);
if (params->ie)
NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie);
- /*
- * TODO: if multiple auth_alg options enabled, try them one by one if
- * the AP rejects authentication due to unknown auth alg
- */
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
goto nla_put_failure;
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+ if (params->local_state_change) {
+ wpa_printf(MSG_DEBUG, " * Local state change only");
+ NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+ }
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
"(%s)", ret, strerror(-ret));
count++;
- if (ret == -EALREADY && count == 1 && params->bssid) {
+ if (ret == -EALREADY && count == 1 && params->bssid &&
+ !params->local_state_change) {
/*
* mac80211 does not currently accept new
* authentication if we are already authenticated. As a
static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
+ if (params->p2p)
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+ "group (GO)");
if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) ||
wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
nl80211_remove_monitor_interface(drv);
params->prev_bssid);
}
+ if (params->p2p)
+ wpa_printf(MSG_DEBUG, " * P2P group");
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
int nlmode;
+ int i;
switch (mode) {
case 0:
* take the device down, try to set the mode again, and bring the
* device back up.
*/
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) {
- /* Try to set the mode again while the interface is down */
- ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
- ret = -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+ "interface down");
+ for (i = 0; i < 10; i++) {
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) ==
+ 0) {
+ /* Try to set the mode again while the interface is
+ * down */
+ ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname,
+ 1))
+ ret = -1;
+ if (!ret)
+ break;
+ } else
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+ "interface down");
+ os_sleep(0, 100000);
}
if (!ret) {
"interfaces");
wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
return;
- }
+ } else if (!old)
+ os_memcpy(drv->if_indices, drv->default_if_indices,
+ sizeof(drv->default_if_indices));
drv->if_indices[drv->num_if_indices] = ifidx;
drv->num_if_indices++;
}
return -ENOBUFS;
}
+#endif /* HOSTAPD */
+
/* Set kernel driver on given frequency (MHz) */
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
}
+#ifdef HOSTAPD
+
static int i802_set_rts(void *priv, int rts)
{
struct i802_bss *bss = priv;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ int ret = -ENOBUFS;
msg = nlmsg_alloc();
if (!msg)
NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
if_nametoindex(ifname));
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+ MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+ MAC2STR(addr), ifname, vlan_id, ret,
+ strerror(-ret));
+ }
nla_put_failure:
- return -ENOBUFS;
+ return ret;
}
{
switch (type) {
case WPA_IF_STATION:
+ case WPA_IF_P2P_CLIENT:
+ case WPA_IF_P2P_GROUP:
return NL80211_IFTYPE_STATION;
case WPA_IF_AP_VLAN:
return NL80211_IFTYPE_AP_VLAN;
case WPA_IF_AP_BSS:
+ case WPA_IF_P2P_GO:
return NL80211_IFTYPE_AP;
}
return -1;
static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
- void *bss_ctx, void **drv_priv)
+ void *bss_ctx, void **drv_priv,
+ char *force_ifname, u8 *if_addr)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
}
#endif /* HOSTAPD */
+ if (addr)
+ os_memcpy(if_addr, addr, ETH_ALEN);
ifidx = nl80211_create_iface(drv, ifname,
wpa_driver_nl80211_if_type(type), addr,
0);
return -1;
}
+ if (!addr &&
+ linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0)
+ return -1;
+
#ifdef HOSTAPD
if (type == WPA_IF_AP_BSS) {
if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
}
msg = nlmsg_alloc();
- if (!msg)
+ if (!msg) {
+ os_free(buf);
return -1;
+ }
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
NL80211_CMD_ACTION, 0);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf);
os_free(buf);
+ buf = NULL;
cookie = 0;
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; "
"cookie 0x%llx", (long long unsigned int) cookie);
drv->send_action_cookie = cookie;
- drv->pending_send_action = 1;
ret = 0;
nla_put_failure:
+ os_free(buf);
nlmsg_free(msg);
return ret;
}
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
- "(freq=%d): %d (%s)", freq, ret, strerror(-ret));
+ "(freq=%d duration=%u): %d (%s)",
+ freq, duration, ret, strerror(-ret));
nla_put_failure:
return -1;
}
}
-static int wpa_driver_nl80211_alloc_interface_addr(void *priv, u8 *addr,
- char *ifname)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
-
- if (ifname)
- ifname[0] = '\0';
-
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, addr) < 0)
- return -1;
-
- if (addr[0] & 0x02) {
- /* TODO: add support for generating multiple addresses */
- addr[0] ^= 0x80;
- } else
- addr[0] = 0x02; /* locally administered */
-
- return 0;
-}
-
-
-static void wpa_driver_nl80211_release_interface_addr(void *priv,
- const u8 *addr)
-{
- /* TODO: keep list of allocated address and release them here */
-}
-
-
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled)
{
}
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg, *cqm = NULL;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+ "hysteresis=%d", threshold, hysteresis);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_CQM, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ cqm = nlmsg_alloc();
+ if (cqm == NULL)
+ return -1;
+
+ NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
+ NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+ nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return 0;
+ msg = NULL;
+
+nla_put_failure:
+ if (cqm)
+ nlmsg_free(cqm);
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+ int encrypt)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.sta_disassoc = i802_sta_disassoc,
.get_inact_sec = i802_get_inact_sec,
.sta_clear_stats = i802_sta_clear_stats,
- .set_freq = i802_set_freq,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
.set_rate_sets = i802_set_rate_sets,
.set_sta_vlan = i802_set_sta_vlan,
.set_wds_sta = i802_set_wds_sta,
#endif /* HOSTAPD */
+ .set_freq = i802_set_freq,
.send_action = wpa_driver_nl80211_send_action,
.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
.cancel_remain_on_channel =
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = wpa_driver_nl80211_probe_req_report,
- .alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr,
- .release_interface_addr = wpa_driver_nl80211_release_interface_addr,
.disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
.deinit_ap = wpa_driver_nl80211_deinit_ap,
.resume = wpa_driver_nl80211_resume,
.send_ft_action = nl80211_send_ft_action,
+ .signal_monitor = nl80211_signal_monitor,
+ .send_frame = nl80211_send_frame,
};