Allow AP mode to disconnect STAs based on low ACK condition
authorJohannes Berg <johannes.berg@intel.com>
Tue, 28 Dec 2010 15:15:01 +0000 (17:15 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 28 Dec 2010 15:15:01 +0000 (17:15 +0200)
The nl80211 driver can report low ACK condition (in fact it reports
complete loss right now only). Use that, along with a config option, to
disconnect stations when the data connection is not working properly,
e.g., due to the STA having went outside the range of the AP. This is
disabled by default and can be enabled with disassoc_low_ack=1 in
hostapd or wpa_supplicant configuration file.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
14 files changed:
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.h
src/ap/drv_callbacks.c
src/ap/hostapd.h
src/common/ieee802_11_defs.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/ap.c
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/config_winreg.c
wpa_supplicant/events.c

index 1651e25..4938611 100644 (file)
@@ -2032,6 +2032,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
                        else
                                bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
 #endif /* CONFIG_P2P_MANAGER */
+               } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
+                       bss->disassoc_low_ack = atoi(pos);
                } else {
                        wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
                                   "item '%s'", line, buf);
index fe97ea8..2090e9f 100644 (file)
@@ -342,6 +342,11 @@ wmm_ac_vo_acm=0
 # default: 300 (i.e., 5 minutes)
 #ap_max_inactivity=300
 
+# Disassociate stations based on excessive transmission failures or other
+# indications of connection loss. This depends on the driver capabilities and
+# may not be available with all drivers.
+#disassoc_low_ack=1
+
 # Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
 # remain asleep). Default: 65535 (no limit apart from field size)
 #max_listen_interval=100
index a95ebe9..0a929d6 100644 (file)
@@ -323,6 +323,8 @@ struct hostapd_bss_config {
 #define P2P_MANAGE BIT(3)
 #define P2P_ALLOW_CROSS_CONNECTION BIT(4)
        int p2p;
+
+       int disassoc_low_ack;
 };
 
 
index 5469d05..a49248f 100644 (file)
@@ -231,6 +231,22 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
 }
 
 
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+       struct sta_info *sta = ap_get_sta(hapd, addr);
+
+       if (!sta || !hapd->conf->disassoc_low_ack)
+               return;
+
+       hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
+                      "missing ACKs");
+       hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+       if (sta)
+               ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+
 #ifdef HOSTAPD
 
 #ifdef NEED_AP_MLME
@@ -500,6 +516,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                if (data)
                        hostapd_notif_disassoc(hapd, data->deauth_info.addr);
                break;
+       case EVENT_STATION_LOW_ACK:
+               if (!data)
+                       break;
+               hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "Unknown event %d", event);
                break;
index e2182cc..0346408 100644 (file)
@@ -247,5 +247,6 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                        const u8 *ie, size_t ielen);
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
 
 #endif /* HOSTAPD_H */
index c10ea13..3f5dc7a 100644 (file)
 #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+/* IEEE 802.11e */
+#define WLAN_REASON_DISASSOC_LOW_ACK 34
 
 
 /* Information Element IDs */
index 0360924..728ef79 100644 (file)
@@ -2287,6 +2287,15 @@ enum wpa_event_type {
         * details of the frame.
         */
        EVENT_UNPROT_DISASSOC,
+
+       /**
+        * EVENT_STATION_LOW_ACK
+        *
+        * Driver generates this event whenever it detected that a particular
+        * station was lost. Detection can be through massive transmission
+        * failures for example.
+        */
+       EVENT_STATION_LOW_ACK
 };
 
 
@@ -2732,6 +2741,14 @@ union wpa_event_data {
                const u8 *da;
                u16 reason_code;
        } unprot_disassoc;
+
+       /**
+        * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+        * @addr: station address
+        */
+       struct low_ack {
+               u8 addr[ETH_ALEN];
+       } low_ack;
 };
 
 /**
index e0f8b11..f792099 100644 (file)
@@ -1210,6 +1210,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
                [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
                [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
                [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+               [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
        };
        struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
        enum nl80211_cqm_rssi_threshold_event event;
@@ -1224,12 +1225,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
                return;
        }
 
+       os_memset(&ed, 0, sizeof(ed));
+
+       if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+               if (!tb[NL80211_ATTR_MAC])
+                       return;
+               os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+                         ETH_ALEN);
+               wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+               return;
+       }
+
        if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
                return;
        event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
 
-       os_memset(&ed, 0, sizeof(ed));
-
        if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
                wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
                           "event: RSSI high");
index bb2f2f2..26ccdea 100644 (file)
@@ -191,6 +191,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        else
                bss->max_num_sta = wpa_s->conf->max_num_sta;
 
+       bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
+
        return 0;
 }
 
index 081f7e0..1ea37d4 100644 (file)
@@ -2408,7 +2408,8 @@ static const struct global_parse_data global_fields[] = {
        { FUNC(country), CFG_CHANGED_COUNTRY },
        { INT(bss_max_count), 0 },
        { INT_RANGE(filter_ssids, 0, 1), 0 },
-       { INT(max_num_sta), 0 }
+       { INT(max_num_sta), 0 },
+       { INT_RANGE(disassoc_low_ack, 0, 1), 0 }
 };
 
 #undef FUNC
index f288b2c..c0ac41f 100644 (file)
@@ -400,6 +400,11 @@ struct wpa_config {
         * changed_parameters - Bitmap of changed parameters since last update
         */
        unsigned int changed_parameters;
+
+       /**
+        * disassoc_low_ack - disassocenticate stations with massive packet loss
+        */
+       int disassoc_low_ack;
 };
 
 
index 8715a46..09c99e3 100644 (file)
@@ -690,6 +690,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
        if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
                fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
+       if (config->disassoc_low_ack)
+               fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
index ef95a29..6b2096f 100644 (file)
@@ -269,6 +269,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
                                  &config->filter_ssids);
        wpa_config_read_reg_dword(hk, TEXT("max_num_sta"),
                                  (int *) &config->max_num_sta);
+       wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"),
+                                 (int *) &config->disassoc_low_ack);
 
        return errors ? -1 : 0;
 }
@@ -609,6 +611,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
                                   config->filter_ssids, 0);
        wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
                                   config->max_num_sta, DEFAULT_MAX_NUM_STA);
+       wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"),
+                                  config->disassoc_low_ack, 0);
 
        return 0;
 }
index c48365e..bcd59d9 100644 (file)
@@ -2030,6 +2030,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                wpa_supplicant_event_unprot_disassoc(wpa_s,
                                                     &data->unprot_disassoc);
                break;
+       case EVENT_STATION_LOW_ACK:
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface && data)
+                       hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
+                                                 data->low_ack.addr);
+#endif /* CONFIG_AP */
+               break;
        default:
                wpa_printf(MSG_INFO, "Unknown event %d", event);
                break;