To achieve this, multiple things are needed: 1) since hostapd needs to handle *all* action frames, make the normal registration only when in a non-AP mode, to be able to do this use the new socket 2) store the frequency in each BSS to be able to give the right frequency to nl80211's mgmt-tx operation 3) make TX status processing reject non-matched cookie only in non-AP mode The whole thing depends on having station-poll support in the kernel. That's currently a good indicator since the kernel patches are added together. --- src/drivers/driver_nl80211.c | 215 ++++++++++++++++++++++++++++++++---------- 1 files changed, 164 insertions(+), 51 deletions(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 089cf1e..4ced3cc 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -192,7 +192,7 @@ struct i802_bss { int freq; - struct nl80211_handles nl_preq; + struct nl80211_handles nl_preq, nl_mgmt; }; struct wpa_driver_nl80211_data { @@ -914,26 +914,29 @@ static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, } -static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, - struct nlattr *cookie, const u8 *frame, - size_t len, struct nlattr *ack) +static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) { union wpa_event_data event; const struct ieee80211_hdr *hdr; u16 fc; - u64 cookie_val; - if (!cookie) - return; + if (!is_ap_interface(drv->nlmode)) { + u64 cookie_val; - cookie_val = nla_get_u64(cookie); - 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)", ack != NULL); - if (cookie_val != drv->send_action_cookie) - return; + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + 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)", ack != NULL); + if (cookie_val != drv->send_action_cookie) + return; + } hdr = (const struct ieee80211_hdr *) frame; fc = le_to_host16(hdr->frame_control); @@ -1078,8 +1081,8 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME_TX_STATUS: - mlme_event_action_tx_status(drv, cookie, nla_data(frame), - nla_len(frame), ack); + mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); break; case NL80211_CMD_UNPROT_DEAUTHENTICATE: mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, @@ -2035,6 +2038,14 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->poll_command_supported = info.poll_command_supported; drv->data_tx_status = info.data_tx_status; + /* + * If poll command is supported we'll not use the monitor + * interface even with mac80211, and then we require data + * TX status to get EAPOL. + */ + if (info.poll_command_supported && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + return 0; } @@ -2311,6 +2322,14 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, wpa_printf(MSG_DEBUG, "nl80211: wifi status sockopt failed\n"); drv->data_tx_status = 0; + /* + * If we have the poll command, we'll not be using + * monitor interface for EAPOL, so we fail to report + * TX status. + */ + if (drv->poll_command_supported) + drv->capa.flags &= + ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; } else { eloop_register_read_sock(drv->eapol_tx_sock, wpa_driver_nl80211_handle_eapol_tx_status, @@ -2339,10 +2358,11 @@ failed: } -static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, +static int nl80211_register_frame(struct i802_bss *bss, struct nl_handle *nl_handle, u16 type, const u8 *match, size_t match_len) { + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -1; @@ -2353,7 +2373,7 @@ static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_REGISTER_ACTION, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); @@ -2374,61 +2394,135 @@ nla_put_failure: } -static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, +static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (bss->nl_mgmt.handle) { + wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " + "already on!"); + return -1; + } + + if (nl_create_handles(&bss->nl_mgmt, drv->nl_cb, "mgmt")) + return -1; + + eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt.handle), + wpa_driver_nl80211_event_receive, bss, + bss->nl_mgmt.handle); + + return 0; +} + +static int nl80211_register_action_frame(struct i802_bss *bss, 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_event.handle, + return nl80211_register_frame(bss, bss->nl_mgmt.handle, type, match, match_len); } -static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) +static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + #if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) /* GAS Initial Request */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) return -1; /* GAS Initial Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) return -1; /* GAS Comeback Request */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) return -1; /* GAS Comeback Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) return -1; #endif /* CONFIG_P2P || CONFIG_INTERWORKING */ #ifdef CONFIG_P2P /* P2P Public Action */ - if (nl80211_register_action_frame(drv, + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x09\x50\x6f\x9a\x09", 6) < 0) return -1; /* P2P Action */ - if (nl80211_register_action_frame(drv, + if (nl80211_register_action_frame(bss, (u8 *) "\x7f\x50\x6f\x9a\x09", 5) < 0) return -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_IEEE80211W /* SA Query Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) return -1; #endif /* CONFIG_IEEE80211W */ /* FT Action frames */ - if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) return -1; else drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; /* WNM - BSS Transition Management Request */ - if (nl80211_register_action_frame(drv, (u8 *) "\x0a\x07", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) + return -1; + + return 0; +} + + +static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) +{ + static const int stypes[] = { + WLAN_FC_STYPE_AUTH, + WLAN_FC_STYPE_ASSOC_REQ, + WLAN_FC_STYPE_REASSOC_REQ, + WLAN_FC_STYPE_DISASSOC, + WLAN_FC_STYPE_DEAUTH, + WLAN_FC_STYPE_ACTION, + WLAN_FC_STYPE_PROBE_REQ, +/* Beacon doesn't work as mac80211 doesn't currently allow + * it, but it wouldn't really be the right thing anyway as + * it isn't per interface ... maybe just dump the scan + * results periodically for OLBC? + */ +// WLAN_FC_STYPE_BEACON, + }; + int i; + + if (nl80211_alloc_mgmt_handle(bss)) return -1; + for (i = 0; i < sizeof(stypes)/sizeof(stypes[0]); i++) { + if (nl80211_register_frame(bss, bss->nl_mgmt.handle, + (WLAN_FC_TYPE_MGMT << 2) | + (stypes[i] << 4), + NULL, 0) < 0) { + goto out_err; + } + } + return 0; + + out_err: + eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt.handle)); + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static void nl80211_mgmt_unsubscribe(struct i802_bss *bss) +{ + if (bss->nl_mgmt.handle == NULL) + return; + eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt.handle)); + nl_destroy_handles(&bss->nl_mgmt); } @@ -2485,16 +2579,6 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr)) return -1; - if (nl80211_register_action_frames(drv) < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " - "frame processing - ignore for now"); - /* - * Older kernel versions did not support this, so ignore the - * error for now. Some functionality may not be available - * because of this. - */ - } - if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, drv, drv->ctx); @@ -2588,6 +2672,7 @@ static void wpa_driver_nl80211_deinit(void *priv) (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0); wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); + nl80211_mgmt_unsubscribe(bss); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); @@ -4172,9 +4257,9 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) } -static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, - const void *data, size_t len, - int encrypt) +static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt) { __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ @@ -4224,6 +4309,20 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, } +static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, + const void *data, size_t len, + int encrypt) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + u64 cookie; + + if (!drv->poll_command_supported) + return wpa_driver_nl80211_send_mntr(drv, data, len, encrypt); + + return nl80211_send_frame_cmd(bss, bss->freq, 0, data, len, 0, &cookie); +} + + static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, size_t data_len) { @@ -4267,7 +4366,7 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, encrypt = 0; } - return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt); } @@ -5159,7 +5258,11 @@ static int nl80211_setup_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; - if (!drv->device_ap_sme && + if (!drv->device_ap_sme && drv->poll_command_supported) + if (nl80211_mgmt_subscribe_ap(bss)) + return -1; + + if (!drv->device_ap_sme && !drv->poll_command_supported && nl80211_create_monitor_interface(drv) && !drv->device_ap_sme) return -1; @@ -5181,8 +5284,11 @@ static void nl80211_teardown_ap(struct i802_bss *bss) if (drv->device_ap_sme) wpa_driver_nl80211_probe_req_report(bss, 0); - else + else if (!drv->poll_command_supported) nl80211_remove_monitor_interface(drv); + else + nl80211_mgmt_unsubscribe(bss); + bss->beacon_set = 0; } @@ -5267,7 +5373,7 @@ static int wpa_driver_nl80211_hapd_send_eapol( pos += 2; memcpy(pos, data, data_len); - res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt); + res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -5916,14 +6022,22 @@ done: } if (is_ap_interface(nlmode)) { + nl80211_mgmt_unsubscribe(bss); /* Setup additional AP mode functionality if needed */ if (nl80211_setup_ap(bss)) return -1; } else if (was_ap) { /* Remove additional AP mode functionality */ nl80211_teardown_ap(bss); + } else { + nl80211_mgmt_unsubscribe(bss); } + if (!is_ap_interface(nlmode) && + nl80211_mgmt_subscribe_non_ap(bss) < 0) + wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " + "frame processing - ignore for now"); + return 0; } @@ -7108,7 +7222,7 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report) if (nl_create_handles(&bss->nl_preq, drv->nl_cb, "preq")) return -1; - if (nl80211_register_frame(drv, bss->nl_preq.handle, + if (nl80211_register_frame(bss, bss->nl_preq.handle, (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_REQ << 4), NULL, 0) < 0) @@ -7312,8 +7426,7 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, int encrypt) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt); } -- 1.7.6.3