nl80211: Use socket error queue for EAPOL TX status
This will allow getting TX status for EAPOL frames sent as data frames if the driver supports it. Signed-hostap: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
204dd3f420
commit
32ab485503
1 changed files with 113 additions and 13 deletions
|
@ -28,6 +28,7 @@
|
|||
#include <linux/rtnetlink.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include "nl80211_copy.h"
|
||||
|
||||
#include "common.h"
|
||||
|
@ -43,6 +44,26 @@
|
|||
#include "rfkill.h"
|
||||
#include "driver.h"
|
||||
|
||||
#ifndef SO_WIFI_STATUS
|
||||
# if defined(__sparc__)
|
||||
# define SO_WIFI_STATUS 0x0025
|
||||
# elif defined(__parisc__)
|
||||
# define SO_WIFI_STATUS 0x4022
|
||||
# else
|
||||
# define SO_WIFI_STATUS 41
|
||||
# endif
|
||||
|
||||
# define SCM_WIFI_STATUS SO_WIFI_STATUS
|
||||
#endif
|
||||
|
||||
#ifndef SO_EE_ORIGIN_TXSTATUS
|
||||
#define SO_EE_ORIGIN_TXSTATUS 4
|
||||
#endif
|
||||
|
||||
#ifndef PACKET_TX_TIMESTAMP
|
||||
#define PACKET_TX_TIMESTAMP 16
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "android_drv.h"
|
||||
#endif /* ANDROID */
|
||||
|
@ -211,6 +232,7 @@ struct wpa_driver_nl80211_data {
|
|||
unsigned int in_interface_list:1;
|
||||
unsigned int device_ap_sme:1;
|
||||
unsigned int poll_command_supported:1;
|
||||
unsigned int data_tx_status:1;
|
||||
|
||||
u64 remain_on_chan_cookie;
|
||||
u64 send_action_cookie;
|
||||
|
@ -223,9 +245,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 */
|
||||
|
@ -1898,6 +1918,7 @@ struct wiphy_info_data {
|
|||
unsigned int error:1;
|
||||
unsigned int device_ap_sme:1;
|
||||
unsigned int poll_command_supported:1;
|
||||
unsigned int data_tx_status:1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2085,6 +2106,13 @@ broken_combination:
|
|||
if (tb[NL80211_ATTR_DEVICE_AP_SME])
|
||||
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;
|
||||
}
|
||||
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
|
@ -2144,6 +2172,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;
|
||||
}
|
||||
|
@ -2282,6 +2311,68 @@ 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,
|
||||
|
@ -2309,9 +2400,7 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
|
|||
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
|
||||
drv->monitor_ifidx = -1;
|
||||
drv->monitor_sock = -1;
|
||||
#ifdef CONFIG_AP
|
||||
drv->eapol_tx_sock = -1;
|
||||
#endif /* CONFIG_AP */
|
||||
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
|
||||
|
||||
if (wpa_driver_nl80211_init_nl(drv)) {
|
||||
|
@ -2337,9 +2426,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);
|
||||
|
@ -2555,10 +2659,10 @@ 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);
|
||||
if (drv->eapol_tx_sock >= 0)
|
||||
close(drv->eapol_tx_sock);
|
||||
#endif /* CONFIG_AP */
|
||||
|
||||
if (bss->nl_preq.handle)
|
||||
wpa_driver_nl80211_probe_req_report(bss, 0);
|
||||
|
@ -5151,7 +5255,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)
|
||||
|
@ -5178,7 +5281,6 @@ static int nl80211_send_eapol_data(struct i802_bss *bss,
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_AP */
|
||||
|
||||
|
||||
static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
|
||||
|
@ -5195,10 +5297,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;
|
||||
|
|
Loading…
Reference in a new issue