/*
* Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
eloop_sock_handler handler,
void *eloop_data)
{
+#ifdef CONFIG_LIBNL20
+ /*
+ * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+ * by default. It is possible to hit that limit in some cases where
+ * operations are blocked, e.g., with a burst of Deauthentication frames
+ * to hostapd and STA entry deletion. Try to increase the buffer to make
+ * this less likely to occur.
+ */
+ if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not set nl_socket RX buffer size: %s",
+ strerror(errno));
+ /* continue anyway with the default (smaller) buffer */
+ }
+#endif /* CONFIG_LIBNL20 */
+
nl_socket_set_nonblocking(*handle);
eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
eloop_data, *handle);
static void nl80211_global_deinit(void *priv);
+static void nl80211_check_global(struct nl80211_global *global);
static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
return 1;
if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+ nl80211_check_global(drv->global);
wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
"interface");
wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
- linux_iface_up(drv->global->ioctl_sock,
- drv->first_bss->ifname) > 0) {
+ linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event since interface %s is up", namebuf);
drv->ignore_if_down_event = 0;
return;
}
- wpa_printf(MSG_DEBUG, "nl80211: Interface down");
- if (drv->ignore_if_down_event) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ namebuf, ifname);
+ if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface down",
+ drv->first_bss->ifname);
+ } else if (drv->ignore_if_down_event) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event generated by mode change");
drv->ignore_if_down_event = 0;
if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
if (if_indextoname(ifi->ifi_index, namebuf) &&
- linux_iface_up(drv->global->ioctl_sock,
- drv->first_bss->ifname) == 0) {
+ linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s is down",
namebuf);
}
+static void nl80211_check_global(struct nl80211_global *global)
+{
+ struct nl_handle *handle;
+ const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+ int ret;
+ unsigned int i;
+
+ /*
+ * Try to re-add memberships to handle case of cfg80211 getting reloaded
+ * and all registration having been cleared.
+ */
+ handle = (void *) (((intptr_t) global->nl_event) ^
+ ELOOP_SOCKET_INVALID);
+
+ for (i = 0; groups[i]; i++) {
+ ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+ if (ret >= 0)
+ ret = nl_socket_add_membership(handle, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+ groups[i], ret, strerror(-ret));
+ }
+ }
+}
+
+
static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
{
wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
}
if (drv->global) {
+ nl80211_check_global(drv->global);
dl_list_add(&drv->global->interfaces, &drv->list);
drv->in_interface_list = 1;
}
}
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
+{
+ /* struct wpa_driver_nl80211_data *drv = arg; */
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: QCA vendor test command response received");
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+ wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+ return NL_SKIP;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "nl80211: Received QCA vendor test command response",
+ nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+ nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+ return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+ nlmsg_free(msg);
+ return;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: QCA vendor test command returned %d (%s)",
+ ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
const u8 *set_addr, int first,
drv, drv->ctx);
}
+ if (drv->vendor_cmd_test_avail)
+ qca_vendor_test(drv);
+
return 0;
}
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
if (!msg ||
nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) ||
- nla_put_flag(msg, alg == WPA_ALG_IGTK ?
+ nla_put_flag(msg, (alg == WPA_ALG_IGTK ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256) ?
NL80211_ATTR_KEY_DEFAULT_MGMT :
NL80211_ATTR_KEY_DEFAULT))
goto fail;
}
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+ if (beacon_int > 0) {
+ wpa_printf(MSG_DEBUG, " * beacon_int=%d", beacon_int);
+ return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+ beacon_int);
+ }
+
+ return 0;
+}
+
+
static int wpa_driver_nl80211_set_ap(void *priv,
struct wpa_driver_ap_params *params)
{
u32 suites[10], suite;
u32 ver;
- beacon_set = bss->beacon_set;
+ beacon_set = params->reenable ? 0 : bss->beacon_set;
wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
beacon_set);
params->head) ||
nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
params->tail) ||
- nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
- params->beacon_int) ||
+ nl80211_put_beacon_int(msg, params->beacon_int) ||
nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
goto fail;
}
+#ifdef CONFIG_P2P
+ if (params->p2p_go_ctwindow > 0) {
+ if (drv->p2p_go_ctwindow_supported) {
+ wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+ params->p2p_go_ctwindow);
+ if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+ params->p2p_go_ctwindow))
+ goto fail;
+ } else {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+ }
+ }
+#endif /* CONFIG_P2P */
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
}
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+ struct wpa_driver_associate_params *params)
+{
+ if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+ return -1;
+
+ if (params->htcaps && params->htcaps_mask) {
+ int sz = sizeof(struct ieee80211_ht_capabilities);
+ wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz);
+ wpa_hexdump(MSG_DEBUG, " * htcaps_mask",
+ params->htcaps_mask, sz);
+ if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+ params->htcaps) ||
+ nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+ params->htcaps_mask))
+ return -1;
+ }
+
+#ifdef CONFIG_VHT_OVERRIDES
+ if (params->disable_vht) {
+ wpa_printf(MSG_DEBUG, " * VHT disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+ return -1;
+ }
+
+ if (params->vhtcaps && params->vhtcaps_mask) {
+ int sz = sizeof(struct ieee80211_vht_capabilities);
+ wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz);
+ wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask",
+ params->vhtcaps_mask, sz);
+ if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+ params->vhtcaps) ||
+ nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+ params->vhtcaps_mask))
+ return -1;
+ }
+#endif /* CONFIG_VHT_OVERRIDES */
+
+ return 0;
+}
+
+
static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
os_memcpy(drv->ssid, params->ssid, params->ssid_len);
drv->ssid_len = params->ssid_len;
- if (nl80211_put_freq_params(msg, ¶ms->freq) < 0)
+ if (nl80211_put_freq_params(msg, ¶ms->freq) < 0 ||
+ nl80211_put_beacon_int(msg, params->beacon_int))
goto fail;
- if (params->beacon_int > 0) {
- wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int);
- if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
- params->beacon_int))
- goto fail;
- }
-
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto fail;
goto fail;
}
+ if (params->fixed_freq) {
+ wpa_printf(MSG_DEBUG, " * fixed_freq");
+ if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+ goto fail;
+ }
+
if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
goto fail;
}
+ if (nl80211_ht_vht_overrides(msg, params) < 0)
+ return -1;
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
- params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
int mgmt = WLAN_AKM_SUITE_PSK;
switch (params->key_mgmt_suite) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
break;
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+ break;
case WPA_KEY_MGMT_PSK:
default:
mgmt = WLAN_AKM_SUITE_PSK;
return -1;
}
- if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+ if (nl80211_ht_vht_overrides(msg, params) < 0)
return -1;
- if (params->htcaps && params->htcaps_mask) {
- int sz = sizeof(struct ieee80211_ht_capabilities);
- wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz);
- wpa_hexdump(MSG_DEBUG, " * htcaps_mask",
- params->htcaps_mask, sz);
- if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
- params->htcaps) ||
- nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
- params->htcaps_mask))
- return -1;
- }
-
-#ifdef CONFIG_VHT_OVERRIDES
- if (params->disable_vht) {
- wpa_printf(MSG_DEBUG, " * VHT disabled");
- if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
- return -1;
- }
-
- if (params->vhtcaps && params->vhtcaps_mask) {
- int sz = sizeof(struct ieee80211_vht_capabilities);
- wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz);
- wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask",
- params->vhtcaps_mask, sz);
- if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
- params->vhtcaps) ||
- nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
- params->vhtcaps_mask))
- return -1;
- }
-#endif /* CONFIG_VHT_OVERRIDES */
-
if (params->p2p)
wpa_printf(MSG_DEBUG, " * P2P group");
data.inactive_msec = (unsigned long) -1;
ret = i802_read_sta_data(priv, &data, addr);
+ if (ret == -ENOENT)
+ return -ENOENT;
if (ret || data.inactive_msec == (unsigned long) -1)
return -1;
return data.inactive_msec / 1000;
}
-static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
const u8 *replay_ctr)
{
struct i802_bss *bss = priv;
wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
!(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
- nla_put(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek) ||
- nla_put(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck) ||
+ nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+ nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
replay_ctr)) {
nl80211_nlmsg_clear(msg);
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ int ret;
if (!drv->poll_command_supported) {
nl80211_send_null_frame(bss, own_addr, addr, qos);
return;
}
- send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+ MACSTR " failed: ret=%d (%s)",
+ MAC2STR(addr), ret, strerror(-ret));
+ }
}
}
-static int
-wpa_driver_nl80211_join_mesh(void *priv,
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+ size_t mesh_id_len)
+{
+ if (mesh_id) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * Mesh ID (SSID)",
+ mesh_id, mesh_id_len);
+ return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_join_mesh(struct i802_bss *bss,
struct wpa_driver_mesh_join_params *params)
{
- struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *container;
- int ret = 0;
+ int ret = -1;
wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
- if (!msg)
- goto fail;
- if (params->freq) {
- wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
- goto fail;
- }
-
- if (params->ht_mode) {
- unsigned int ht_value;
- char *ht_mode = "";
-
- switch (params->ht_mode) {
- default:
- case CHAN_NO_HT:
- ht_value = NL80211_CHAN_NO_HT;
- ht_mode = "NOHT";
- break;
- case CHAN_HT20:
- ht_value = NL80211_CHAN_HT20;
- ht_mode = "HT20";
- break;
- case CHAN_HT40PLUS:
- ht_value = NL80211_CHAN_HT40PLUS;
- ht_mode = "HT40+";
- break;
- case CHAN_HT40MINUS:
- ht_value = NL80211_CHAN_HT40MINUS;
- ht_mode = "HT40-";
- break;
- }
- wpa_printf(MSG_DEBUG, " * ht_mode=%s", ht_mode);
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ht_value))
- goto fail;
- }
-
- if (nl80211_put_basic_rates(msg, params->basic_rates))
+ if (!msg ||
+ nl80211_put_freq_params(msg, ¶ms->freq) ||
+ nl80211_put_basic_rates(msg, params->basic_rates) ||
+ nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+ nl80211_put_beacon_int(msg, params->beacon_int))
goto fail;
- if (params->meshid) {
- wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
- params->meshid, params->meshid_len);
- if (nla_put(msg, NL80211_ATTR_MESH_ID, params->meshid_len,
- params->meshid))
- goto fail;
- }
-
- if (params->beacon_int > 0) {
- wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int);
- if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
- params->beacon_int))
- goto fail;
- }
-
wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags);
container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
params->max_peer_links))
goto fail;
+
+ /*
+ * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+ * the timer could disconnect stations even in that case.
+ */
+ if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+ params->conf.peer_link_timeout)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+ goto fail;
+ }
+
nla_nest_end(msg, container);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
goto fail;
}
ret = 0;
- bss->freq = params->freq;
+ bss->freq = params->freq.freq;
wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
fail:
}
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+ struct wpa_driver_mesh_join_params *params)
+{
+ struct i802_bss *bss = priv;
+ int ret, timeout;
+
+ timeout = params->conf.peer_link_timeout;
+
+ /* Disable kernel inactivity timer */
+ if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+ params->conf.peer_link_timeout = 0;
+
+ ret = nl80211_join_mesh(bss, params);
+ if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Mesh join retry for peer_link_timeout");
+ /*
+ * Old kernel does not support setting
+ * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+ * into future from peer_link_timeout.
+ */
+ params->conf.peer_link_timeout = timeout + 60;
+ ret = nl80211_join_mesh(priv, params);
+ }
+
+ params->conf.peer_link_timeout = timeout;
+ return ret;
+}
+
+
static int wpa_driver_nl80211_leave_mesh(void *priv)
{
struct i802_bss *bss = priv;