AP: Do station poll in driver wrapper

This offloads the station polling to driver wrappers, which may offload
it again to the driver. The hostap driver wrapper uses "real" data
frames while nl80211 uses null data frames.

Also add a specific event to indicate that a poll was successful for
future use with the nl80211 driver.
This commit is contained in:
Johannes Berg 2011-10-20 21:03:08 +03:00 committed by Jouni Malinen
parent 7a25f29d89
commit bcf24348ed
12 changed files with 155 additions and 47 deletions

View file

@ -207,4 +207,13 @@ static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
}
static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
const u8 *own_addr, const u8 *addr,
int qos)
{
if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
return;
hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
}
#endif /* AP_DRV_OPS */

View file

@ -549,6 +549,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
}
break;
case EVENT_DRIVER_CLIENT_POLL_OK:
hostapd_client_poll_ok(hapd, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame,
data->rx_from_unknown.len);

View file

@ -1735,6 +1735,32 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
}
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
struct hostapd_iface *iface = hapd->iface;
sta = ap_get_sta(hapd, addr);
if (sta == NULL && iface->num_bss > 1) {
size_t j;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
sta = ap_get_sta(hapd, addr);
if (sta)
break;
}
}
if (sta == NULL)
return;
if (!(sta->flags & WLAN_STA_PENDING_POLL))
return;
wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
"activity poll", MAC2STR(sta->addr));
sta->flags &= ~WLAN_STA_PENDING_POLL;
}
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds)
{

View file

@ -75,5 +75,6 @@ u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
#endif /* IEEE802_11_H */

View file

@ -319,52 +319,10 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
if (sta->timeout_next == STA_NULLFUNC &&
(sta->flags & WLAN_STA_ASSOC)) {
#ifndef CONFIG_NATIVE_WINDOWS
/* send data frame to poll STA and check whether this frame
* is ACKed */
struct {
struct ieee80211_hdr hdr;
u16 qos_ctl;
} STRUCT_PACKED nulldata;
int size = sizeof(struct ieee80211_hdr);
wpa_printf(MSG_DEBUG, " Polling STA with data frame");
wpa_printf(MSG_DEBUG, " Polling STA");
sta->flags |= WLAN_STA_PENDING_POLL;
os_memset(&nulldata, 0, sizeof(nulldata));
if (hapd->driver &&
os_strcmp(hapd->driver->name, "hostap") == 0) {
/*
* WLAN_FC_STYPE_NULLFUNC would be more appropriate,
* but it is apparently not retried so TX Exc events
* are not received for it.
*/
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_DATA);
} else {
if (sta->flags & WLAN_STA_WMM) {
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_QOS_NULL);
size = sizeof(nulldata);
} else
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_NULLFUNC);
}
nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, sta->addr,
ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr,
ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, hapd->own_addr,
ETH_ALEN);
if (hostapd_drv_send_mlme(hapd, &nulldata, size) < 0)
perror("ap_handle_timer: send");
#endif /* CONFIG_NATIVE_WINDOWS */
hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
sta->flags & WLAN_STA_WMM);
} else if (sta->timeout_next != STA_REMOVE) {
int deauth = sta->timeout_next == STA_DEAUTH;

View file

@ -2501,6 +2501,20 @@ struct wpa_driver_ops {
* sched_scan is supported.
*/
int (*stop_sched_scan)(void *priv);
/**
* poll_client - Probe (null data or such) the given station
* @priv: Private driver interface data
* @own_addr: MAC address of sending interface
* @addr: MAC address of the station to probe
* @qos: Indicates whether station is QoS station
*
* This function is used to verify whether an associated station is
* still present. This function does not need to be implemented if the
* driver provides such inactivity polling mechanism.
*/
void (*poll_client)(void *priv, const u8 *own_addr,
const u8 *addr, int qos);
};
@ -2918,7 +2932,15 @@ enum wpa_event_type {
/**
* EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
*/
EVENT_SCHED_SCAN_STOPPED
EVENT_SCHED_SCAN_STOPPED,
/**
* EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
*
* This event indicates that the station responded to the poll
* initiated with @poll_client.
*/
EVENT_DRIVER_CLIENT_POLL_OK
};
@ -3472,6 +3494,14 @@ union wpa_event_data {
const u8 *bssid;
const u8 *replay_ctr;
} driver_gtk_rekey;
/**
* struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
* @addr: station address
*/
struct client_poll {
u8 addr[ETH_ALEN];
} client_poll;
};
/**

View file

@ -1143,6 +1143,33 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
return mode;
}
static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
const u8 *addr, int qos)
{
struct ieee80211_hdr hdr;
os_memset(&hdr, 0, sizeof(hdr));
/*
* WLAN_FC_STYPE_NULLFUNC would be more appropriate,
* but it is apparently not retried so TX Exc events
* are not received for it.
* This is the reason the driver overrides the default
* handling.
*/
hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_DATA);
hdr.frame_control |=
host_to_le16(WLAN_FC_FROMDS);
os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr));
}
#else /* HOSTAPD */
struct wpa_driver_hostap_data {
@ -1650,6 +1677,7 @@ const struct wpa_driver_ops wpa_driver_hostap_ops = {
.get_hw_feature_data = hostap_get_hw_feature_data,
.set_ap_wps_ie = hostap_set_ap_wps_ie,
.set_freq = hostap_set_freq,
.poll_client = wpa_driver_hostap_poll_client,
#else /* HOSTAPD */
.get_bssid = wpa_driver_hostap_get_bssid,
.get_ssid = wpa_driver_hostap_get_ssid,

View file

@ -3328,5 +3328,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
NULL /* add_tspec */,
NULL /* add_sta_node */,
NULL /* sched_scan */,
NULL /* stop_sched_scan */
NULL /* stop_sched_scan */,
NULL /* poll_client */
};

View file

@ -7329,6 +7329,43 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
}
static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
int qos)
{
struct i802_bss *bss = priv;
struct {
struct ieee80211_hdr hdr;
u16 qos_ctl;
} STRUCT_PACKED nulldata;
size_t size;
/* Send data frame to poll STA and check whether this frame is ACKed */
os_memset(&nulldata, 0, sizeof(nulldata));
if (qos) {
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_QOS_NULL);
size = sizeof(nulldata);
} else {
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_NULLFUNC);
size = sizeof(struct ieee80211_hdr);
}
nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size) < 0)
wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
"send poll frame");
}
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@ -7399,4 +7436,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.remove_pmkid = nl80211_remove_pmkid,
.flush_pmkid = nl80211_flush_pmkid,
.set_rekey_info = nl80211_set_rekey_info,
.poll_client = nl80211_poll_client,
};

View file

@ -566,6 +566,16 @@ void ap_tx_status(void *ctx, const u8 *addr,
}
void ap_client_poll_ok(void *ctx, const u8 *addr)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->ap_iface)
hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr);
#endif /* NEED_AP_MLME */
}
void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len)
{
#ifdef NEED_AP_MLME

View file

@ -41,6 +41,7 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen, int verbose);
void ap_tx_status(void *ctx, const u8 *addr,
const u8 *buf, size_t len, int ack);
void ap_client_poll_ok(void *ctx, const u8 *addr);
void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);

View file

@ -2027,6 +2027,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
#endif /* CONFIG_AP */
break;
#ifdef CONFIG_AP
case EVENT_DRIVER_CLIENT_POLL_OK:
ap_client_poll_ok(wpa_s, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
if (wpa_s->ap_iface == NULL)
break;