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 */
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);
}
+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)
{
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 */
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;
* 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);
};
/**
* 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
};
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;
};
/**
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 {
.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,
NULL /* add_tspec */,
NULL /* add_sta_node */,
NULL /* sched_scan */,
- NULL /* stop_sched_scan */
+ NULL /* stop_sched_scan */,
+ NULL /* poll_client */
};
}
+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",
.remove_pmkid = nl80211_remove_pmkid,
.flush_pmkid = nl80211_flush_pmkid,
.set_rekey_info = nl80211_set_rekey_info,
+ .poll_client = nl80211_poll_client,
};
}
+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
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);
#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;