l2_packet: Add build option to disable Linux packet socket workaround
Linux packet socket workaround(*) has an impact in performance when the workaround socket needs to be kept open to receive EAPOL frames. While this is normally avoided with a kernel that has the issue addressed by closing the workaround packet socket when detecting a frame through the main socket, it is possible for that mechanism to not be sufficient, e.g., when an open network connection (no EAPOL frames) is used. Add a build option (CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y) to disable the workaround. This build option is disabled by default and can be enabled explicitly on distributions which have an older kernel or a fix for the kernel regression. Also remove the unused variable num_rx. (*) Linux kernel commit 576eb62598f10c8c7fd75703fe89010cdcfff596 ('bridge: respect RFC2863 operational state') from 2012 introduced a regression for using wpa_supplicant with EAPOL frames and a station interface in a bridge. Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>
This commit is contained in:
parent
fde35be9b1
commit
67deaa582d
3 changed files with 25 additions and 2 deletions
|
@ -30,11 +30,13 @@ struct l2_packet_data {
|
||||||
int l2_hdr; /* whether to include layer 2 (Ethernet) header data
|
int l2_hdr; /* whether to include layer 2 (Ethernet) header data
|
||||||
* buffers */
|
* buffers */
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
/* For working around Linux packet socket behavior and regression. */
|
/* For working around Linux packet socket behavior and regression. */
|
||||||
int fd_br_rx;
|
int fd_br_rx;
|
||||||
int last_from_br;
|
int last_from_br;
|
||||||
u8 last_hash[SHA1_MAC_LEN];
|
u8 last_hash[SHA1_MAC_LEN];
|
||||||
unsigned int num_rx, num_rx_br;
|
unsigned int num_rx_br;
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
|
/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
|
||||||
|
@ -127,7 +129,6 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
struct sockaddr_ll ll;
|
struct sockaddr_ll ll;
|
||||||
socklen_t fromlen;
|
socklen_t fromlen;
|
||||||
|
|
||||||
l2->num_rx++;
|
|
||||||
os_memset(&ll, 0, sizeof(ll));
|
os_memset(&ll, 0, sizeof(ll));
|
||||||
fromlen = sizeof(ll);
|
fromlen = sizeof(ll);
|
||||||
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
|
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
|
||||||
|
@ -141,6 +142,7 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
|
wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
|
||||||
__func__, MAC2STR(ll.sll_addr), (int) res);
|
__func__, MAC2STR(ll.sll_addr), (int) res);
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
if (l2->fd_br_rx >= 0) {
|
if (l2->fd_br_rx >= 0) {
|
||||||
u8 hash[SHA1_MAC_LEN];
|
u8 hash[SHA1_MAC_LEN];
|
||||||
const u8 *addr[1];
|
const u8 *addr[1];
|
||||||
|
@ -173,10 +175,12 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
l2->last_from_br = 0;
|
l2->last_from_br = 0;
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
|
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
|
static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
{
|
{
|
||||||
struct l2_packet_data *l2 = eloop_ctx;
|
struct l2_packet_data *l2 = eloop_ctx;
|
||||||
|
@ -214,6 +218,7 @@ static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
|
os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
|
||||||
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
|
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
|
|
||||||
|
|
||||||
struct l2_packet_data * l2_packet_init(
|
struct l2_packet_data * l2_packet_init(
|
||||||
|
@ -233,7 +238,9 @@ struct l2_packet_data * l2_packet_init(
|
||||||
l2->rx_callback = rx_callback;
|
l2->rx_callback = rx_callback;
|
||||||
l2->rx_callback_ctx = rx_callback_ctx;
|
l2->rx_callback_ctx = rx_callback_ctx;
|
||||||
l2->l2_hdr = l2_hdr;
|
l2->l2_hdr = l2_hdr;
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
l2->fd_br_rx = -1;
|
l2->fd_br_rx = -1;
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
|
|
||||||
l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
|
l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
|
||||||
htons(protocol));
|
htons(protocol));
|
||||||
|
@ -289,6 +296,7 @@ struct l2_packet_data * l2_packet_init_bridge(
|
||||||
void *rx_callback_ctx, int l2_hdr)
|
void *rx_callback_ctx, int l2_hdr)
|
||||||
{
|
{
|
||||||
struct l2_packet_data *l2;
|
struct l2_packet_data *l2;
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
struct sock_filter ethertype_sock_filter_insns[] = {
|
struct sock_filter ethertype_sock_filter_insns[] = {
|
||||||
/* Load ethertype */
|
/* Load ethertype */
|
||||||
BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
|
BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
|
||||||
|
@ -304,12 +312,14 @@ struct l2_packet_data * l2_packet_init_bridge(
|
||||||
.filter = ethertype_sock_filter_insns,
|
.filter = ethertype_sock_filter_insns,
|
||||||
};
|
};
|
||||||
struct sockaddr_ll ll;
|
struct sockaddr_ll ll;
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
|
|
||||||
l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
|
l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
|
||||||
rx_callback_ctx, l2_hdr);
|
rx_callback_ctx, l2_hdr);
|
||||||
if (!l2)
|
if (!l2)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
/*
|
/*
|
||||||
* The Linux packet socket behavior has changed over the years and there
|
* The Linux packet socket behavior has changed over the years and there
|
||||||
* is an inconvenient regression in it that breaks RX for a specific
|
* is an inconvenient regression in it that breaks RX for a specific
|
||||||
|
@ -357,6 +367,7 @@ struct l2_packet_data * l2_packet_init_bridge(
|
||||||
}
|
}
|
||||||
|
|
||||||
eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
|
eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
|
|
||||||
return l2;
|
return l2;
|
||||||
}
|
}
|
||||||
|
@ -372,10 +383,12 @@ void l2_packet_deinit(struct l2_packet_data *l2)
|
||||||
close(l2->fd);
|
close(l2->fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
if (l2->fd_br_rx >= 0) {
|
if (l2->fd_br_rx >= 0) {
|
||||||
eloop_unregister_read_sock(l2->fd_br_rx);
|
eloop_unregister_read_sock(l2->fd_br_rx);
|
||||||
close(l2->fd_br_rx);
|
close(l2->fd_br_rx);
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
|
||||||
|
|
||||||
os_free(l2);
|
os_free(l2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1453,6 +1453,10 @@ ifdef CONFIG_IPV6
|
||||||
CFLAGS += -DCONFIG_IPV6
|
CFLAGS += -DCONFIG_IPV6
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifdef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
|
CFLAGS += -DCONFIG_NO_LINUX_PACKET_SOCKET_WAR
|
||||||
|
endif
|
||||||
|
|
||||||
ifdef NEED_BASE64
|
ifdef NEED_BASE64
|
||||||
OBJS += ../src/utils/base64.o
|
OBJS += ../src/utils/base64.o
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -279,6 +279,12 @@ CONFIG_BACKEND=file
|
||||||
# none = Empty template
|
# none = Empty template
|
||||||
#CONFIG_L2_PACKET=linux
|
#CONFIG_L2_PACKET=linux
|
||||||
|
|
||||||
|
# Disable Linux packet socket workaround applicable for station interface
|
||||||
|
# in a bridge for EAPOL frames. This should be uncommented only if the kernel
|
||||||
|
# is known to not have the regression issue in packet socket behavior with
|
||||||
|
# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
|
||||||
|
#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
|
||||||
|
|
||||||
# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
|
# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
|
||||||
CONFIG_PEERKEY=y
|
CONFIG_PEERKEY=y
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue