diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 1651e2578..493861188 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index fe97ea87e..2090e9faa 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -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 diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index a95ebe9ac..0a929d66e 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -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; }; diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 5469d05c0..a49248fde 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -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; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index e2182cc0c..03464089f 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -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 */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index c10ea13ea..3f5dc7ae9 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -174,6 +174,8 @@ #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 */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 036092489..728ef79ca 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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; }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index e0f8b1101..f7920990e 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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"); diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index bb2f2f296..26ccdeab2 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -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; } diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 081f7e065..1ea37d4bb 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -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 diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index f288b2c97..c0ac41fa1 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -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; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 8715a46f0..09c99e372 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -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 */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index ef95a2945..6b2096f50 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -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; } diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index c48365e5d..bcd59d94e 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -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;