nl_handle_destroy(handle);
}
-
-static inline int __genl_ctrl_alloc_cache(struct nl_handle *h,
- struct nl_cache **cache)
-{
- struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
- if (!tmp)
- return -ENOMEM;
- *cache = tmp;
- return 0;
-}
-#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
#endif /* CONFIG_LIBNL20 */
struct nl80211_handles {
struct nl_handle *handle;
- struct nl_cache *cache;
};
goto err;
}
- if (genl_ctrl_alloc_cache(handles->handle, &handles->cache) < 0) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache (%s)", dbg);
- goto err;
- }
-
return 0;
err:
nl80211_handle_destroy(handles->handle);
{
if (handles->handle == NULL)
return;
- nl_cache_free(handles->cache);
nl80211_handle_destroy(handles->handle);
handles->handle = NULL;
}
struct netlink_data *netlink;
struct nl_cb *nl_cb;
struct nl80211_handles nl;
- struct genl_family *nl80211;
+ int nl80211_id;
+ int ioctl_sock; /* socket for ioctl() use */
};
static void nl80211_global_deinit(void *priv);
u8 addr[ETH_ALEN];
char phyname[32];
void *ctx;
- int ioctl_sock; /* socket for ioctl() use */
int ifindex;
int if_removed;
int if_disabled;
int monitor_sock;
int monitor_ifidx;
- int no_monitor_iface_capab;
- int disable_11b_rates;
+ unsigned int disabled_11b_rates:1;
unsigned int pending_remain_on_chan:1;
unsigned int in_interface_list:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
struct i802_bss first_bss;
#ifdef CONFIG_AP
- struct l2_packet_data *l2;
+ int eapol_tx_sock;
#endif /* CONFIG_AP */
#ifdef HOSTAPD
struct wpa_driver_nl80211_data *drv);
static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
unsigned int freq, unsigned int wait,
- const u8 *buf, size_t buf_len, u64 *cookie);
+ const u8 *buf, size_t buf_len, u64 *cookie,
+ int no_cck);
static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
#ifdef HOSTAPD
enum wpa_driver_if_type type,
const char *ifname);
#else /* HOSTAPD */
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
{
return 0;
}
}
+static int is_p2p_interface(enum nl80211_iftype nlmode)
+{
+ return (nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+ nlmode == NL80211_IFTYPE_P2P_GO);
+}
+
+
struct nl80211_bss_info_arg {
struct wpa_driver_nl80211_data *drv;
struct wpa_scan_results *res;
static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, int flags, uint8_t cmd)
{
- return genlmsg_put(msg, 0, 0, genl_family_get_id(drv->global->nl80211),
+ return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
0, flags, cmd, 0);
}
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ char namebuf[IFNAMSIZ];
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock,
+ drv->first_bss.ifname) > 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+ "event since interface %s is up", namebuf);
+ return;
+ }
wpa_printf(MSG_DEBUG, "nl80211: Interface down");
if (drv->ignore_if_down_event) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
attr = RTA_NEXT(attr, attrlen);
}
-#ifdef HOSTAPD
if (ifi->ifi_family == AF_BRIDGE && brid) {
/* device has been added to bridge */
char namebuf[IFNAMSIZ];
brid, namebuf);
add_ifidx(drv, brid);
}
-#endif /* HOSTAPD */
}
attr = RTA_NEXT(attr, attrlen);
}
-#ifdef HOSTAPD
if (ifi->ifi_family == AF_BRIDGE && brid) {
/* device has been removed from bridge */
char namebuf[IFNAMSIZ];
"%s", brid, namebuf);
del_ifidx(drv, brid);
}
-#endif /* HOSTAPD */
}
}
+static int get_noise_for_scan_results(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_SURVEY_INFO_MAX + 1];
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+ struct wpa_scan_results *scan_results = arg;
+ struct wpa_scan_res *scan_res;
+ size_t i;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+ return NL_SKIP;
+ }
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
+ "attributes");
+ return NL_SKIP;
+ }
+
+ if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ for (i = 0; i < scan_results->num; ++i) {
+ scan_res = scan_results->res[i];
+ if (!scan_res)
+ continue;
+ if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ scan_res->freq)
+ continue;
+ if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
+ continue;
+ scan_res->noise = (s8)
+ nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_scan_results *scan_res)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+ scan_res);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
struct nlattr *tb[])
{
addr = nla_data(tb[NL80211_ATTR_MAC]);
wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
- if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+ if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
u8 *ies = NULL;
size_t ies_len = 0;
if (tb[NL80211_ATTR_IE]) {
wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
MAC2STR(addr));
- if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+ if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
drv_event_disassoc(drv->ctx, addr);
return;
}
}
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+
+ if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.client_poll.addr,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+ wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
static int process_event(struct nl_msg *msg, void *arg)
{
struct wpa_driver_nl80211_data *drv = arg;
case NL80211_CMD_PMKSA_CANDIDATE:
nl80211_pmksa_candidate_event(drv, tb);
break;
+ case NL80211_CMD_PROBE_CLIENT:
+ nl80211_client_probe_event(drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
struct wpa_driver_capa *capa;
unsigned int error:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
};
case NL80211_CMD_START_SCHED_SCAN:
capa->sched_scan_supported = 1;
break;
+ case NL80211_CMD_PROBE_CLIENT:
+ info->poll_command_supported = 1;
+ break;
}
}
}
capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
}
+ if (tb[NL80211_ATTR_TDLS_SUPPORT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+ capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+ if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+ capa->flags |=
+ WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+ }
+ }
+
+ if (tb[NL80211_ATTR_DEVICE_AP_SME])
+ info->device_ap_sme = 1;
+
return NL_SKIP;
}
drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+ drv->device_ap_sme = info.device_ap_sme;
+ drv->poll_command_supported = info.poll_command_supported;
+
return 0;
}
if (nl_create_handles(&global->nl, global->nl_cb, "nl"))
return -1;
- global->nl80211 = genl_ctrl_search_by_name(global->nl.cache,
- "nl80211");
- if (global->nl80211 == NULL) {
+ global->nl80211_id = genl_ctrl_resolve(global->nl.handle, "nl80211");
+ if (global->nl80211_id < 0) {
wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
"found");
return -1;
{
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)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock,
+ drv->first_bss.ifname, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
"after rfkill unblock");
return;
}
-#ifdef CONFIG_AP
-static void nl80211_l2_read(void *ctx, const u8 *src_addr, const u8 *buf,
- size_t len)
-{
- wpa_printf(MSG_DEBUG, "nl80211: l2_packet read %u",
- (unsigned int) len);
-}
-#endif /* CONFIG_AP */
-
-
/**
* wpa_driver_nl80211_init - Initialize nl80211 driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
struct rfkill_config *rcfg;
struct i802_bss *bss;
+ if (global_priv == NULL)
+ return NULL;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
- drv->ioctl_sock = -1;
+#ifdef CONFIG_AP
+ drv->eapol_tx_sock = -1;
+#endif /* CONFIG_AP */
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
if (wpa_driver_nl80211_init_nl(drv)) {
nl80211_get_phy_name(drv);
- drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->ioctl_sock < 0) {
- perror("socket(PF_INET,SOCK_DGRAM)");
- goto failed;
- }
-
rcfg = os_zalloc(sizeof(*rcfg));
if (rcfg == NULL)
goto failed;
goto failed;
#ifdef CONFIG_AP
- drv->l2 = l2_packet_init(ifname, NULL, ETH_P_EAPOL,
- nl80211_l2_read, drv, 0);
+ drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
#endif /* CONFIG_AP */
if (drv->global) {
if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
return -1;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_TDLS
+ if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+ /* TDLS Discovery Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0e", 2) <
+ 0)
+ return -1;
+ }
+#endif /* CONFIG_TDLS */
/* FT Action frames */
if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
* dynamically added interface (e.g., P2P) that was already configured
* with proper iftype.
*/
- if ((drv->global == NULL ||
- drv->ifindex != drv->global->if_add_ifindex) &&
+ if (drv->ifindex != drv->global->if_add_ifindex &&
wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to "
"use managed mode");
return -1;
}
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
if (rfkill_is_blocked(drv->rfkill)) {
wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
"interface '%s' due to rfkill",
if (wpa_driver_nl80211_capa(drv))
return -1;
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr))
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ drv->addr))
return -1;
if (nl80211_register_action_frames(drv) < 0) {
struct wpa_driver_nl80211_data *drv = bss->drv;
#ifdef CONFIG_AP
- if (drv->l2)
- l2_packet_deinit(drv->l2);
+ if (drv->eapol_tx_sock >= 0)
+ close(drv->eapol_tx_sock);
#endif /* CONFIG_AP */
if (drv->nl_preq.handle)
wpa_driver_nl80211_probe_req_report(bss, 0);
if (bss->added_if_into_bridge) {
- if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
- < 0)
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
if (bss->added_bridge) {
- if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
bss->brname, strerror(errno));
os_free(drv->if_indices);
#endif /* HOSTAPD */
- if (drv->disable_11b_rates)
+ if (drv->disabled_11b_rates)
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+ (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
-
eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle));
nl_destroy_handles(&drv->nl_event);
NLA_PUT(rates, NL80211_BAND_2GHZ, 8,
"\x0c\x12\x18\x24\x30\x48\x60\x6c");
nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates);
+
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
- r->flags |= WPA_SCAN_LEVEL_INVALID;
+ r->flags |= WPA_SCAN_QUAL_INVALID;
} else
r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
if (bss[NL80211_BSS_TSF])
}
-static void wpa_scan_results_free(struct wpa_scan_results *res)
-{
- size_t i;
-
- if (res == NULL)
- return;
-
- for (i = 0; i < res->num; i++)
- os_free(res->res[i]);
- os_free(res->res);
- os_free(res);
-}
-
-
static struct wpa_scan_results *
nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
{
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
msg = NULL;
if (ret == 0) {
- wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
- (unsigned long) res->num);
+ wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+ "BSSes)", (unsigned long) res->num);
+ nl80211_get_noise_for_scan_results(drv, res);
return res;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
"set_tx=%d seq_len=%lu key_len=%lu",
__func__, ifindex, alg, addr, key_idx, set_tx,
(unsigned long) seq_len, (unsigned long) key_len);
+#ifdef CONFIG_TDLS
+ if (key_idx == -1)
+ key_idx = 0;
+#endif /* CONFIG_TDLS */
msg = nlmsg_alloc();
if (!msg)
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
- "(%s)", ret, strerror(-ret));
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed: reason=%u ret=%d (%s)",
+ reason_code, ret, strerror(-ret));
goto nla_put_failure;
}
ret = 0;
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
- "(%s)", ret, strerror(-ret));
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed (auth): ret=%d (%s)",
+ ret, strerror(-ret));
count++;
if (ret == -EALREADY && count == 1 && params->bssid &&
!params->local_state_change) {
mode = &phy_info->modes[*(phy_info->num_modes)];
memset(mode, 0, sizeof(*mode));
+ mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
*(phy_info->num_modes) += 1;
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
- size_t data_len)
+ size_t data_len, int noack)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
* of wpa_supplicant.
*/
return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
- data, data_len, NULL);
+ data, data_len, NULL, 1);
}
- if (drv->no_monitor_iface_capab && is_ap_interface(drv->nlmode)) {
+ if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
return nl80211_send_frame_cmd(drv, drv->ap_oper_freq, 0,
- data, data_len, NULL);
+ data, data_len, NULL, 0);
}
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
if (flags & WPA_STA_MFP)
f |= BIT(NL80211_STA_FLAG_MFP);
+ if (flags & WPA_STA_TDLS_PEER)
+ f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
return f;
}
struct nl80211_sta_flag_update upd;
int ret = -ENOBUFS;
+ if ((params->flags & WPA_STA_TDLS_PEER) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_STATION);
+ nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
+ NL80211_CMD_NEW_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
params->supp_rates);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
- params->listen_interval);
+ if (!params->set) {
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval);
+ }
if (params->ht_capabilities) {
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
sizeof(*params->ht_capabilities),
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret)
- wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
- "result: %d (%s)", ret, strerror(-ret));
+ wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
+ "result: %d (%s)", params->set ? "SET" : "NEW", ret,
+ strerror(-ret));
if (ret == -EEXIST)
ret = 0;
nla_put_failure:
wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
-#ifdef HOSTAPD
/* stop listening for EAPOL on this interface */
del_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
msg = nlmsg_alloc();
if (!msg)
if (ifidx <= 0)
return -1;
-#ifdef HOSTAPD
/* start listening for EAPOL on this interface */
add_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
if (addr && iftype != NL80211_IFTYPE_MONITOR &&
- linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
wds);
}
- if (ret >= 0 && drv->disable_11b_rates)
+ if (ret >= 0 && is_p2p_interface(iftype))
nl80211_disable_11b_rates(drv, ret, 1);
return ret;
0);
if (drv->monitor_ifidx == -EOPNOTSUPP) {
+ /*
+ * This is backward compatibility for a few versions of
+ * the kernel only that didn't advertise the right
+ * attributes for the only driver that then supported
+ * AP mode w/o monitor -- ath6kl.
+ */
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
"monitor interface type - try to run without it");
- drv->no_monitor_iface_capab = 1;
+ drv->device_ap_sme = 1;
}
if (drv->monitor_ifidx < 0)
return -1;
- if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
goto error;
memset(&ll, 0, sizeof(ll));
#ifdef CONFIG_AP
static int nl80211_send_eapol_data(struct i802_bss *bss,
const u8 *addr, const u8 *data,
- size_t data_len, const u8 *own_addr)
+ size_t data_len)
{
- if (bss->drv->l2 == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: No l2_packet to send EAPOL");
+ struct sockaddr_ll ll;
+ int ret;
+
+ if (bss->drv->eapol_tx_sock < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
return -1;
}
- if (l2_packet_send(bss->drv->l2, addr, ETH_P_EAPOL, data, data_len) <
- 0)
- return -1;
- return 0;
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = bss->ifindex;
+ ll.sll_protocol = htons(ETH_P_PAE);
+ ll.sll_halen = ETH_ALEN;
+ os_memcpy(ll.sll_addr, addr, ETH_ALEN);
+ ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
+ (struct sockaddr *) &ll, sizeof(ll));
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
+ strerror(errno));
+
+ return ret;
}
#endif /* CONFIG_AP */
int qos = flags & WPA_STA_WMM;
#ifdef CONFIG_AP
- if (drv->no_monitor_iface_capab)
- return nl80211_send_eapol_data(bss, addr, data, data_len,
- own_addr);
+ if (drv->device_ap_sme)
+ return nl80211_send_eapol_data(bss, addr, data, data_len);
#endif /* CONFIG_AP */
len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
if (total_flags & WPA_STA_MFP)
NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
+ if (total_flags & WPA_STA_TDLS_PEER)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER);
+
if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
goto nla_put_failure;
return -1;
}
- if (drv->no_monitor_iface_capab) {
+ if (drv->device_ap_sme) {
if (wpa_driver_nl80211_probe_req_report(&drv->first_bss, 1) < 0)
{
wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
- "(%s)", ret, strerror(-ret));
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed (assoc): ret=%d (%s)",
+ ret, strerror(-ret));
nl80211_dump_scan(drv);
goto nla_put_failure;
}
"interface down");
for (i = 0; i < 10; i++) {
int res;
- res = linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+ res = linux_set_iface_flags(drv->global->ioctl_sock,
+ bss->ifname, 0);
if (res == -EACCES || res == -ENODEV)
break;
if (res == 0) {
ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
if (ret == -EACCES)
break;
- res = linux_set_iface_flags(drv->ioctl_sock,
+ res = linux_set_iface_flags(drv->global->ioctl_sock,
bss->ifname, 1);
if (res && !ret)
ret = -1;
done:
if (!ret && is_ap_interface(nlmode)) {
/* Setup additional AP mode functionality if needed */
- if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 &&
+ if (!drv->device_ap_sme && drv->monitor_ifidx < 0 &&
nl80211_create_monitor_interface(drv) &&
- !drv->no_monitor_iface_capab)
+ !drv->device_ap_sme)
return -1;
} else if (!ret && !is_ap_interface(nlmode)) {
/* Remove additional AP mode functionality */
- if (was_ap && drv->no_monitor_iface_capab)
+ if (was_ap && drv->device_ap_sme)
wpa_driver_nl80211_probe_req_report(bss, 0);
nl80211_remove_monitor_interface(drv);
bss->beacon_set = 0;
}
+ if (!ret && is_p2p_interface(drv->nlmode)) {
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+ drv->disabled_11b_rates = 1;
+ } else if (!ret && drv->disabled_11b_rates) {
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+ drv->disabled_11b_rates = 0;
+ }
+
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
"from %d failed", nlmode, drv->nlmode);
mgmt.u.deauth.reason_code = host_to_le16(reason);
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth));
+ sizeof(mgmt.u.deauth), 0);
}
mgmt.u.disassoc.reason_code = host_to_le16(reason);
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
- sizeof(mgmt.u.disassoc));
+ sizeof(mgmt.u.disassoc), 0);
}
#endif /* HOSTAPD || CONFIG_AP */
NULL, 1) < 0)
return -1;
if (bridge_ifname &&
- linux_br_add_if(drv->ioctl_sock, bridge_ifname,
- name) < 0)
+ linux_br_add_if(drv->global->ioctl_sock,
+ bridge_ifname, name) < 0)
return -1;
}
- linux_set_iface_flags(drv->ioctl_sock, name, 1);
+ linux_set_iface_flags(drv->global->ioctl_sock, name, 1);
return i802_set_sta_vlan(priv, addr, name, 0);
} else {
i802_set_sta_vlan(priv, addr, bss->ifname, 0);
* Bridge was configured, but the bridge device does
* not exist. Try to add it now.
*/
- if (linux_br_add(drv->ioctl_sock, brname) < 0) {
+ if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
"bridge interface %s: %s",
brname, strerror(errno));
wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
"bridge %s", ifname, in_br);
- if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) {
+ if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+ 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to "
"remove interface %s from bridge "
"%s: %s",
wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
ifname, brname);
- if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+ if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
"into bridge %s: %s",
ifname, brname, strerror(errno));
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
goto failed;
if (params->bssid) {
- if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
params->bssid))
goto failed;
}
i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
goto failed;
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
goto failed;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
goto failed;
}
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ params->own_addr))
goto failed;
return bss;
}
if (!addr &&
- linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) {
+ linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ if_addr) < 0) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
/* Enforce unique P2P Interface Address */
u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN];
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr)
- < 0 ||
- linux_get_ifhwaddr(drv->ioctl_sock, ifname, new_addr) < 0)
- {
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ own_addr) < 0 ||
+ linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
nl80211_remove_iface(drv, ifidx);
return -1;
}
- if (linux_set_ifhwaddr(drv->ioctl_sock, ifname,
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
new_addr) < 0) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
if (type == WPA_IF_AP_BSS) {
- if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+ {
nl80211_remove_iface(drv, ifidx);
os_free(new_bss);
return -1;
#ifdef HOSTAPD
if (bss->added_if_into_bridge) {
- if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
- < 0)
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
if (bss->added_bridge) {
- if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
bss->brname, strerror(errno));
static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
- u64 *cookie_out)
+ u64 *cookie_out, int no_cck)
{
struct nl_msg *msg;
u64 cookie;
if (wait)
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+ if (no_cck)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
+
NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
cookie = 0;
unsigned int wait_time,
const u8 *dst, const u8 *src,
const u8 *bssid,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len,
+ int no_cck)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_hdr *hdr;
wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
- "wait=%d ms)", drv->ifindex, wait_time);
+ "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck);
buf = os_zalloc(24 + data_len);
if (buf == NULL)
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
if (is_ap_interface(drv->nlmode))
- ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+ ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len,
+ 0);
else
ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
24 + data_len,
- &drv->send_action_cookie);
+ &drv->send_action_cookie,
+ no_cck);
os_free(buf);
return ret;
band = nla_nest_start(msg, NL80211_BAND_2GHZ);
if (!band)
goto nla_put_failure;
- NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
- "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ if (disabled) {
+ NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ }
nla_nest_end(msg, band);
nla_nest_end(msg, bands);
}
-static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- drv->disable_11b_rates = disabled;
- return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
-}
-
-
static int wpa_driver_nl80211_deinit_ap(void *priv)
{
struct i802_bss *bss = priv;
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
"resume event");
}
size_t data_len;
u8 own_addr[ETH_ALEN];
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ own_addr) < 0)
return -1;
if (action != 1) {
ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
drv->bssid, own_addr, drv->bssid,
- data, data_len);
+ data, data_len, 0);
os_free(data);
return ret;
}
+static int wpa_driver_nl80211_shared_freq(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct wpa_driver_nl80211_data *driver;
+ int freq = 0;
+
+ /*
+ * If the same PHY is in connected state with some other interface,
+ * then retrieve the assoc freq.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s",
+ drv->phyname);
+
+ dl_list_for_each(driver, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (drv == driver ||
+ os_strcmp(drv->phyname, driver->phyname) != 0 ||
+ !driver->associated)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
+ MACSTR,
+ driver->phyname, driver->first_bss.ifname,
+ MAC2STR(driver->addr));
+ freq = nl80211_get_assoc_freq(driver);
+ wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
+ drv->phyname, freq);
+ }
+
+ if (!freq)
+ wpa_printf(MSG_DEBUG, "nl80211: No shared interface for "
+ "PHY (%s) in associated state", drv->phyname);
+
+ return freq;
+}
+
+
static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
int encrypt)
{
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
+ global->ioctl_sock = -1;
dl_list_init(&global->interfaces);
global->if_add_ifindex = -1;
if (wpa_driver_nl80211_init_nl_global(global) < 0)
goto err;
+ global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (global->ioctl_sock < 0) {
+ perror("socket(PF_INET,SOCK_DGRAM)");
+ goto err;
+ }
+
return global;
err:
if (global->netlink)
netlink_deinit(global->netlink);
- if (global->nl80211)
- genl_family_put(global->nl80211);
nl_destroy_handles(&global->nl);
if (global->nl_cb)
nl_cb_put(global->nl_cb);
+ if (global->ioctl_sock >= 0)
+ close(global->ioctl_sock);
+
os_free(global);
}
}
-static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
- int qos)
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+ const u8 *addr, int qos)
{
- struct i802_bss *bss = priv;
+ /* send data frame to poll STA and check whether
+ * this frame is ACKed */
struct {
struct ieee80211_hdr hdr;
u16 qos_ctl;
os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
- if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size) < 0)
+ if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0) < 0)
wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
"send poll frame");
}
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!drv->poll_command_supported) {
+ nl80211_send_null_frame(bss, own_addr, addr, qos);
+ return;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ send_and_recv_msgs(drv, msg, NULL, NULL);
+ return;
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len)
+{
+ 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))
+ return -EOPNOTSUPP;
+
+ if (!dst)
+ return -EINVAL;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
+ NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
+ NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ enum nl80211_tdls_operation nl80211_oper;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ switch (oper) {
+ case TDLS_DISCOVERY_REQ:
+ nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+ break;
+ case TDLS_SETUP:
+ nl80211_oper = NL80211_TDLS_SETUP;
+ break;
+ case TDLS_TEARDOWN:
+ nl80211_oper = NL80211_TDLS_TEARDOWN;
+ break;
+ case TDLS_ENABLE_LINK:
+ nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+ break;
+ case TDLS_DISABLE_LINK:
+ nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+ break;
+ case TDLS_ENABLE:
+ return 0;
+ case TDLS_DISABLE:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+#endif /* CONFIG TDLS */
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.cancel_remain_on_channel =
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = wpa_driver_nl80211_probe_req_report,
- .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,
.signal_poll = nl80211_signal_poll,
.send_frame = nl80211_send_frame,
+ .shared_freq = wpa_driver_nl80211_shared_freq,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
.add_pmkid = nl80211_add_pmkid,
.flush_pmkid = nl80211_flush_pmkid,
.set_rekey_info = nl80211_set_rekey_info,
.poll_client = nl80211_poll_client,
+#ifdef CONFIG_TDLS
+ .send_tdls_mgmt = nl80211_send_tdls_mgmt,
+ .tdls_oper = nl80211_tdls_oper,
+#endif /* CONFIG_TDLS */
};