struct i802_bss *next;
int ifindex;
char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
unsigned int beacon_set:1;
+ unsigned int added_if_into_bridge:1;
+ unsigned int added_bridge:1;
};
struct wpa_driver_nl80211_data {
void *ctx;
struct netlink_data *netlink;
int ioctl_sock; /* socket for ioctl() use */
- char brname[IFNAMSIZ];
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 pending_remain_on_chan:1;
- unsigned int added_bridge:1;
- unsigned int added_if_into_bridge:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
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);
+
+
+struct nl80211_bss_info_arg {
+ struct wpa_driver_nl80211_data *drv;
+ struct wpa_scan_results *res;
+ unsigned int assoc_freq;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg);
+
/* nl80211 code */
static int ack_handler(struct nl_msg *msg, void *arg)
}
+static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_bss_info_arg arg;
+
+ os_memset(&arg, 0, sizeof(arg));
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+ NL80211_CMD_GET_SCAN, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ arg.drv = drv;
+ ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+ msg = NULL;
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
+ "associated BSS from scan results: %u MHz",
+ arg.assoc_freq);
+ return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+nla_put_failure:
+ nlmsg_free(msg);
+ return drv->assoc_freq;
+}
+
+
static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
const u8 *frame, size_t len)
{
event.assoc_info.resp_ies_len = nla_len(resp_ie);
}
+ event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
};
- struct signal_change *sig_change = arg;
+ struct wpa_signal_info *sig_change = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- struct signal_change *sig)
+ struct wpa_signal_info *sig)
{
struct nl_msg *msg;
[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
};
- struct signal_change *sig_change = arg;
+ struct wpa_signal_info *sig_change = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
- struct signal_change *sig_change)
+ struct wpa_signal_info *sig_change)
{
struct nl_msg *msg;
struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
enum nl80211_cqm_rssi_threshold_event event;
union wpa_event_data ed;
- struct signal_change sig;
+ struct wpa_signal_info sig;
int res;
if (tb[NL80211_ATTR_CQM] == NULL ||
return;
addr = nla_data(tb[NL80211_ATTR_MAC]);
wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+ if (drv->nlmode == NL80211_IFTYPE_AP &&
+ drv->no_monitor_iface_capab) {
+ u8 *ies = NULL;
+ size_t ies_len = 0;
+ if (tb[NL80211_ATTR_IE]) {
+ ies = nla_data(tb[NL80211_ATTR_IE]);
+ ies_len = nla_len(tb[NL80211_ATTR_IE]);
+ }
+ wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+ drv_event_assoc(drv->ctx, addr, ies, ies_len, 0);
+ return;
+ }
+
if (drv->nlmode != NL80211_IFTYPE_ADHOC)
return;
}
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u8 *addr;
+ union wpa_event_data data;
+
+ if (tb[NL80211_ATTR_MAC] == NULL)
+ return;
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+ MAC2STR(addr));
+
+ if (drv->nlmode == NL80211_IFTYPE_AP &&
+ drv->no_monitor_iface_capab) {
+ drv_event_disassoc(drv->ctx, addr);
+ return;
+ }
+
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
static int process_event(struct nl_msg *msg, void *arg)
{
struct wpa_driver_nl80211_data *drv = arg;
case NL80211_CMD_NEW_STATION:
nl80211_new_station_event(drv, tb);
break;
+ case NL80211_CMD_DEL_STATION:
+ nl80211_del_station_event(drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
}
-#ifndef HOSTAPD
struct wiphy_info_data {
int max_scan_ssids;
int ap_supported;
+ int p2p_supported;
int auth_supported;
int connect_supported;
int offchan_tx_supported;
+ int max_remain_on_chan;
};
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wiphy_info_data *info = arg;
+ int p2p_go_supported = 0, p2p_client_supported = 0;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
int i;
nla_for_each_nested(nl_mode,
tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
- if (nl_mode->nla_type == NL80211_IFTYPE_AP) {
+ switch (nla_type(nl_mode)) {
+ case NL80211_IFTYPE_AP:
info->ap_supported = 1;
break;
+ case NL80211_IFTYPE_P2P_GO:
+ p2p_go_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ p2p_client_supported = 1;
+ break;
}
}
}
+ info->p2p_supported = p2p_go_supported && p2p_client_supported;
+
if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
struct nlattr *nl_cmd;
int i;
if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK])
info->offchan_tx_supported = 1;
+ if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
+ info->max_remain_on_chan =
+ nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
return NL_SKIP;
}
struct nl_msg *msg;
os_memset(info, 0, sizeof(*info));
+
+ /* default to 5000 since early versions of mac80211 don't set it */
+ info->max_remain_on_chan = 5000;
+
msg = nlmsg_alloc();
if (!msg)
return -1;
drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
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;
+ if (info.p2p_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ drv->capa.max_remain_on_chan = info.max_remain_on_chan;
return 0;
}
-#endif /* HOSTAPD */
static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
if (drv == NULL)
return NULL;
drv->global = global_priv;
- if (drv->global)
- dl_list_add(&drv->global->interfaces, &drv->list);
drv->ctx = ctx;
bss = &drv->first_bss;
bss->drv = drv;
if (wpa_driver_nl80211_finish_drv_init(drv))
goto failed;
+ if (drv->global)
+ dl_list_add(&drv->global->interfaces, &drv->list);
+
return bss;
failed:
}
}
- if (wpa_driver_nl80211_capa(drv))
- return -1;
-
netlink_send_oper_ifla(drv->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
#endif /* HOSTAPD */
+ if (wpa_driver_nl80211_capa(drv))
+ return -1;
+
if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr))
return -1;
if (drv->nl_handle_preq)
wpa_driver_nl80211_probe_req_report(bss, 0);
- if (drv->added_if_into_bridge) {
- if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
< 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
- bss->ifname, drv->brname, strerror(errno));
+ bss->ifname, bss->brname, strerror(errno));
}
- if (drv->added_bridge) {
- if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+ if (bss->added_bridge) {
+ if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
- drv->brname, strerror(errno));
+ bss->brname, strerror(errno));
}
nl80211_remove_monitor_interface(drv);
}
-struct nl80211_bss_info_arg {
- struct wpa_driver_nl80211_data *drv;
- struct wpa_scan_results *res;
-};
-
static int bss_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
const u8 *ie, *beacon_ie;
size_t ie_len, beacon_ie_len;
u8 *pos;
+ size_t i;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
bss_policy))
return NL_SKIP;
+ if (bss[NL80211_BSS_STATUS]) {
+ enum nl80211_bss_status status;
+ status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ _arg->assoc_freq =
+ nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+ _arg->assoc_freq);
+ }
+ }
+ if (!res)
+ return NL_SKIP;
if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
}
}
+ /*
+ * cfg80211 maintains separate BSS table entries for APs if the same
+ * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+ * not use frequency as a separate key in the BSS table, so filter out
+ * duplicated entries. Prefer associated BSS entry in such a case in
+ * order to get the correct frequency into the BSS table.
+ */
+ for (i = 0; i < res->num; i++) {
+ const u8 *s1, *s2;
+ if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+ continue;
+
+ s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+ res->res[i]->ie_len, WLAN_EID_SSID);
+ s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+ if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+ os_memcmp(s1, s2, 2 + s1[1]) != 0)
+ continue;
+
+ /* Same BSSID,SSID was already included in scan results */
+ wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+ "for " MACSTR, MAC2STR(r->bssid));
+
+ if ((r->flags & WPA_SCAN_ASSOCIATED) &&
+ !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
+ os_free(res->res[i]);
+ res->res[i] = r;
+ } else
+ os_free(r);
+ return NL_SKIP;
+ }
+
tmp = os_realloc(res->res,
(res->num + 1) * sizeof(struct wpa_scan_res *));
if (tmp == NULL) {
if (seq && seq_len)
NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
- if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
- {
+ if (addr && !is_broadcast_ether_addr(addr)) {
wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
NL80211_KEYTYPE_GROUP);
}
+ } else if (addr && is_broadcast_ether_addr(addr)) {
+ struct nl_msg *types;
+ int err;
+ wpa_printf(MSG_DEBUG, " broadcast key");
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
}
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
*/
if (ret || !set_tx || alg == WPA_ALG_NONE)
return ret;
-#ifdef HOSTAPD
- if (addr)
- return ret;
-#else /* HOSTAPD */
- if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+ if (drv->nlmode == NL80211_IFTYPE_AP && addr &&
+ !is_broadcast_ether_addr(addr))
return ret;
-#endif /* HOSTAPD */
msg = nlmsg_alloc();
if (!msg)
NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
else
NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+ if (addr && is_broadcast_ether_addr(addr)) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ } else if (addr) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ }
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret == -ENOENT)
wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
__func__, MAC2STR(addr), reason_code);
drv->associated = 0;
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC)
+ return nl80211_leave_ibss(drv);
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
reason_code, 0);
}
drv->associated = 0;
os_memset(drv->auth_bssid, 0, ETH_ALEN);
/* FIX: IBSS mode */
- if (drv->nlmode != NL80211_IFTYPE_STATION)
- wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
-
- if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
+ if (drv->nlmode != NL80211_IFTYPE_STATION &&
+ wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
return -1;
retry:
if (encrypt)
rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+ if (drv->monitor_sock < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+ "for %s", __func__);
+ return -1;
+ }
+
res = sendmsg(drv->monitor_sock, &msg, 0);
if (res < 0) {
wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
}
+static u32 sta_flags_nl80211(int flags)
+{
+ u32 f = 0;
+
+ if (flags & WPA_STA_AUTHORIZED)
+ f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (flags & WPA_STA_WMM)
+ f |= BIT(NL80211_STA_FLAG_WME);
+ if (flags & WPA_STA_SHORT_PREAMBLE)
+ f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+ if (flags & WPA_STA_MFP)
+ f |= BIT(NL80211_STA_FLAG_MFP);
+
+ return f;
+}
+
+
static int wpa_driver_nl80211_sta_add(void *priv,
struct hostapd_sta_add_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ struct nl80211_sta_flag_update upd;
int ret = -ENOBUFS;
msg = nlmsg_alloc();
params->ht_capabilities);
}
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = sta_flags_nl80211(params->flags);
+ upd.set = upd.mask;
+ NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
0);
+ if (drv->monitor_ifidx == -EOPNOTSUPP) {
+ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+ "monitor interface type - try to run without it");
+ drv->no_monitor_iface_capab = 1;
+ }
+
if (drv->monitor_ifidx < 0)
return -1;
static int wpa_driver_nl80211_hapd_send_eapol(
void *priv, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt, const u8 *own_addr)
+ size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
size_t len;
u8 *pos;
int res;
-#if 0 /* FIX */
- int qos = sta->flags & WPA_STA_WMM;
-#else
- int qos = 0;
-#endif
+ int qos = flags & WPA_STA_WMM;
len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
data_len;
hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
if (encrypt)
hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-#if 0 /* To be enabled if qos determination is added above */
if (qos) {
hdr->frame_control |=
host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
}
-#endif
memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
pos = (u8 *) (hdr + 1);
-#if 0 /* To be enabled if qos determination is added above */
if (qos) {
/* add an empty QoS header if needed */
pos[0] = 0;
pos[1] = 0;
pos += 2;
}
-#endif
memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
pos += sizeof(rfc1042_header);
}
-static u32 sta_flags_nl80211(int flags)
-{
- u32 f = 0;
-
- if (flags & WPA_STA_AUTHORIZED)
- f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
- if (flags & WPA_STA_WMM)
- f |= BIT(NL80211_STA_FLAG_WME);
- if (flags & WPA_STA_SHORT_PREAMBLE)
- f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
- if (flags & WPA_STA_MFP)
- f |= BIT(NL80211_STA_FLAG_MFP);
-
- return f;
-}
-
-
static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
int total_flags,
int flags_or, int flags_and)
struct nl_msg *msg;
enum nl80211_auth_type type;
int ret = 0;
+ int algs;
msg = nlmsg_alloc();
if (!msg)
NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie);
+ algs = 0;
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+ algs++;
+ if (algs > 1) {
+ wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
+ "selection");
+ goto skip_auth_type;
+ }
+
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+skip_auth_type:
if (params->wpa_ie && params->wpa_ie_len) {
enum nl80211_wpa_versions ver;
done:
if (!ret && nlmode == NL80211_IFTYPE_AP) {
/* Setup additional AP mode functionality if needed */
- if (drv->monitor_ifidx < 0 &&
- nl80211_create_monitor_interface(drv))
+ if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv) &&
+ !drv->no_monitor_iface_capab)
return -1;
} else if (!ret && nlmode != NL80211_IFTYPE_AP) {
/* Remove additional AP mode functionality */
}
-#ifdef HOSTAPD
-
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
- int i;
- int *old;
-
- wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
- ifidx);
- for (i = 0; i < drv->num_if_indices; i++) {
- if (drv->if_indices[i] == 0) {
- drv->if_indices[i] = ifidx;
- return;
- }
- }
-
- if (drv->if_indices != drv->default_if_indices)
- old = drv->if_indices;
- else
- old = NULL;
-
- drv->if_indices = os_realloc(old,
- sizeof(int) * (drv->num_if_indices + 1));
- if (!drv->if_indices) {
- if (!old)
- drv->if_indices = drv->default_if_indices;
- else
- drv->if_indices = old;
- wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
- "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++;
-}
-
-
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
{
- int i;
-
- for (i = 0; i < drv->num_if_indices; i++) {
- if (drv->if_indices[i] == ifidx) {
- drv->if_indices[i] = 0;
- break;
- }
- }
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+ freq->sec_channel_offset);
}
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
- int i;
-
- for (i = 0; i < drv->num_if_indices; i++)
- if (drv->if_indices[i] == ifidx)
- return 1;
-
- return 0;
-}
-
+#if defined(HOSTAPD) || defined(CONFIG_AP)
static inline int min_int(int a, int b)
{
return -ENOBUFS;
}
-#endif /* HOSTAPD */
-
-
-/* Set kernel driver on given frequency (MHz) */
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
- freq->sec_channel_offset);
-}
-
-
-#ifdef HOSTAPD
static int i802_set_rts(void *priv, int rts)
{
}
-static int i802_set_bss(void *priv, int cts, int preamble, int slot)
+static int i802_set_bss(void *priv, int cts, int preamble, int slot,
+ int ht_opmode)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
if (slot >= 0)
NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
-
+ if (ht_opmode >= 0)
+ NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
return send_and_recv_msgs(drv, msg, NULL, NULL);
static int i802_set_cts_protect(void *priv, int value)
{
- return i802_set_bss(priv, value, -1, -1);
+ return i802_set_bss(priv, value, -1, -1, -1);
}
static int i802_set_preamble(void *priv, int value)
{
- return i802_set_bss(priv, -1, value, -1);
+ return i802_set_bss(priv, -1, value, -1, -1);
}
static int i802_set_short_slot_time(void *priv, int value)
{
- return i802_set_bss(priv, -1, -1, value);
+ return i802_set_bss(priv, -1, -1, value, -1);
}
}
-static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
- const char *bridge_ifname)
+static int i802_set_ht_params(void *priv, const u8 *ht_capab,
+ size_t ht_capab_len, const u8 *ht_oper,
+ size_t ht_oper_len)
{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- char name[IFNAMSIZ + 1];
-
- os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
- wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
- " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
- if (val) {
- if (!if_nametoindex(name)) {
- if (nl80211_create_iface(drv, name,
- NL80211_IFTYPE_AP_VLAN,
- NULL, 1) < 0)
- return -1;
- if (bridge_ifname &&
- linux_br_add_if(drv->ioctl_sock, bridge_ifname,
- name) < 0)
- return -1;
- }
- linux_set_iface_flags(drv->ioctl_sock, name, 1);
- return i802_set_sta_vlan(priv, addr, name, 0);
- } else {
- i802_set_sta_vlan(priv, addr, bss->ifname, 0);
- return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
- name);
- }
-}
-
-
-static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct wpa_driver_nl80211_data *drv = eloop_ctx;
- struct sockaddr_ll lladdr;
- unsigned char buf[3000];
- int len;
- socklen_t fromlen = sizeof(lladdr);
-
- len = recvfrom(sock, buf, sizeof(buf), 0,
- (struct sockaddr *)&lladdr, &fromlen);
- if (len < 0) {
- perror("recv");
- return;
- }
-
- if (have_ifidx(drv, lladdr.sll_ifindex))
- drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+ if (ht_oper_len >= 6) {
+ /* ht opmode uses 16bit in octet 5 & 6 */
+ u16 ht_opmode = le_to_host16(((u16 *) ht_oper)[2]);
+ return i802_set_bss(priv, -1, -1, -1, ht_opmode);
+ } else
+ return -1;
}
sizeof(mgmt.u.disassoc));
}
+#endif /* HOSTAPD || CONFIG_AP */
+
+#ifdef HOSTAPD
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+ int *old;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+ ifidx);
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == 0) {
+ drv->if_indices[i] = ifidx;
+ return;
+ }
+ }
+
+ if (drv->if_indices != drv->default_if_indices)
+ old = drv->if_indices;
+ else
+ old = NULL;
+
+ drv->if_indices = os_realloc(old,
+ sizeof(int) * (drv->num_if_indices + 1));
+ if (!drv->if_indices) {
+ if (!old)
+ drv->if_indices = drv->default_if_indices;
+ else
+ drv->if_indices = old;
+ wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+ "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++;
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == ifidx) {
+ drv->if_indices[i] = 0;
+ break;
+ }
+ }
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++)
+ if (drv->if_indices[i] == ifidx)
+ return 1;
+
+ return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ char name[IFNAMSIZ + 1];
+
+ os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ if (val) {
+ if (!if_nametoindex(name)) {
+ if (nl80211_create_iface(drv, name,
+ NL80211_IFTYPE_AP_VLAN,
+ NULL, 1) < 0)
+ return -1;
+ if (bridge_ifname &&
+ linux_br_add_if(drv->ioctl_sock, bridge_ifname,
+ name) < 0)
+ return -1;
+ }
+ linux_set_iface_flags(drv->ioctl_sock, name, 1);
+ return i802_set_sta_vlan(priv, addr, name, 0);
+ } else {
+ i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+ return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+ name);
+ }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ struct sockaddr_ll lladdr;
+ unsigned char buf[3000];
+ int len;
+ socklen_t fromlen = sizeof(lladdr);
+
+ len = recvfrom(sock, buf, sizeof(buf), 0,
+ (struct sockaddr *)&lladdr, &fromlen);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ if (have_ifidx(drv, lladdr.sll_ifindex))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
const char *brname, const char *ifname)
{
int ifindex;
char in_br[IFNAMSIZ];
- os_strlcpy(drv->brname, brname, IFNAMSIZ);
+ os_strlcpy(bss->brname, brname, IFNAMSIZ);
ifindex = if_nametoindex(brname);
if (ifindex == 0) {
/*
brname, strerror(errno));
return -1;
}
- drv->added_bridge = 1;
+ bss->added_bridge = 1;
add_ifidx(drv, if_nametoindex(brname));
}
ifname, brname, strerror(errno));
return -1;
}
- drv->added_if_into_bridge = 1;
+ bss->added_if_into_bridge = 1;
return 0;
}
return NULL;
drv = bss->drv;
+ drv->nlmode = NL80211_IFTYPE_AP;
if (linux_br_get(brname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
params->ifname, brname);
}
if (params->num_bridge && params->bridge[0] &&
- i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+ i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
goto failed;
if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
{
switch (type) {
case WPA_IF_STATION:
+ return NL80211_IFTYPE_STATION;
case WPA_IF_P2P_CLIENT:
case WPA_IF_P2P_GROUP:
- return NL80211_IFTYPE_STATION;
+ return NL80211_IFTYPE_P2P_CLIENT;
case WPA_IF_AP_VLAN:
return NL80211_IFTYPE_AP_VLAN;
case WPA_IF_AP_BSS:
- case WPA_IF_P2P_GO:
return NL80211_IFTYPE_AP;
+ case WPA_IF_P2P_GO:
+ return NL80211_IFTYPE_P2P_GO;
}
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,
- char *force_ifname, u8 *if_addr)
+ char *force_ifname, u8 *if_addr,
+ const char *bridge)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
#endif /* CONFIG_P2P */
#ifdef HOSTAPD
+ if (bridge &&
+ i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+ "interface %s to a bridge %s", ifname, bridge);
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+
if (type == WPA_IF_AP_BSS) {
if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
nl80211_remove_iface(drv, ifidx);
__func__, type, ifname, ifindex);
if (ifindex <= 0)
return -1;
+
+#ifdef HOSTAPD
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->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)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+#endif /* HOSTAPD */
+
nl80211_remove_iface(drv, ifindex);
#ifdef HOSTAPD
return 0;
if (bss != &drv->first_bss) {
- struct i802_bss *tbss = &drv->first_bss;
-
- while (tbss) {
- if (tbss->next != bss)
- continue;
+ struct i802_bss *tbss;
- tbss->next = bss->next;
- os_free(bss);
- break;
+ for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next == bss) {
+ tbss->next = bss->next;
+ os_free(bss);
+ bss = NULL;
+ break;
+ }
}
+ if (bss)
+ wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+ "BSS %p in the list", __func__, bss);
}
#endif /* HOSTAPD */
}
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+
+ os_memset(si, 0, sizeof(*si));
+ res = nl80211_get_link_signal(drv, si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_link_noise(drv, si);
+}
+
+
static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
int encrypt)
{
}
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(bss->drv->nl80211), 0, 0,
+ cmd, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ if (pmkid)
+ NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid);
+ if (bssid)
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+ return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+ MAC2STR(bssid));
+ return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+ return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
#ifdef HOSTAPD
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
+ .set_wds_sta = i802_set_wds_sta,
+#endif /* HOSTAPD */
+#if defined(HOSTAPD) || defined(CONFIG_AP)
.get_seqnum = i802_get_seqnum,
.flush = i802_flush,
.read_sta_data = i802_read_sta_data,
- .sta_deauth = i802_sta_deauth,
- .sta_disassoc = i802_sta_disassoc,
.get_inact_sec = i802_get_inact_sec,
.sta_clear_stats = i802_sta_clear_stats,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
- .set_rate_sets = i802_set_rate_sets,
.set_cts_protect = i802_set_cts_protect,
.set_preamble = i802_set_preamble,
.set_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan,
- .set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
+ .set_ht_params = i802_set_ht_params,
+ .set_rate_sets = i802_set_rate_sets,
+ .sta_deauth = i802_sta_deauth,
+ .sta_disassoc = i802_sta_disassoc,
+#endif /* HOSTAPD || CONFIG_AP */
.set_freq = i802_set_freq,
.send_action = wpa_driver_nl80211_send_action,
.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
.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,
.set_intra_bss = nl80211_set_intra_bss,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
+ .add_pmkid = nl80211_add_pmkid,
+ .remove_pmkid = nl80211_remove_pmkid,
+ .flush_pmkid = nl80211_flush_pmkid,
};