unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len, u64 *cookie,
int no_cck, int no_ack, int offchanok);
-static int nl80211_register_frame(struct i802_bss *bss,
- struct nl_handle *hl_handle,
- u16 type, const u8 *match, size_t match_len);
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
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 have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
- enum wpa_driver_if_type type,
- const char *ifname);
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan);
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled);
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+ int reset_mode);
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
static int i802_set_iface_flags(struct i802_bss *bss, int up);
static int nl80211_set_param(void *priv, const char *param);
}
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+ /*
+ * Clear nlmsg data, e.g., to make sure key material is not left in
+ * heap memory for unnecessarily long time.
+ */
+ if (msg) {
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+ void *data = nlmsg_data(hdr);
+ int len = nlmsg_datalen(hdr);
+
+ os_memset(data, 0, len);
+ }
+}
+
+
static int send_and_recv(struct nl80211_global *global,
struct nl_handle *nl_handle, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
}
out:
nl_cb_put(cb);
+ if (!valid_handler && valid_data == (void *) -1)
+ nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return err;
}
-static int send_and_recv_msgs_global(struct nl80211_global *global,
- struct nl_msg *msg,
- int (*valid_handler)(struct nl_msg *, void *),
- void *valid_data)
-{
- return send_and_recv(global, global->nl, msg, valid_handler,
- valid_data);
-}
-
-
int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
return -1;
}
- ret = send_and_recv_msgs_global(global, msg, family_handler, &res);
+ ret = send_and_recv(global, global->nl, msg, family_handler, &res);
if (ret == 0)
ret = res.id;
return ret;
static void wpa_driver_nl80211_event_newlink(
- struct wpa_driver_nl80211_data *drv, char *ifname)
+ struct wpa_driver_nl80211_data *drv, const char *ifname)
{
union wpa_event_data event;
static void wpa_driver_nl80211_event_dellink(
- struct wpa_driver_nl80211_data *drv, char *ifname)
+ struct wpa_driver_nl80211_data *drv, const char *ifname)
{
union wpa_event_data event;
struct i802_bss *bss;
/* device has been added to bridge */
- if_indextoname(brid, namebuf);
+ if (!if_indextoname(brid, namebuf)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find bridge ifname for ifindex %u",
+ brid);
+ return;
+ }
wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
brid, namebuf);
add_ifidx(drv, brid);
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);
+
+ if (!if_indextoname(brid, namebuf)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find bridge ifname for ifindex %u",
+ brid);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remove ifindex %u for bridge %s",
+ brid, namebuf);
+ }
del_ifidx(drv, brid);
}
}
}
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
-{
- drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
- if (!drv->nl_cb) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct");
- return -1;
- }
-
- nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
- process_drv_event, drv);
-
- return 0;
-}
-
-
static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
{
wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
drv->ctx = ctx;
drv->hostapd = !!hostapd;
drv->eapol_sock = -1;
+
+ /*
+ * There is no driver capability flag for this, so assume it is
+ * supported and disable this on first attempt to use if the driver
+ * rejects the command due to missing support.
+ */
+ drv->set_rekey_offload = 1;
+
drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
drv->if_indices = drv->default_if_indices;
drv->eapol_tx_sock = -1;
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
- if (wpa_driver_nl80211_init_nl(drv)) {
- os_free(drv);
- return NULL;
- }
-
if (nl80211_init_bss(bss))
goto failed;
static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
{
- struct wpa_driver_nl80211_data *drv = bss->drv;
-
if (bss->nl_mgmt) {
wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
"already on! (nl_mgmt=%p)", bss->nl_mgmt);
return -1;
}
- bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt");
+ bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
if (bss->nl_mgmt == NULL)
return -1;
/* WMM-AC ADDTS Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
- return -1;
+ ret = -1;
/* WMM-AC DELTS */
if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
- return -1;
+ ret = -1;
/* Radio Measurement - Neighbor Report Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
nl80211_handle_destroy(drv->rtnl_sk);
}
if (bss->added_bridge) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
+ 0) < 0)
+ wpa_printf(MSG_INFO,
+ "nl80211: Could not set bridge %s down",
+ bss->brname);
if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
nl80211_mgmt_unsubscribe(bss, "deinit");
nl80211_del_p2pdev(bss);
}
- nl_cb_put(drv->nl_cb);
nl80211_destroy_bss(drv->first_bss);
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
+ nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Key management set key failed: ret=%d (%s)",
if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
ret = 0;
if (ret)
return ret;
fail:
+ nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return -ENOBUFS;
}
if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
nl80211_mark_disconnected(drv);
- return nl80211_leave_ibss(drv);
+ return nl80211_leave_ibss(drv, 1);
}
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
return wpa_driver_nl80211_disconnect(drv, reason_code);
suites))
goto fail;
- if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X &&
+ if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) &&
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
goto fail;
static int nl80211_put_freq_params(struct nl_msg *msg,
- struct hostapd_freq_params *freq)
+ const struct hostapd_freq_params *freq)
{
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
return -ENOBUFS;
}
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+ int reset_mode)
{
struct nl_msg *msg;
int ret;
"nl80211: Leave IBSS request sent successfully");
}
- if (wpa_driver_nl80211_set_mode(drv->first_bss,
+ if (reset_mode &&
+ wpa_driver_nl80211_set_mode(drv->first_bss,
NL80211_IFTYPE_STATION)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"station mode");
if (ret == -EALREADY && count == 1) {
wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
"forced leave");
- nl80211_leave_ibss(drv);
+ nl80211_leave_ibss(drv, 0);
nlmsg_free(msg);
goto retry;
}
* on a frequency that the mode is disallowed in.
*/
if (desired_freq_params) {
- res = i802_set_freq(bss, desired_freq_params);
+ res = nl80211_set_channel(bss, desired_freq_params, 0);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to set frequency on interface");
br_added = 1;
}
}
- if (!br_added && br_ifindex &&
- (params->num_bridge == 0 || !params->bridge[0]))
- add_ifidx(drv, br_ifindex);
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (params->num_bridge && params->bridge[0] &&
- i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
- goto failed;
+ if (params->num_bridge && params->bridge[0]) {
+ if (i802_check_bridge(drv, bss, params->bridge[0],
+ params->ifname) < 0)
+ goto failed;
+ if (os_strcmp(params->bridge[0], brname) != 0)
+ br_added = 1;
+ }
+
+ if (!br_added && br_ifindex &&
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex);
#ifdef CONFIG_LIBNL3_ROUTE
if (bss->added_if_into_bridge) {
return NL80211_IFTYPE_P2P_GO;
case WPA_IF_P2P_DEVICE:
return NL80211_IFTYPE_P2P_DEVICE;
+ case WPA_IF_MESH:
+ return NL80211_IFTYPE_MESH_POINT;
}
return -1;
}
-#ifdef CONFIG_P2P
+#if defined(CONFIG_P2P) || defined(CONFIG_MESH)
static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
{
}
-static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
- u8 *new_addr)
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
{
unsigned int idx;
if (idx == 64)
return -1;
- wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address "
+ wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
MACSTR, MAC2STR(new_addr));
return 0;
}
-#endif /* CONFIG_P2P */
+#endif /* CONFIG_P2P || CONFIG_MESH */
struct wdev_info {
}
}
-#ifdef CONFIG_P2P
+#if defined(CONFIG_P2P) || defined(CONFIG_MESH)
if (!addr &&
(type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
- type == WPA_IF_P2P_GO)) {
+ type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) {
/* Enforce unique P2P Interface Address */
u8 new_addr[ETH_ALEN];
}
if (nl80211_addr_in_use(drv->global, new_addr)) {
wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
- "for P2P group interface");
- if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
+ "for %s interface", type == WPA_IF_MESH ?
+ "mesh" : "P2P group");
+ if (nl80211_vif_addr(drv, new_addr) < 0) {
if (added)
nl80211_remove_iface(drv, ifidx);
return -1;
}
os_memcpy(if_addr, new_addr, ETH_ALEN);
}
-#endif /* CONFIG_P2P */
+#endif /* CONFIG_P2P || CONFIG_MESH */
if (type == WPA_IF_AP_BSS) {
struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nlattr *replay_nested;
struct nl_msg *msg;
+ int ret;
+ if (!drv->set_rekey_offload)
+ return;
+
+ 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_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
replay_ctr)) {
+ nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return;
}
nla_nest_end(msg, replay_nested);
- send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ if (ret == -EOPNOTSUPP) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver does not support rekey offload");
+ drv->set_rekey_offload = 0;
+ }
}
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
+
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+ " oper_class=%u freq=%u",
+ MAC2STR(addr), oper_class, params->freq);
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+ (ret = nl80211_put_freq_params(msg, params))) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+ return ret;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+ MAC2STR(addr));
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not build TDLS cancel chan switch");
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
#endif /* CONFIG TDLS */
"capa.max_stations=%u\n"
"capa.probe_resp_offloads=0x%x\n"
"capa.max_acl_mac_addrs=%u\n"
- "capa.num_multichan_concurrent=%u\n",
+ "capa.num_multichan_concurrent=%u\n"
+ "capa.mac_addr_rand_sched_scan_supported=%d\n"
+ "capa.mac_addr_rand_scan_supported=%d\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
drv->capa.max_stations,
drv->capa.probe_resp_offloads,
drv->capa.max_acl_mac_addrs,
- drv->capa.num_multichan_concurrent);
+ drv->capa.num_multichan_concurrent,
+ drv->capa.mac_addr_rand_sched_scan_supported,
+ drv->capa.mac_addr_rand_scan_supported);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
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);
if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0))
goto fail;
+ if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) &&
+ nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+ params->max_peer_links))
+ goto fail;
nla_nest_end(msg, container);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
+ .tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+ .tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
#endif /* CONFIG_TDLS */
.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
.get_mac_addr = wpa_driver_nl80211_get_macaddr,