From patchwork Mon Nov 21 10:41:53 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Berg X-Patchwork-Id: 126733 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from maxx.maxx.shmoo.com (maxx.shmoo.com [205.134.188.171]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "maxx.shmoo.com", Issuer "CA Cert Signing Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 27AB3B71FF for ; Mon, 21 Nov 2011 21:42:12 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id EB8C99C154; Mon, 21 Nov 2011 05:42:06 -0500 (EST) X-Virus-Scanned: amavisd-new at maxx.shmoo.com Received: from maxx.maxx.shmoo.com ([127.0.0.1]) by localhost (maxx.shmoo.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id oQwkksXRvn5c; Mon, 21 Nov 2011 05:42:06 -0500 (EST) Received: from maxx.shmoo.com (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id 661A79C15A; Mon, 21 Nov 2011 05:42:02 -0500 (EST) X-Original-To: mailman-post+hostap@maxx.shmoo.com Delivered-To: mailman-post+hostap@maxx.shmoo.com Received: from localhost (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id 7F7959C15D for ; Mon, 21 Nov 2011 05:42:01 -0500 (EST) X-Virus-Scanned: amavisd-new at maxx.shmoo.com Received: from maxx.maxx.shmoo.com ([127.0.0.1]) by localhost (maxx.shmoo.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id V5YbPC8GNfn4 for ; Mon, 21 Nov 2011 05:41:57 -0500 (EST) Received: from sipsolutions.net (he.sipsolutions.net [78.46.109.217]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by maxx.maxx.shmoo.com (Postfix) with ESMTPS id 3ADFC9C15A for ; Mon, 21 Nov 2011 05:41:57 -0500 (EST) Received: by sipsolutions.net with esmtpsa (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.77) (envelope-from ) id 1RSRK0-0004Dw-2l for hostap@lists.shmoo.com; Mon, 21 Nov 2011 11:41:56 +0100 Subject: [PATCH v4 10/15] driver_nl80211: use nl80211 for mgmt TX/RX in AP mode From: Johannes Berg To: hostap@lists.shmoo.com In-Reply-To: <20111119123952.403374239@sipsolutions.net> References: <20111119123910.783418920@sipsolutions.net> <20111119123952.403374239@sipsolutions.net> Date: Mon, 21 Nov 2011 11:41:53 +0100 Message-ID: <1321872113.3999.37.camel@jlt3.sipsolutions.net> Mime-Version: 1.0 X-Mailer: Evolution 2.30.3 X-BeenThere: hostap@lists.shmoo.com X-Mailman-Version: 2.1.9 Precedence: list List-Id: HostAP Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: hostap-bounces@lists.shmoo.com Errors-To: hostap-bounces@lists.shmoo.com From: Johannes Berg 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. Signed-hostap: Johannes Berg --- v4: rebase onto Helmut's changes src/drivers/driver_nl80211.c | 232 ++++++++++++++++++++++++++++++++---------- 1 files changed, 178 insertions(+), 54 deletions(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 8f1417c..ab5c2dd 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -191,7 +191,7 @@ struct i802_bss { int freq; - struct nl80211_handles nl_preq; + struct nl80211_handles nl_preq, nl_mgmt; struct nl_cb *nl_cb; }; @@ -235,6 +235,7 @@ struct wpa_driver_nl80211_data { unsigned int device_ap_sme:1; unsigned int poll_command_supported:1; unsigned int data_tx_status:1; + unsigned int use_monitor:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -962,26 +963,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); @@ -1126,8 +1130,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, @@ -1776,7 +1780,6 @@ static int process_drv_event(struct nl_msg *msg, void *arg) case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_FRAME: case NL80211_CMD_FRAME_TX_STATUS: case NL80211_CMD_UNPROT_DEAUTHENTICATE: case NL80211_CMD_UNPROT_DISASSOCIATE: @@ -1849,6 +1852,7 @@ static int process_drv_event(struct nl_msg *msg, void *arg) static int process_bss_event(struct nl_msg *msg, void *arg) { + struct i802_bss *bss = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1856,6 +1860,13 @@ static int process_bss_event(struct nl_msg *msg, void *arg) genlmsg_attrlen(gnlh, 0), NULL); switch (gnlh->cmd) { + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event(bss->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], + tb[NL80211_ATTR_COOKIE]); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); @@ -2174,6 +2185,20 @@ 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 mac80211 is new enough to + * have everything we need to not need monitor interfaces. + */ + drv->use_monitor = !info.poll_command_supported; + + /* + * If we aren't going to use monitor interfaces, but the + * driver doesn't support data TX status, we won't get TX + * status for EAPOL frames. + */ + if (!drv->use_monitor && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + return 0; } @@ -2474,6 +2499,9 @@ 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 (!drv->use_monitor) + 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, @@ -2494,10 +2522,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; @@ -2507,7 +2536,7 @@ static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); - 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); @@ -2528,46 +2557,71 @@ 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->nl_cb, + 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 */ #ifdef CONFIG_TDLS @@ -2580,17 +2634,66 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) #endif /* CONFIG_TDLS */ /* 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); } @@ -2647,16 +2750,6 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) 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); @@ -2750,6 +2843,7 @@ static void wpa_driver_nl80211_deinit(void *priv) (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); + nl80211_mgmt_unsubscribe(bss); eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle)); nl_destroy_handles(&drv->nl_event); @@ -4317,9 +4411,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, int noack) +static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) { __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ @@ -4374,6 +4468,22 @@ 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, int noack) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + u64 cookie; + + if (drv->use_monitor) + return wpa_driver_nl80211_send_mntr(drv, data, len, + encrypt, noack); + + return nl80211_send_frame_cmd(bss, bss->freq, 0, data, len, + &cookie, 0, noack, 0); +} + + static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, size_t data_len, int noack) { @@ -4419,7 +4529,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, noack); } @@ -5310,7 +5420,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->use_monitor) + if (nl80211_mgmt_subscribe_ap(bss)) + return -1; + + if (!drv->device_ap_sme && drv->use_monitor && nl80211_create_monitor_interface(drv) && !drv->device_ap_sme) return -1; @@ -5332,8 +5446,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->use_monitor) nl80211_remove_monitor_interface(drv); + else + nl80211_mgmt_unsubscribe(bss); + bss->beacon_set = 0; } @@ -5380,7 +5497,7 @@ static int wpa_driver_nl80211_hapd_send_eapol( int res; int qos = flags & WPA_STA_WMM; - if (drv->device_ap_sme || drv->data_tx_status) + if (drv->device_ap_sme || !drv->use_monitor) return nl80211_send_eapol_data(bss, addr, data, data_len); len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + @@ -5420,7 +5537,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, 0); + res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -6067,14 +6184,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; } @@ -7259,14 +7384,14 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report) if (nl_create_handles(&bss->nl_preq, drv->global->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) goto out_err; eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq.handle), - wpa_driver_nl80211_event_receive, drv->nl_cb, + wpa_driver_nl80211_event_receive, bss->nl_cb, bss->nl_preq.handle); return 0; @@ -7493,8 +7618,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, 0); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0); }