--- src/drivers/driver_nl80211.c | 116 ++++++++++++++++++++++++++++++++++++++---- 1 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 653cba5..1f0b7e0 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "nl80211_copy.h" #include "common.h" @@ -41,6 +42,20 @@ #include "rfkill.h" #include "driver.h" +#ifndef SO_WIFI_STATUS +# define SO_WIFI_STATUS 41 +# define SCM_WIFI_STATUS SO_WIFI_STATUS +/* XXX: different on parisc & sparc */ +#endif + +#ifndef SO_EE_ORIGIN_TXSTATUS +#define SO_EE_ORIGIN_TXSTATUS 4 +#endif + +#ifndef PACKET_TX_TIMESTAMP +#define PACKET_TX_TIMESTAMP 16 +#endif + #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock @@ -164,6 +179,7 @@ struct wpa_driver_nl80211_data { unsigned int pending_remain_on_chan:1; unsigned int poll_command_supported:1; unsigned int device_ap_sme:1; + unsigned int data_tx_status:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -176,9 +192,7 @@ struct wpa_driver_nl80211_data { struct i802_bss first_bss; -#ifdef CONFIG_AP int eapol_tx_sock; -#endif /* CONFIG_AP */ #ifdef HOSTAPD int eapol_sock; /* socket for EAPOL frames */ @@ -1704,6 +1718,7 @@ struct wiphy_info_data { unsigned int device_ap_sme:1; unsigned int poll_command_supported:1; unsigned int error:1; + unsigned int data_tx_status:1; }; @@ -1852,6 +1867,13 @@ broken_combination: if (tb[NL80211_ATTR_DEVICE_AP_MLME]) info->device_ap_sme = 1; + if (tb[NL80211_ATTR_FEATURE_FLAGS]) { + u32 flags = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]); + + if (flags & NL80211_FEATURE_SK_TX_STATUS) + info->data_tx_status = 1; + } + if (auth_supported) capa->flags |= WPA_DRIVER_FLAGS_SME; else if (!connect_supported) { @@ -1929,6 +1951,7 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->device_ap_sme = info.device_ap_sme; drv->poll_command_supported = info.poll_command_supported; + drv->data_tx_status = info.data_tx_status; return 0; } @@ -2114,6 +2137,66 @@ static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) } +static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, void *eloop_ctx, + void *handle) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + u8 data[2048]; + struct msghdr msg; + struct iovec entry; + struct { + struct cmsghdr cm; + char control[512]; + } control; + struct cmsghdr *cmsg; + int res, found_ee = 0, found_wifi = 0, acked = 0; + union wpa_event_data event; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + res = recvmsg(sock, &msg, MSG_ERRQUEUE); + /* if error or not fitting 802.3 header, return */ + if (res < 14) + return; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_WIFI_STATUS) { + int *ack; + + found_wifi = 1; + ack = (void *)CMSG_DATA(cmsg); + acked = *ack; + } + + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + + if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) + found_ee = 1; + } + } + + if (!found_ee || !found_wifi) + return; + + memset(&event, 0, sizeof(event)); + event.eapol_tx_status.dst = data; + event.eapol_tx_status.data = data + 14; + event.eapol_tx_status.data_len = res - 14; + event.eapol_tx_status.ack = acked; + wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); +} + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -2184,9 +2267,24 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, if (wpa_driver_nl80211_finish_drv_init(drv)) goto failed; -#ifdef CONFIG_AP drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); -#endif /* CONFIG_AP */ + if (drv->eapol_tx_sock < 0) + goto failed; + + if (drv->data_tx_status) { + int enabled = 1; + + if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, + &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: wifi status sockopt failed\n"); + drv->data_tx_status = 0; + } else { + eloop_register_read_sock(drv->eapol_tx_sock, + wpa_driver_nl80211_handle_eapol_tx_status, + drv, NULL); + } + } if (drv->global) dl_list_add(&drv->global->interfaces, &drv->list); @@ -2398,9 +2496,9 @@ static void wpa_driver_nl80211_deinit(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; -#ifdef CONFIG_AP + if (drv->data_tx_status) + eloop_unregister_read_sock(drv->eapol_tx_sock); close(drv->eapol_tx_sock); -#endif /* CONFIG_AP */ if (drv->nl_handle_preq) wpa_driver_nl80211_probe_req_report(bss, 0); @@ -4775,7 +4873,6 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) } -#ifdef CONFIG_AP static int nl80211_send_eapol_data(struct i802_bss *bss, const u8 *addr, const u8 *data, size_t data_len) @@ -4800,7 +4897,6 @@ static int nl80211_send_eapol_data(struct i802_bss *bss, wpa_printf(MSG_ERROR, "nl80211 eapol tx: %s", strerror(errno)); return ret; } -#endif /* CONFIG_AP */ static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; @@ -4817,10 +4913,8 @@ static int wpa_driver_nl80211_hapd_send_eapol( int res; int qos = flags & WPA_STA_WMM; -#ifdef CONFIG_AP - if (drv->device_ap_sme) + if (drv->device_ap_sme || drv->data_tx_status) return nl80211_send_eapol_data(bss, addr, data, data_len); -#endif /* CONFIG_AP */ len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + data_len; -- 1.7.6.3