From 48ec6942cb381d95044cf832263a8336e68d7e8b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 22 Feb 2015 16:00:34 +0200 Subject: [PATCH] Fix Linux packet socket workaround to not close the socket too easily Commit e6dd8196e5daf39e4204ef8ecd26dd50fdca6040 ('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 --- src/l2_packet/l2_packet_linux.c | 60 ++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index c4e73f694..41de2f85d 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -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", - l2->ifname); - eloop_unregister_read_sock(l2->fd_br_rx); - close(l2->fd_br_rx); - l2->fd_br_rx = -1; + 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); }