]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
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 1651e257877e210bdbdd3771b156c005924dab10..493861188cc391a04fc0d46f2b1bc9800d09ca5c 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 fe97ea87eafaf7b8a0929bcf2fb1d99b234010db..2090e9faa216866721fb45fe636379816b175162 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 a95ebe9acb8829371977b8b1b4f6348925be3336..0a929d66eb3d3f33f03f56450616dfa3f873c619 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 5469d05c0c7a0afb68160f94626dc073de4d3242..a49248fde3dca63d0fdaf1028614ad00a12107f4 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 e2182cc0cbb39624d29c42f9004a510175aea278..03464089f716478f0d3271af0c901b890265444b 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 c10ea13ea4133ef8a0906f4a8882d80b08a58d0a..3f5dc7ae9ff06691ed1daabaa9992f702b3e165b 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 036092489d7b6fb96659e8a25a582b91751aa4bd..728ef79cac1e2bb72908c52780aceaa92450d027 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 e0f8b1101251d6ed0d4ec76881424fd7578f19b1..f7920990efad9717b22267937c869e2290f7f48d 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 bb2f2f29604d86add220edfcf04df69e91d46ca4..26ccdeab29a95fc6160aa57bf8111f4b24eccb01 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 081f7e065deef1ad80d0699a05c5cac2158ff3fe..1ea37d4bb2fbbca0fe2bedb724ac06a35de7cea0 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 f288b2c976479dcfe75077fbc4d6fe694e4ac20c..c0ac41fa108c2279a0c6afa44a4f06d180fd16e9 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 8715a46f06ef65dbdae61d04a804c67ea3d137c3..09c99e37271f7cc1dbf2867582694d0410048da8 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 ef95a29453c523e0c3d948eaeb42c8a3381d5fe8..6b2096f503ee1c774a6039d756bc60a98e35918a 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 c48365e5d110f320b126a1f4f73629e8915c80aa..bcd59d94e4e44a9ab045cc83992deb7e99c09404 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;