]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/drivers/driver_nl80211.c
Do not trigger fast reconnection on locally generated deauth/disassoc
[thirdparty/hostap.git] / src / drivers / driver_nl80211.c
index 6d518bdb7ecb47db0c75c296cb18fd698fb13730..6af8cc9cb1a22791031920b6ca6ea82b360d593f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -606,7 +606,7 @@ static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
 {
        struct nl80211_wiphy_data *w = eloop_ctx;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Beacon event message available");
+       wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
 
        nl_recvmsgs(handle, w->nl_cb);
 }
@@ -1151,7 +1151,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 
 
 static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
-                                 struct nlattr *reason, struct nlattr *addr)
+                                 struct nlattr *reason, struct nlattr *addr,
+                                 struct nlattr *by_ap)
 {
        union wpa_event_data data;
 
@@ -1169,6 +1170,7 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
        os_memset(&data, 0, sizeof(data));
        if (reason)
                data.disassoc_info.reason_code = nla_get_u16(reason);
+       data.disassoc_info.locally_generated = by_ap == NULL;
        wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
 }
 
@@ -1309,6 +1311,8 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                reason_code = le_to_host16(mgmt->u.deauth.reason_code);
 
        if (type == EVENT_DISASSOC) {
+               event.disassoc_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
                event.disassoc_info.addr = bssid;
                event.disassoc_info.reason_code = reason_code;
                if (frame + len > mgmt->u.disassoc.variable) {
@@ -1317,6 +1321,8 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                                mgmt->u.disassoc.variable;
                }
        } else {
+               event.deauth_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
                event.deauth_info.addr = bssid;
                event.deauth_info.reason_code = reason_code;
                if (frame + len > mgmt->u.deauth.variable) {
@@ -2083,7 +2089,8 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
                break;
        case NL80211_CMD_DISCONNECT:
                mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
-                                     tb[NL80211_ATTR_MAC]);
+                                     tb[NL80211_ATTR_MAC],
+                                     tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
                break;
        case NL80211_CMD_MICHAEL_MIC_FAILURE:
                mlme_event_michael_mic_failure(drv, tb);
@@ -2269,6 +2276,7 @@ struct wiphy_info_data {
        unsigned int device_ap_sme:1;
        unsigned int poll_command_supported:1;
        unsigned int data_tx_status:1;
+       unsigned int monitor_supported:1;
 };
 
 
@@ -2340,6 +2348,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                        case NL80211_IFTYPE_P2P_CLIENT:
                                p2p_client_supported = 1;
                                break;
+                       case NL80211_IFTYPE_MONITOR:
+                               info->monitor_supported = 1;
+                               break;
                        }
                }
        }
@@ -2560,6 +2571,20 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
         */
        drv->use_monitor = !info.poll_command_supported;
 
+       if (drv->device_ap_sme && drv->use_monitor) {
+               /*
+                * Non-mac80211 drivers may not support monitor interface.
+                * Make sure we do not get stuck with incorrect capability here
+                * by explicitly testing this.
+                */
+               if (!info.monitor_supported) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+                                  "with device_ap_sme since no monitor mode "
+                                  "support detected");
+                       drv->use_monitor = 0;
+               }
+       }
+
        /*
         * If we aren't going to use monitor interfaces, but the
         * driver doesn't support data TX status, we won't get TX
@@ -3145,6 +3170,28 @@ out_err:
 }
 
 
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+                  "handle %p (device SME)", bss->nl_mgmt);
+
+       if (nl80211_register_frame(bss, bss->nl_mgmt,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_ACTION << 4),
+                                  NULL, 0) < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
+       nl_destroy_handles(&bss->nl_mgmt);
+       return -1;
+}
+
+
 static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
 {
        if (bss->nl_mgmt == NULL)
@@ -6066,6 +6113,10 @@ static int nl80211_setup_ap(struct i802_bss *bss)
                if (nl80211_mgmt_subscribe_ap(bss))
                        return -1;
 
+       if (drv->device_ap_sme && !drv->use_monitor)
+               if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+                       return -1;
+
        if (!drv->device_ap_sme && drv->use_monitor &&
            nl80211_create_monitor_interface(drv) &&
            !drv->device_ap_sme)
@@ -6086,9 +6137,11 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (drv->device_ap_sme)
+       if (drv->device_ap_sme) {
                wpa_driver_nl80211_probe_req_report(bss, 0);
-       else if (drv->use_monitor)
+               if (!drv->use_monitor)
+                       nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+       } else if (drv->use_monitor)
                nl80211_remove_monitor_interface(drv);
        else
                nl80211_mgmt_unsubscribe(bss, "AP teardown");
@@ -7830,7 +7883,7 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
        if (wait)
                NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-       if (offchanok)
+       if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
                NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
        if (no_cck)
                NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
@@ -8012,7 +8065,16 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
        if (!report) {
-               if (bss->nl_preq) {
+               if (bss->nl_preq && drv->device_ap_sme &&
+                   is_ap_interface(drv->nlmode)) {
+                       /*
+                        * Do not disable Probe Request reporting that was
+                        * enabled in nl80211_setup_ap().
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+                                  "Probe Request reporting nl_preq=%p while "
+                                  "in AP mode", bss->nl_preq);
+               } else if (bss->nl_preq) {
                        wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
                                   "reporting nl_preq=%p", bss->nl_preq);
                        eloop_unregister_read_sock(