Fix Linux packet socket workaround to not close the socket too easily

Commit e6dd8196e5 ('Work around Linux
packet socket regression') closed the workaround socket on the first
received EAPOL frame from the main packet socket. This can result in
closing the socket in cases where the kernel does not really work in the
expected way during the following initial association since
reauthentication/rekeying using EAPOL frames happens while operstate is
not dormant and as such, the frames can get delivered through the main
packet socket.

Fix this by closing the workaround socket only in case the first EAPOL
frame is received through the main packet socket. This case happens
while the interface is in dormant state and as such, is more likely to
show the more restricted case of kernel functionality.

In order to avoid processing the received EAPOL frames twice, verify a
checksum of the frame contents when receiving frames alternatively from
the main packet socket and the workaround socket.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2015-02-22 16:00:34 +02:00
parent 528a7d22d0
commit 48ec6942cb

View file

@ -14,6 +14,8 @@
#include "common.h"
#include "eloop.h"
#include "crypto/sha1.h"
#include "crypto/crypto.h"
#include "l2_packet.h"
@ -30,6 +32,9 @@ struct l2_packet_data {
/* For working around Linux packet socket behavior and regression. */
int fd_br_rx;
int last_from_br;
u8 last_hash[SHA1_MAC_LEN];
unsigned int num_rx, num_rx_br;
};
/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
@ -122,6 +127,7 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
struct sockaddr_ll ll;
socklen_t fromlen;
l2->num_rx++;
os_memset(&ll, 0, sizeof(ll));
fromlen = sizeof(ll);
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
@ -132,14 +138,41 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
return;
}
wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
__func__, MAC2STR(ll.sll_addr), (int) res);
if (l2->fd_br_rx >= 0) {
wpa_printf(MSG_DEBUG, "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
u8 hash[SHA1_MAC_LEN];
const u8 *addr[1];
size_t len[1];
/*
* Close the workaround socket if the kernel version seems to be
* able to deliver packets through the packet socket before
* authorization has been completed (in dormant state).
*/
if (l2->num_rx_br <= 1) {
wpa_printf(MSG_DEBUG,
"l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
l2->ifname);
eloop_unregister_read_sock(l2->fd_br_rx);
close(l2->fd_br_rx);
l2->fd_br_rx = -1;
}
addr[0] = buf;
len[0] = res;
sha1_vector(1, addr, len, hash);
if (l2->last_from_br &&
os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX",
__func__);
return;
}
os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
}
l2->last_from_br = 0;
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}
@ -151,7 +184,11 @@ static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
int res;
struct sockaddr_ll ll;
socklen_t fromlen;
u8 hash[SHA1_MAC_LEN];
const u8 *addr[1];
size_t len[1];
l2->num_rx_br++;
os_memset(&ll, 0, sizeof(ll));
fromlen = sizeof(ll);
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
@ -162,6 +199,19 @@ static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
return;
}
wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
__func__, MAC2STR(ll.sll_addr), (int) res);
addr[0] = buf;
len[0] = res;
sha1_vector(1, addr, len, hash);
if (!l2->last_from_br &&
os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__);
return;
}
l2->last_from_br = 1;
os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}