X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=352ecd5907b31e2a91fc94976dd6c13458b5a119;hb=33c5deb816f51d80b7ac6aa44b224e6b699ef85c;hp=cbed985306a631c09ccf192edd6048ade94a83db;hpb=e3802622db285eabc5a97b4e2dbccb1db92a1721;p=libeap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index cbed985..352ecd5 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "nl80211_copy.h" @@ -33,13 +34,54 @@ #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" +#include "rfkill.h" #include "driver.h" #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock -#define nl_handle_alloc_cb nl_socket_alloc_cb -#define nl_handle_destroy nl_socket_free +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#else +/* + * libnl 1.1 has a bug, it tries to allocate socket numbers densely + * but when you free a socket again it will mess up its bitmap and + * and use the wrong number the next time it needs a socket ID. + * Therefore, we wrap the handle alloc/destroy and add our own pid + * accounting. + */ +static uint32_t port_bitmap[32] = { 0 }; + +static struct nl_handle *nl80211_handle_alloc(void *cb) +{ + struct nl_handle *handle; + uint32_t pid = getpid() & 0x3FFFFF; + int i; + + handle = nl_handle_alloc_cb(cb); + + for (i = 0; i < 1024; i++) { + if (port_bitmap[i / 32] & (1 << (i % 32))) + continue; + port_bitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + + return handle; +} + +static void nl80211_handle_destroy(struct nl_handle *handle) +{ + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + port_bitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} #endif /* CONFIG_LIBNL20 */ @@ -72,6 +114,8 @@ struct wpa_driver_nl80211_data { char brname[IFNAMSIZ]; int ifindex; int if_removed; + int if_disabled; + struct rfkill_data *rfkill; struct wpa_driver_capa capa; int has_capability; @@ -81,8 +125,10 @@ struct wpa_driver_nl80211_data { struct nl_handle *nl_handle; struct nl_handle *nl_handle_event; + struct nl_handle *nl_handle_preq; struct nl_cache *nl_cache; struct nl_cache *nl_cache_event; + struct nl_cache *nl_cache_preq; struct nl_cb *nl_cb; struct genl_family *nl80211; @@ -97,17 +143,17 @@ struct wpa_driver_nl80211_data { int monitor_sock; int monitor_ifidx; - int probe_req_report; int disable_11b_rates; unsigned int pending_remain_on_chan:1; - unsigned int pending_send_action:1; unsigned int added_bridge:1; unsigned int added_if_into_bridge:1; u64 remain_on_chan_cookie; u64 send_action_cookie; + unsigned int last_mgmt_freq; + struct wpa_driver_scan_filter *filter_ssids; size_t num_filter_ssids; @@ -136,18 +182,26 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); +static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv, + unsigned int freq, const u8 *buf, + size_t buf_len, u64 *cookie); +static int wpa_driver_nl80211_probe_req_report(void *priv, int report); #ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int wpa_driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname); +#else /* HOSTAPD */ +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + return 0; +} #endif /* HOSTAPD */ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); -static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, - void *timeout_ctx); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); @@ -389,10 +443,12 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct wpa_driver_nl80211_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + u32 brid = 0; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len)) { - wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", - ifi->ifi_index); + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) && + !have_ifidx(drv, ifi->ifi_index)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " + "ifindex %d", ifi->ifi_index); return; } @@ -403,6 +459,19 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "nl80211: Interface down"); + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() @@ -424,9 +493,21 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); - } + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); attr = RTA_NEXT(attr, attrlen); } + +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been added to bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", + brid, namebuf); + add_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } @@ -437,6 +518,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct wpa_driver_nl80211_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + u32 brid = 0; attrlen = len; attr = (struct rtattr *) buf; @@ -448,9 +530,21 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); - } + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); attr = RTA_NEXT(attr, attrlen); } + +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been removed from bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " + "%s", brid, namebuf); + del_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } @@ -597,8 +691,8 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, } -static void mlme_event_action(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, const u8 *frame, size_t len) +static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; @@ -614,15 +708,23 @@ static void mlme_event_action(struct wpa_driver_nl80211_data *drv, stype = WLAN_FC_GET_STYPE(fc); os_memset(&event, 0, sizeof(event)); - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category + 1; - event.rx_action.len = frame + len - event.rx_action.data; - if (freq) + if (freq) { event.rx_action.freq = nla_get_u32(freq); - wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + drv->last_mgmt_freq = event.rx_action.freq; + } + if (stype == WLAN_FC_STYPE_ACTION) { + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category + 1; + event.rx_action.len = frame + len - event.rx_action.data; + wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + } else { + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + } } @@ -639,10 +741,11 @@ static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, return; cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s", + wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s " + "(ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)"); + " (match)" : " (unknown)", ack != NULL); if (cookie_val != drv->send_action_cookie) return; @@ -660,6 +763,63 @@ static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, } +static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + const u8 *bssid = NULL; + u16 reason_code = 0; + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; + + if (drv->associated != 0 && + os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { + /* + * We have presumably received this deauth as a + * response to a clear_state_mismatch() outgoing + * deauth. Don't let it take us offline! + */ + wpa_printf(MSG_DEBUG, "nl80211: Deauth received " + "from Unknown BSSID " MACSTR " -- ignoring", + MAC2STR(bssid)); + return; + } + } + + drv->associated = 0; + os_memset(&event, 0, sizeof(event)); + + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_DISASSOC) { + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } + } else { + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + static void mlme_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, @@ -689,17 +849,17 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DEAUTHENTICATE: - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL); + mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, + nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DISASSOCIATE: - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, + nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION: - mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); + case NL80211_CMD_FRAME: + mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION_TX_STATUS: + case NL80211_CMD_FRAME_TX_STATUS: mlme_event_action_tx_status(drv, cookie, nla_data(frame), nla_len(frame), ack); break; @@ -850,6 +1010,144 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } +static int get_link_signal(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + }; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + struct signal_change *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; + + sig_change->current_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, + sinfo[NL80211_STA_INFO_TX_BITRATE], + rate_policy)) { + sig_change->current_txrate = 0; + } else { + if (rinfo[NL80211_RATE_INFO_BITRATE]) { + sig_change->current_txrate = + nla_get_u16(rinfo[ + NL80211_RATE_INFO_BITRATE]) * 100; + } + } + } + + return NL_SKIP; +} + + +static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct signal_change *sig) +{ + struct nl_msg *msg; + + sig->current_signal = -9999; + sig->current_txrate = 0; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + return send_and_recv_msgs(drv, msg, get_link_signal, sig); + nla_put_failure: + return -ENOBUFS; +} + + +static int get_link_noise(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 signal_change *sig_change = arg; + + 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_FREQUENCY]) + return NL_SKIP; + + if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + sig_change->frequency) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + sig_change->current_noise = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + + return NL_SKIP; +} + + +static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct signal_change *sig_change) +{ + struct nl_msg *msg; + + sig_change->current_noise = 9999; + sig_change->frequency = drv->assoc_freq; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + NLM_F_DUMP, NL80211_CMD_GET_SURVEY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); + nla_put_failure: + return -ENOBUFS; +} + + static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { @@ -861,6 +1159,8 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; union wpa_event_data ed; + struct signal_change sig; + int res; if (tb[NL80211_ATTR_CQM] == NULL || nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], @@ -886,6 +1186,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, } else return; + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig.current_signal; + ed.signal_change.current_txrate = sig.current_txrate; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", + sig.current_signal, sig.current_txrate); + } + + res = nl80211_get_link_noise(drv, &sig); + if (res == 0) { + ed.signal_change.current_noise = sig.current_noise; + wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", + sig.current_noise); + } + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); } @@ -895,13 +1210,14 @@ static int process_event(struct nl_msg *msg, void *arg) struct wpa_driver_nl80211_data *drv = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data data; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex) { + if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" " for foreign interface (ifindex %d)", gnlh->cmd, ifindex); @@ -942,8 +1258,8 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_ACTION: - case NL80211_CMD_ACTION_TX_STATUS: + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], @@ -968,7 +1284,11 @@ static int process_event(struct nl_msg *msg, void *arg) break; } drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_REASON_CODE]) + data.disassoc_info.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: mlme_event_michael_mic_failure(drv, tb); @@ -985,6 +1305,16 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_NOTIFY_CQM: nl80211_cqm_event(drv, tb); break; + case NL80211_CMD_REG_CHANGE: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + break; + case NL80211_CMD_REG_BEACON_HINT: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); @@ -996,7 +1326,7 @@ static int process_event(struct nl_msg *msg, void *arg) static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) + void *handle) { struct nl_cb *cb; struct wpa_driver_nl80211_data *drv = eloop_ctx; @@ -1008,7 +1338,7 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, return; nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); - nl_recvmsgs(drv->nl_handle_event, cb); + nl_recvmsgs(handle, cb); nl_cb_put(cb); } @@ -1157,6 +1487,7 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) } drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; drv->capa.max_remain_on_chan = 5000; return 0; @@ -1164,8 +1495,7 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) #endif /* HOSTAPD */ -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, - void *ctx) +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) { int ret; @@ -1178,14 +1508,14 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, goto err1; } - drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + drv->nl_handle = nl80211_handle_alloc(drv->nl_cb); if (drv->nl_handle == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks"); goto err2; } - drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb); + drv->nl_handle_event = nl80211_handle_alloc(drv->nl_cb); if (drv->nl_handle_event == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks (event)"); @@ -1258,8 +1588,19 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, goto err4; } + ret = nl_get_multicast_id(drv, "nl80211", "regulatory"); + if (ret >= 0) + ret = nl_socket_add_membership(drv->nl_handle_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for regulatory events: %d (%s)", + ret, strerror(-ret)); + /* Continue without regulatory events */ + } + eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), - wpa_driver_nl80211_event_receive, drv, ctx); + wpa_driver_nl80211_event_receive, drv, + drv->nl_handle_event); return 0; @@ -1268,9 +1609,9 @@ err4: err3b: nl_cache_free(drv->nl_cache); err3: - nl_handle_destroy(drv->nl_handle_event); + nl80211_handle_destroy(drv->nl_handle_event); err2b: - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); err2: nl_cb_put(drv->nl_cb); err1: @@ -1278,6 +1619,29 @@ err1: } +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -1289,6 +1653,7 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) { struct wpa_driver_nl80211_data *drv; struct netlink_config *cfg; + struct rfkill_config *rcfg; struct i802_bss *bss; drv = os_zalloc(sizeof(*drv)); @@ -1302,7 +1667,7 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) drv->monitor_sock = -1; drv->ioctl_sock = -1; - if (wpa_driver_nl80211_init_nl(drv, ctx)) { + if (wpa_driver_nl80211_init_nl(drv)) { os_free(drv); return NULL; } @@ -1324,19 +1689,34 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) os_free(cfg); goto failed; } + + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } + if (wpa_driver_nl80211_finish_drv_init(drv)) goto failed; return bss; failed: + rfkill_deinit(drv->rfkill); netlink_deinit(drv->netlink); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); nl_cb_put(drv->nl_cb); eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); @@ -1345,8 +1725,9 @@ failed: } -static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, - const u8 *match, size_t match_len) +static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, + struct nl_handle *nl_handle, + u16 type, const u8 *match, size_t match_len) { struct nl_msg *msg; int ret = -1; @@ -1359,14 +1740,16 @@ static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, NL80211_CMD_REGISTER_ACTION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); - ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL); + ret = send_and_recv(drv, nl_handle, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Register Action command " - "failed: ret=%d (%s)", ret, strerror(-ret)); - wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match", + wpa_printf(MSG_DEBUG, "nl80211: Register frame command " + "failed (type=%u): ret=%d (%s)", + type, ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", match, match_len); goto nla_put_failure; } @@ -1377,8 +1760,42 @@ nla_put_failure: } +static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(drv, drv->nl_handle_event, + type, match, match_len); +} + + static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) { +#ifdef CONFIG_P2P + /* GAS Initial Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0) + return -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0) + return -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0) + return -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0) + return -1; + /* P2P Public Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + return -1; + /* P2P Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + return -1; +#endif /* CONFIG_P2P */ + /* FT Action frames */ if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) return -1; @@ -1390,10 +1807,17 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) } +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { struct i802_bss *bss = &drv->first_bss; + int send_rfkill_event = 0; drv->ifindex = if_nametoindex(bss->ifname); drv->first_bss.ifindex = drv->ifindex; @@ -1405,9 +1829,17 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) } if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_ERROR, "Could not set interface '%s' UP", - bss->ifname); - return -1; + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", + bss->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return -1; + } } if (wpa_driver_nl80211_capa(drv)) @@ -1427,6 +1859,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) */ } + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); + } + return 0; } @@ -1461,6 +1898,8 @@ static void wpa_driver_nl80211_deinit(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + 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) < 0) @@ -1503,6 +1942,7 @@ static void wpa_driver_nl80211_deinit(void *priv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); @@ -1516,13 +1956,10 @@ static void wpa_driver_nl80211_deinit(void *priv) genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); nl_cache_free(drv->nl_cache_event); - nl_handle_destroy(drv->nl_handle); - nl_handle_destroy(drv->nl_handle_event); + nl80211_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle_event); nl_cb_put(drv->nl_cb); - eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); - os_free(drv->filter_ssids); os_free(drv); @@ -2252,7 +2689,8 @@ nla_put_failure: static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, const u8 *addr, int reason_code) { - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, reason_code, 0); @@ -2266,7 +2704,8 @@ static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, reason_code, 0); @@ -2353,10 +2792,6 @@ retry: wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); if (params->ie) NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); - /* - * TODO: if multiple auth_alg options enabled, try them one by one if - * the AP rejects authentication due to unknown auth alg - */ if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -2739,6 +3174,18 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); + if (drv->nlmode == NL80211_IFTYPE_STATION && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + /* + * The use of last_mgmt_freq is a bit of a hack, + * but it works due to the single-threaded nature + * of wpa_supplicant. + */ + return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, + data, data_len, NULL); + } + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { /* @@ -3111,12 +3558,6 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface " - "frame since Probe Request reporting is disabled"); - return; - } - if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { printf("received invalid radiotap frame\n"); return; @@ -3540,6 +3981,9 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { + if (params->p2p) + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) || wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { nl80211_remove_monitor_interface(drv); @@ -3874,6 +4318,9 @@ static int wpa_driver_nl80211_associate( params->prev_bssid); } + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -3923,6 +4370,7 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; int nlmode; + int i; switch (mode) { case 0: @@ -3955,11 +4403,23 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) * take the device down, try to set the mode again, and bring the * device back up. */ - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) { - /* Try to set the mode again while the interface is down */ - ret = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) - ret = -1; + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == + 0) { + /* Try to set the mode again while the interface is + * down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, + 1)) + ret = -1; + if (!ret) + break; + } else + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); } if (!ret) { @@ -4075,7 +4535,9 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) "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++; } @@ -4467,6 +4929,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) @@ -4481,9 +4944,15 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname)); - return send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" + MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", + MAC2STR(addr), ifname, vlan_id, ret, + strerror(-ret)); + } nla_put_failure: - return -ENOBUFS; + return ret; } @@ -4730,7 +5199,7 @@ failed: genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); nl_cb_put(drv->nl_cb); os_free(drv); @@ -4751,10 +5220,13 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( { switch (type) { case WPA_IF_STATION: + case WPA_IF_P2P_CLIENT: + case WPA_IF_P2P_GROUP: return NL80211_IFTYPE_STATION; case WPA_IF_AP_VLAN: return NL80211_IFTYPE_AP_VLAN; case WPA_IF_AP_BSS: + case WPA_IF_P2P_GO: return NL80211_IFTYPE_AP; } return -1; @@ -4865,6 +5337,45 @@ static int cookie_handler(struct nl_msg *msg, void *arg) } +static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv, + unsigned int freq, const u8 *buf, + size_t buf_len, u64 *cookie_out) +{ + struct nl_msg *msg; + u64 cookie; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_FRAME, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; " + "cookie 0x%llx", (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = cookie; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, @@ -4873,10 +5384,8 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; - struct nl_msg *msg; u8 *buf; struct ieee80211_hdr *hdr; - u64 cookie; wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)", drv->ifindex); @@ -4892,44 +5401,13 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); - if (drv->nlmode == NL80211_IFTYPE_AP) { + if (drv->nlmode == NL80211_IFTYPE_AP) ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len); - os_free(buf); - return ret; - } - - msg = nlmsg_alloc(); - if (!msg) { - os_free(buf); - return -1; - } - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_ACTION, 0); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf); - os_free(buf); - buf = NULL; - - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d " - "(%s)", ret, strerror(-ret)); - goto nla_put_failure; - } - wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; " - "cookie 0x%llx", (long long unsigned int) cookie); - drv->send_action_cookie = cookie; - drv->pending_send_action = 1; - ret = 0; + else + ret = nl80211_send_frame_cmd(drv, freq, buf, 24 + data_len, + &drv->send_action_cookie); -nla_put_failure: os_free(buf); - nlmsg_free(msg); return ret; } @@ -4964,7 +5442,8 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d): %d (%s)", freq, ret, strerror(-ret)); + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); nla_put_failure: return -1; } @@ -5007,25 +5486,6 @@ nla_put_failure: } -static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, - void *timeout_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->monitor_ifidx < 0) - return; /* monitor interface already removed */ - - if (drv->nlmode != NL80211_IFTYPE_STATION) - return; /* not in station mode anymore */ - - if (drv->probe_req_report) - return; /* reporting enabled */ - - wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no " - "Probe Request reporting needed anymore"); - nl80211_remove_monitor_interface(drv); -} - - static int wpa_driver_nl80211_probe_req_report(void *priv, int report) { struct i802_bss *bss = priv; @@ -5037,30 +5497,74 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report) drv->nlmode); return -1; } - drv->probe_req_report = report; - if (report) { - eloop_cancel_timeout( - wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); - if (drv->monitor_ifidx < 0 && - nl80211_create_monitor_interface(drv)) - return -1; - } else { - /* - * It takes a while to remove the monitor interface, so try to - * avoid doing this if it is needed again shortly. Instead, - * schedule the interface to be removed later if no need for it - * is seen. - */ - wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface " - "to be removed after 10 seconds of no use"); - eloop_register_timeout( - 10, 0, wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); + if (!report) { + if (drv->nl_handle_preq) { + eloop_unregister_read_sock( + nl_socket_get_fd(drv->nl_handle_preq)); + nl_cache_free(drv->nl_cache_preq); + nl80211_handle_destroy(drv->nl_handle_preq); + drv->nl_handle_preq = NULL; + } + return 0; + } + + if (drv->nl_handle_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " + "already on!"); + return 0; + } + + drv->nl_handle_preq = nl80211_handle_alloc(drv->nl_cb); + if (drv->nl_handle_preq == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate " + "netlink callbacks (preq)"); + goto out_err1; + } + + if (genl_connect(drv->nl_handle_preq)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to " + "generic netlink (preq)"); + goto out_err2; + return -1; + } + +#ifdef CONFIG_LIBNL20 + if (genl_ctrl_alloc_cache(drv->nl_handle_preq, + &drv->nl_cache_preq) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache (preq)"); + goto out_err2; + } +#else /* CONFIG_LIBNL20 */ + drv->nl_cache_preq = genl_ctrl_alloc_cache(drv->nl_handle_preq); + if (drv->nl_cache_preq == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache (preq)"); + goto out_err2; + } +#endif /* CONFIG_LIBNL20 */ + + if (nl80211_register_frame(drv, drv->nl_handle_preq, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_REQ << 4), + NULL, 0) < 0) { + goto out_err3; } + eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_preq), + wpa_driver_nl80211_event_receive, drv, + drv->nl_handle_preq); + return 0; + + out_err3: + nl_cache_free(drv->nl_cache_preq); + out_err2: + nl80211_handle_destroy(drv->nl_handle_preq); + drv->nl_handle_preq = NULL; + out_err1: + return -1; } @@ -5240,6 +5744,28 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, } +static int nl80211_set_intra_bss(void *priv, int enabled) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, !enabled); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -5299,4 +5825,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, .send_frame = nl80211_send_frame, + .set_intra_bss = nl80211_set_intra_bss, };