Allow AP mode to disconnect STAs based on low ACK condition

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>
This commit is contained in:
Johannes Berg 2010-12-28 17:15:01 +02:00 committed by Jouni Malinen
parent d3e01b9d71
commit 0d7e5a3a29
14 changed files with 84 additions and 3 deletions

View file

@ -2032,6 +2032,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
else else
bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION; bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
#endif /* CONFIG_P2P_MANAGER */ #endif /* CONFIG_P2P_MANAGER */
} else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
bss->disassoc_low_ack = atoi(pos);
} else { } else {
wpa_printf(MSG_ERROR, "Line %d: unknown configuration " wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
"item '%s'", line, buf); "item '%s'", line, buf);

View file

@ -342,6 +342,11 @@ wmm_ac_vo_acm=0
# default: 300 (i.e., 5 minutes) # default: 300 (i.e., 5 minutes)
#ap_max_inactivity=300 #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 # Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
# remain asleep). Default: 65535 (no limit apart from field size) # remain asleep). Default: 65535 (no limit apart from field size)
#max_listen_interval=100 #max_listen_interval=100

View file

@ -323,6 +323,8 @@ struct hostapd_bss_config {
#define P2P_MANAGE BIT(3) #define P2P_MANAGE BIT(3)
#define P2P_ALLOW_CROSS_CONNECTION BIT(4) #define P2P_ALLOW_CROSS_CONNECTION BIT(4)
int p2p; int p2p;
int disassoc_low_ack;
}; };

View 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 HOSTAPD
#ifdef NEED_AP_MLME #ifdef NEED_AP_MLME
@ -500,6 +516,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (data) if (data)
hostapd_notif_disassoc(hapd, data->deauth_info.addr); hostapd_notif_disassoc(hapd, data->deauth_info.addr);
break; break;
case EVENT_STATION_LOW_ACK:
if (!data)
break;
hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
break;
default: default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event); wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break; break;

View 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, int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ielen); const u8 *ie, size_t ielen);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); 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 */ #endif /* HOSTAPD_H */

View file

@ -174,6 +174,8 @@
#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24
/* IEEE 802.11e */
#define WLAN_REASON_DISASSOC_LOW_ACK 34
/* Information Element IDs */ /* Information Element IDs */

View file

@ -2287,6 +2287,15 @@ enum wpa_event_type {
* details of the frame. * details of the frame.
*/ */
EVENT_UNPROT_DISASSOC, 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; const u8 *da;
u16 reason_code; u16 reason_code;
} unprot_disassoc; } unprot_disassoc;
/**
* struct low_ack - Data for EVENT_STATION_LOW_ACK events
* @addr: station address
*/
struct low_ack {
u8 addr[ETH_ALEN];
} low_ack;
}; };
/** /**

View 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_THOLD] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [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]; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
enum nl80211_cqm_rssi_threshold_event event; enum nl80211_cqm_rssi_threshold_event event;
@ -1224,12 +1225,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
return; 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) if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
return; return;
event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); 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) { if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
"event: RSSI high"); "event: RSSI high");

View file

@ -191,6 +191,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
else else
bss->max_num_sta = wpa_s->conf->max_num_sta; bss->max_num_sta = wpa_s->conf->max_num_sta;
bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
return 0; return 0;
} }

View file

@ -2408,7 +2408,8 @@ static const struct global_parse_data global_fields[] = {
{ FUNC(country), CFG_CHANGED_COUNTRY }, { FUNC(country), CFG_CHANGED_COUNTRY },
{ INT(bss_max_count), 0 }, { INT(bss_max_count), 0 },
{ INT_RANGE(filter_ssids, 0, 1), 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 #undef FUNC

View file

@ -400,6 +400,11 @@ struct wpa_config {
* changed_parameters - Bitmap of changed parameters since last update * changed_parameters - Bitmap of changed parameters since last update
*/ */
unsigned int changed_parameters; unsigned int changed_parameters;
/**
* disassoc_low_ack - disassocenticate stations with massive packet loss
*/
int disassoc_low_ack;
}; };

View 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); fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
if (config->max_num_sta != DEFAULT_MAX_NUM_STA) if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
fprintf(f, "max_num_sta=%u\n", config->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 */ #endif /* CONFIG_NO_CONFIG_WRITE */

View file

@ -269,6 +269,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
&config->filter_ssids); &config->filter_ssids);
wpa_config_read_reg_dword(hk, TEXT("max_num_sta"), wpa_config_read_reg_dword(hk, TEXT("max_num_sta"),
(int *) &config->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; return errors ? -1 : 0;
} }
@ -609,6 +611,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
config->filter_ssids, 0); config->filter_ssids, 0);
wpa_config_write_reg_dword(hk, TEXT("max_num_sta"), wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
config->max_num_sta, DEFAULT_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; return 0;
} }

View file

@ -2030,6 +2030,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_supplicant_event_unprot_disassoc(wpa_s, wpa_supplicant_event_unprot_disassoc(wpa_s,
&data->unprot_disassoc); &data->unprot_disassoc);
break; 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: default:
wpa_printf(MSG_INFO, "Unknown event %d", event); wpa_printf(MSG_INFO, "Unknown event %d", event);
break; break;