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);
# 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
#define P2P_MANAGE BIT(3)
#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
int p2p;
+
+ int disassoc_low_ack;
};
}
+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
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;
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 */
#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 */
* 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
};
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;
};
/**
[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;
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");
else
bss->max_num_sta = wpa_s->conf->max_num_sta;
+ bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
+
return 0;
}
{ 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
* 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;
};
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 */
&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;
}
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;
}
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;