implement station inactivity timeout

After the max station inactivity has expired, a probe request
(a data null frame) is sent to the station. It the station does
not ACK this frame, it is removed from the WTP.

Note: inactivity timeout is not the same as the CAPWAP Idle Timeout.
The CAPWAP Idle Timeout would remove a station due to inactivity
even when it is still reachable from the WTP. In contrast, the
inactivity timeout probes whether the station is still present
and only removes it when not.
This commit is contained in:
Andreas Schultz
2016-04-07 15:15:50 +02:00
parent bca5c91ae1
commit 813f24b8ac
9 changed files with 432 additions and 38 deletions

View File

@ -520,6 +520,16 @@ static int nl80211_device_changefrequency(struct wifi_device* device, struct wif
return result;
}
/* */
static void nl80211_wlan_client_probe_event(struct wifi_wlan* wlan,
struct nlattr** tb_msg)
{
if (!tb_msg[NL80211_ATTR_MAC] || !tb_msg[NL80211_ATTR_ACK])
return;
wifi_wlan_client_probe_event(wlan, nla_data(tb_msg[NL80211_ATTR_MAC]));
}
/* */
static int nl80211_wlan_event(struct wifi_wlan* wlan,
struct genlmsghdr* gnlh,
@ -574,6 +584,10 @@ static int nl80211_wlan_event(struct wifi_wlan* wlan,
case NL80211_CMD_DEL_STATION:
break;
case NL80211_CMD_PROBE_CLIENT:
nl80211_wlan_client_probe_event(wlan, tb_msg);
break;
default:
log_printf(LOG_DEBUG, "*** nl80211_wlan_event: %s (%d)",
nl80211_command_to_string((int)gnlh->cmd), (int)gnlh->cmd);
@ -709,6 +723,12 @@ static wifi_wlan_handle nl80211_wlan_create(struct wifi_device* device, struct w
ASSERT(device->handle != NULL);
ASSERT(wlan != NULL);
if (!wlan->device->capability->supp_cmds.poll_command_supported) {
log_printf(LOG_CRIT, "unable to run nl80211 WTP on device %s without "
"probe_client command support", wlan->device->phyname);
return NULL;
}
/* */
msg = nlmsg_alloc();
if (!msg ||
@ -1054,7 +1074,13 @@ static int cb_wlan_send_frame(struct nl_msg* msg, void* arg) {
}
/* */
static int nl80211_wlan_sendframe(struct wifi_wlan* wlan, uint8_t* frame, int length, uint32_t frequency, uint32_t duration, int offchannel_tx_ok, int no_cck_rate, int no_wait_ack) {
static int
nl80211_wlan_sendframe(struct wifi_wlan* wlan,
uint8_t* frame, int length,
uint32_t frequency, uint32_t duration,
int offchannel_tx_ok, int no_cck_rate,
int no_wait_ack)
{
int result;
uint64_t cookie;
struct nl_msg* msg;
@ -1066,6 +1092,11 @@ static int nl80211_wlan_sendframe(struct wifi_wlan* wlan, uint8_t* frame, int le
ASSERT(frame != NULL);
ASSERT(length > 0);
log_printf(LOG_DEBUG, "nl80211: CMD_FRAME frequency=%u duration=%u no_cck=%d "
"no_ack=%d offchanok=%d",
frequency, duration, no_cck_rate, no_wait_ack, offchannel_tx_ok);
log_hexdump(LOG_DEBUG, "CMD_FRAME", frame, length);
/* */
msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_FRAME);
if (!msg)
@ -1106,6 +1137,79 @@ out_err:
return -1;
}
#if 0
/* Sending non-Mgmt Frames via nl80211_wlan_sendframe does not work,
disable this till the WTP kernel mode supports frame injection from userspace */
/* Send data frame to poll STA and check whether this frame is ACKed */
static void nl80211_wlan_send_null_frame(struct wifi_wlan* wlan, const uint8_t* address, int qos)
{
struct {
struct ieee80211_header hdr;
uint16_t qos_ctl;
} STRUCT_PACKED nulldata;
size_t size;
memset(&nulldata, 0, sizeof(nulldata));
if (qos) {
nulldata.hdr.framecontrol =
IEEE80211_FRAME_CONTROL(IEEE80211_FRAMECONTROL_TYPE_DATA,
IEEE80211_FRAMECONTROL_DATA_SUBTYPE_QOSNULL);
size = sizeof(nulldata);
} else {
nulldata.hdr.framecontrol =
IEEE80211_FRAME_CONTROL(IEEE80211_FRAMECONTROL_TYPE_DATA,
IEEE80211_FRAMECONTROL_DATA_SUBTYPE_NULL);
size = sizeof(struct ieee80211_header);
}
nulldata.hdr.framecontrol |= __cpu_to_le16(IEEE80211_FRAME_CONTROL_MASK_FROMDS);
memcpy(nulldata.hdr.address1, address, ETH_ALEN);
memcpy(nulldata.hdr.address2, wlan->address, ETH_ALEN);
memcpy(nulldata.hdr.address3, wlan->address, ETH_ALEN);
if (nl80211_wlan_sendframe(wlan, (uint8_t *)&nulldata, size,
wlan->device->currentfrequency.frequency,
0, 0, 0, 0))
log_printf(LOG_DEBUG, "nl80211_send_null_frame: Failed to send poll frame");
}
#endif
/* */
static void nl80211_wlan_poll_station(struct wifi_wlan* wlan, const uint8_t* address, int qos)
{
int result;
struct nl_msg* msg;
ASSERT(wlan != NULL);
ASSERT(wlan->handle != NULL);
ASSERT(address != NULL);
#if 0 /* see nl80211_wlan_send_null_frame for explanation */
if (!wlan->device->capability->supp_cmds.poll_command_supported) {
nl80211_wlan_send_null_frame(wlan, address, qos);
return;
}
#endif
/* */
msg = nl80211_wlan_msg(wlan, 0, NL80211_CMD_PROBE_CLIENT);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, MACADDRESS_EUI48_LENGTH, address)) {
nlmsg_free(msg);
return;
}
/* */
result = nl80211_wlan_send_and_recv_msg(wlan, msg, NULL, NULL);
if (result < 0)
log_printf(LOG_DEBUG, "nl80211: Client probe request for "
MACSTR " failed: ret=%d (%s)",
MAC2STR(address), result, strerror(-result));
}
/* */
static void nl80211_wlan_delete(struct wifi_wlan* wlan) {
struct nl80211_wlan_handle* wlanhandle;
@ -1486,6 +1590,24 @@ static void phydevice_capability_feature_flags(struct wifi_capability *capabilit
}
}
static void phydevice_capability_supp_cmds(struct wifi_commands_capability *cmd_cap,
struct nlattr *tb)
{
int i;
struct nlattr *nl_cmd;
if (tb == NULL)
return;
nla_for_each_nested(nl_cmd, tb, i) {
switch (nla_get_u32(nl_cmd)) {
case NL80211_CMD_PROBE_CLIENT:
cmd_cap->poll_command_supported = 1;
break;
}
}
}
static void phydevice_capability_cipher_suites(struct wifi_capability *capability,
struct nlattr *tb)
{
@ -1775,6 +1897,9 @@ static int cb_get_phydevice_capability(struct nl_msg* msg, void* data)
phydevice_capability_feature_flags(capability, tb_msg[NL80211_ATTR_FEATURE_FLAGS]);
/* Commands supported */
phydevice_capability_supp_cmds(&capability->supp_cmds, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]);
/* Cipher supported */
phydevice_capability_cipher_suites(capability, tb_msg[NL80211_ATTR_CIPHER_SUITES]);
@ -2155,6 +2280,7 @@ const struct wifi_driver_ops wifi_driver_nl80211_ops = {
.wlan_startap = nl80211_wlan_startap,
.wlan_stopap = nl80211_wlan_stopap,
.wlan_sendframe = nl80211_wlan_sendframe,
.wlan_poll_station = nl80211_wlan_poll_station,
.wlan_delete = nl80211_wlan_delete,
.station_authorize = nl80211_station_authorize,