mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-01-20 09:34:06 +01:00
eapol: Move to a single PAE socket
We used to open a socket for each wireless interface. This patch uses a single socket with an attached BPF to handle all EAPoL traffic via a single file descriptor.
This commit is contained in:
parent
8e50144d17
commit
baf72d7f86
257
src/eapol.c
257
src/eapol.c
@ -32,6 +32,7 @@
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/filter.h>
|
||||
#include <ell/ell.h>
|
||||
|
||||
#include "crypto.h"
|
||||
@ -42,13 +43,132 @@
|
||||
#include "eap.h"
|
||||
|
||||
struct l_queue *state_machines;
|
||||
eapol_tx_packet_func_t tx_packet = NULL;
|
||||
|
||||
eapol_get_nonce_func_t get_nonce = NULL;
|
||||
eapol_install_tk_func_t install_tk = NULL;
|
||||
eapol_install_gtk_func_t install_gtk = NULL;
|
||||
eapol_deauthenticate_func_t deauthenticate = NULL;
|
||||
eapol_rekey_offload_func_t rekey_offload = NULL;
|
||||
|
||||
static struct l_io *pae_io;
|
||||
eapol_tx_packet_func_t tx_packet = NULL;
|
||||
void *tx_user_data;
|
||||
|
||||
/*
|
||||
* BPF filter to match skb->dev->type == 1 (ARPHRD_ETHER) and
|
||||
* match skb->protocol == 0x888e (PAE).
|
||||
*/
|
||||
static struct sock_filter pae_filter[] = {
|
||||
{ 0x28, 0, 0, 0xfffff01c }, /* ldh #hatype */
|
||||
{ 0x15, 0, 3, 0x00000001 }, /* jne #1, drop */
|
||||
{ 0x28, 0, 0, 0xfffff000 }, /* ldh #proto */
|
||||
{ 0x15, 0, 1, 0x0000888e }, /* jne #0x888e, drop */
|
||||
{ 0x06, 0, 0, 0xffffffff }, /* ret #-1 */
|
||||
{ 0x06, 0, 0, 0000000000 }, /* drop: ret #0 */
|
||||
};
|
||||
|
||||
static const struct sock_fprog pae_fprog = { .len = 6, .filter = pae_filter };
|
||||
|
||||
static struct l_io *pae_open(void)
|
||||
{
|
||||
struct l_io *io;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
||||
htons(ETH_P_ALL));
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
|
||||
&pae_fprog, sizeof(pae_fprog)) < 0) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io = l_io_new(fd);
|
||||
l_io_set_close_on_destroy(io, true);
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
static bool pae_read(struct l_io *io, void *user_data)
|
||||
{
|
||||
int fd = l_io_get_fd(io);
|
||||
struct sockaddr_ll sll;
|
||||
socklen_t sll_len;
|
||||
ssize_t bytes;
|
||||
uint8_t frame[2304]; /* IEEE Std 802.11 ch. 8.2.3 */
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll_len = sizeof(sll);
|
||||
|
||||
bytes = recvfrom(fd, frame, sizeof(frame), 0,
|
||||
(struct sockaddr *) &sll, &sll_len);
|
||||
if (bytes <= 0) {
|
||||
l_error("EAPoL read socket: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sll.sll_halen != ETH_ALEN)
|
||||
return true;
|
||||
|
||||
__eapol_rx_packet(sll.sll_ifindex, sll.sll_addr, frame, bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pae_destroy()
|
||||
{
|
||||
pae_io = NULL;
|
||||
}
|
||||
|
||||
static void pae_write(uint32_t ifindex, const uint8_t *aa, const uint8_t *spa,
|
||||
const struct eapol_frame *ef)
|
||||
{
|
||||
size_t frame_size;
|
||||
struct sockaddr_ll sll;
|
||||
ssize_t r;
|
||||
int fd;
|
||||
|
||||
if (!pae_io) {
|
||||
if (tx_packet) /* Used for unit tests */
|
||||
tx_packet(ifindex, aa, spa, ef, tx_user_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fd = l_io_get_fd(pae_io);
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = ifindex;
|
||||
sll.sll_protocol = htons(ETH_P_PAE);
|
||||
sll.sll_halen = ETH_ALEN;
|
||||
memcpy(sll.sll_addr, aa, ETH_ALEN);
|
||||
|
||||
frame_size = sizeof(struct eapol_header) +
|
||||
L_BE16_TO_CPU(ef->header.packet_len);
|
||||
|
||||
r = sendto(fd, ef, frame_size, 0,
|
||||
(struct sockaddr *) &sll, sizeof(sll));
|
||||
if (r < 0)
|
||||
l_error("EAPoL write socket: %s", strerror(errno));
|
||||
}
|
||||
|
||||
void eapol_pae_open()
|
||||
{
|
||||
pae_io = pae_open();
|
||||
if (!pae_io)
|
||||
return;
|
||||
|
||||
l_io_set_read_handler(pae_io, pae_read, NULL, pae_destroy);
|
||||
}
|
||||
|
||||
void eapol_pae_close()
|
||||
{
|
||||
l_io_destroy(pae_io);
|
||||
}
|
||||
|
||||
#define VERIFY_IS_ZERO(field) \
|
||||
do { \
|
||||
unsigned int i; \
|
||||
@ -604,9 +724,7 @@ struct eapol_sm {
|
||||
uint8_t anonce[32];
|
||||
uint8_t ptk[64];
|
||||
void *user_data;
|
||||
void *tx_user_data;
|
||||
struct l_timeout *timeout;
|
||||
struct l_io *io;
|
||||
bool have_snonce:1;
|
||||
bool have_replay:1;
|
||||
bool ptk_complete:1;
|
||||
@ -623,7 +741,6 @@ static void eapol_sm_destroy(void *value)
|
||||
l_free(sm->own_ie);
|
||||
|
||||
l_timeout_remove(sm->timeout);
|
||||
l_io_destroy(sm->io);
|
||||
|
||||
if (sm->eap)
|
||||
eap_free(sm->eap);
|
||||
@ -746,11 +863,6 @@ void eapol_sm_set_user_data(struct eapol_sm *sm, void *user_data)
|
||||
sm->user_data = user_data;
|
||||
}
|
||||
|
||||
void eapol_sm_set_tx_user_data(struct eapol_sm *sm, void *user_data)
|
||||
{
|
||||
sm->tx_user_data = user_data;
|
||||
}
|
||||
|
||||
uint32_t eapol_sm_get_pairwise_cipher(struct eapol_sm *sm)
|
||||
{
|
||||
return sm->pairwise_cipher;
|
||||
@ -802,36 +914,6 @@ static void eapol_timeout(struct l_timeout *timeout, void *user_data)
|
||||
MPDU_REASON_CODE_4WAY_HANDSHAKE_TIMEOUT);
|
||||
}
|
||||
|
||||
static void eapol_tx_packet(struct eapol_sm *sm, const struct eapol_frame *ef)
|
||||
{
|
||||
size_t frame_size;
|
||||
struct sockaddr_ll sll;
|
||||
ssize_t r;
|
||||
int fd;
|
||||
|
||||
if (!sm->io) {
|
||||
tx_packet(sm->ifindex, sm->aa, sm->spa, ef, sm->tx_user_data);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = l_io_get_fd(sm->io);
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = sm->ifindex;
|
||||
sll.sll_protocol = htons(ETH_P_PAE);
|
||||
sll.sll_halen = ETH_ALEN;
|
||||
memcpy(sll.sll_addr, sm->aa, ETH_ALEN);
|
||||
|
||||
frame_size = sizeof(struct eapol_header) +
|
||||
L_BE16_TO_CPU(ef->header.packet_len);
|
||||
|
||||
r = sendto(fd, ef, frame_size, 0,
|
||||
(struct sockaddr *) &sll, sizeof(sll));
|
||||
if (r < 0)
|
||||
l_error("EAPoL write socket: %s", strerror(errno));
|
||||
}
|
||||
|
||||
static void eapol_handle_ptk_1_of_4(uint32_t ifindex, struct eapol_sm *sm,
|
||||
const struct eapol_key *ek)
|
||||
{
|
||||
@ -883,7 +965,7 @@ static void eapol_handle_ptk_1_of_4(uint32_t ifindex, struct eapol_sm *sm,
|
||||
}
|
||||
|
||||
memcpy(step2->key_mic_data, mic, sizeof(mic));
|
||||
eapol_tx_packet(sm, (struct eapol_frame *) step2);
|
||||
pae_write(sm->ifindex, sm->aa, sm->spa, (struct eapol_frame *) step2);
|
||||
l_free(step2);
|
||||
|
||||
l_timeout_remove(sm->timeout);
|
||||
@ -1208,7 +1290,7 @@ static void eapol_handle_ptk_3_of_4(uint32_t ifindex,
|
||||
goto fail;
|
||||
|
||||
memcpy(step4->key_mic_data, mic, sizeof(mic));
|
||||
eapol_tx_packet(sm, (struct eapol_frame *) step4);
|
||||
pae_write(sm->ifindex, sm->aa, sm->spa, (struct eapol_frame *) step4);
|
||||
|
||||
sm->ptk_complete = true;
|
||||
|
||||
@ -1290,7 +1372,7 @@ static void eapol_handle_gtk_1_of_2(uint32_t ifindex,
|
||||
goto done;
|
||||
|
||||
memcpy(step2->key_mic_data, mic, sizeof(mic));
|
||||
eapol_tx_packet(sm, (struct eapol_frame *) step2);
|
||||
pae_write(sm->ifindex, sm->aa, sm->spa, (struct eapol_frame *) step2);
|
||||
|
||||
if (install_gtk) {
|
||||
uint32_t cipher =
|
||||
@ -1304,9 +1386,7 @@ done:
|
||||
l_free(step2);
|
||||
}
|
||||
|
||||
static struct eapol_sm *eapol_find_sm(uint32_t ifindex,
|
||||
const uint8_t *spa,
|
||||
const uint8_t *aa)
|
||||
static struct eapol_sm *eapol_find_sm(uint32_t ifindex, const uint8_t *aa)
|
||||
{
|
||||
const struct l_queue_entry *entry;
|
||||
struct eapol_sm *sm;
|
||||
@ -1318,10 +1398,7 @@ static struct eapol_sm *eapol_find_sm(uint32_t ifindex,
|
||||
if (sm->ifindex != ifindex)
|
||||
continue;
|
||||
|
||||
if (memcmp(sm->spa, spa, 6))
|
||||
continue;
|
||||
|
||||
if (memcmp(sm->aa, aa, 6))
|
||||
if (memcmp(sm->aa, aa, ETH_ALEN))
|
||||
continue;
|
||||
|
||||
return sm;
|
||||
@ -1441,7 +1518,7 @@ static void eapol_eap_msg_cb(const uint8_t *eap_data, size_t len,
|
||||
|
||||
memcpy(frame->data, eap_data, len);
|
||||
|
||||
eapol_tx_packet(sm, frame);
|
||||
pae_write(sm->ifindex, sm->aa, sm->spa, frame);
|
||||
}
|
||||
|
||||
/* This respresentes the eapTimout, eapFail and eapSuccess messages */
|
||||
@ -1569,10 +1646,10 @@ static void eapol_rx_packet(struct eapol_sm *sm,
|
||||
}
|
||||
}
|
||||
|
||||
void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa,
|
||||
void __eapol_rx_packet(uint32_t ifindex, const uint8_t *aa,
|
||||
const uint8_t *frame, size_t len)
|
||||
{
|
||||
struct eapol_sm *sm = eapol_find_sm(ifindex, spa, aa);
|
||||
struct eapol_sm *sm = eapol_find_sm(ifindex, aa);
|
||||
|
||||
if (!sm)
|
||||
return;
|
||||
@ -1585,7 +1662,7 @@ void __eapol_update_replay_counter(uint32_t ifindex, const uint8_t *spa,
|
||||
{
|
||||
struct eapol_sm *sm;
|
||||
|
||||
sm = eapol_find_sm(ifindex, spa, aa);
|
||||
sm = eapol_find_sm(ifindex, aa);
|
||||
|
||||
if (!sm)
|
||||
return;
|
||||
@ -1601,6 +1678,11 @@ void __eapol_set_tx_packet_func(eapol_tx_packet_func_t func)
|
||||
tx_packet = func;
|
||||
}
|
||||
|
||||
void __eapol_set_tx_user_data(void *user_data)
|
||||
{
|
||||
tx_user_data = user_data;
|
||||
}
|
||||
|
||||
void __eapol_set_get_nonce_func(eapol_get_nonce_func_t func)
|
||||
{
|
||||
get_nonce = func;
|
||||
@ -1626,70 +1708,6 @@ void __eapol_set_rekey_offload_func(eapol_rekey_offload_func_t func)
|
||||
rekey_offload = func;
|
||||
}
|
||||
|
||||
struct l_io *eapol_open_pae(uint32_t index)
|
||||
{
|
||||
struct l_io *io;
|
||||
struct sockaddr_ll sll;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_PAE));
|
||||
if (fd < 0) {
|
||||
l_error("Failed to create PAE socket %s (%d)",
|
||||
strerror(errno), errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_protocol = htons(ETH_P_PAE);
|
||||
sll.sll_ifindex = index;
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
|
||||
l_error("Failed to bind PAE socket %s (%d)",
|
||||
strerror(errno), errno);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io = l_io_new(fd);
|
||||
if (!io) {
|
||||
l_error("Failed to create IO handling for PAE socket ");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l_io_set_close_on_destroy(io, true);
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
static bool eapol_read(struct l_io *io, void *user_data)
|
||||
{
|
||||
struct eapol_sm *sm = user_data;
|
||||
int fd = l_io_get_fd(io);
|
||||
struct sockaddr_ll sll;
|
||||
socklen_t sll_len;
|
||||
ssize_t bytes;
|
||||
uint8_t frame[2304]; /* IEEE Std 802.11 ch. 8.2.3 */
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll_len = sizeof(sll);
|
||||
|
||||
bytes = recvfrom(fd, frame, sizeof(frame), 0,
|
||||
(struct sockaddr *) &sll, &sll_len);
|
||||
if (bytes <= 0) {
|
||||
l_error("EAPoL read socket: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(sm->aa, sll.sll_addr, 6))
|
||||
return true;
|
||||
|
||||
eapol_rx_packet(sm, frame, bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool eapol_get_nonce(uint8_t nonce[])
|
||||
{
|
||||
return l_getrandom(nonce, 32);
|
||||
@ -1700,11 +1718,6 @@ void eapol_start(uint32_t ifindex, struct l_io *io, struct eapol_sm *sm)
|
||||
sm->ifindex = ifindex;
|
||||
sm->timeout = l_timeout_create(2, eapol_timeout, sm, NULL);
|
||||
|
||||
if (io) {
|
||||
sm->io = io;
|
||||
l_io_set_read_handler(io, eapol_read, sm, NULL);
|
||||
}
|
||||
|
||||
l_queue_push_head(state_machines, sm);
|
||||
}
|
||||
|
||||
|
11
src/eapol.h
11
src/eapol.h
@ -164,10 +164,11 @@ struct eapol_key *eapol_create_gtk_2_of_2(
|
||||
uint64_t key_replay_counter,
|
||||
bool is_wpa, uint8_t wpa_key_id);
|
||||
|
||||
void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa,
|
||||
void __eapol_rx_packet(uint32_t ifindex, const uint8_t *aa,
|
||||
const uint8_t *frame, size_t len);
|
||||
|
||||
void __eapol_set_tx_packet_func(eapol_tx_packet_func_t func);
|
||||
void __eapol_set_tx_user_data(void *user_data);
|
||||
|
||||
void __eapol_set_get_nonce_func(eapol_get_nonce_func_t func);
|
||||
void __eapol_set_install_tk_func(eapol_install_tk_func_t func);
|
||||
void __eapol_set_install_gtk_func(eapol_install_gtk_func_t func);
|
||||
@ -196,16 +197,16 @@ void eapol_sm_set_ap_wpa(struct eapol_sm *sm, const uint8_t *wpa_ie,
|
||||
bool eapol_sm_set_own_wpa(struct eapol_sm *sm, const uint8_t *wpa_ie,
|
||||
size_t len);
|
||||
void eapol_sm_set_user_data(struct eapol_sm *sm, void *user_data);
|
||||
void eapol_sm_set_tx_user_data(struct eapol_sm *sm, void *user_data);
|
||||
|
||||
uint32_t eapol_sm_get_pairwise_cipher(struct eapol_sm *sm);
|
||||
uint32_t eapol_sm_get_group_cipher(struct eapol_sm *sm);
|
||||
const uint8_t *eapol_sm_get_own_ie(struct eapol_sm *sm, size_t *out_ie_len);
|
||||
|
||||
struct l_io *eapol_open_pae(uint32_t index);
|
||||
|
||||
void eapol_start(uint32_t ifindex, struct l_io *io, struct eapol_sm *sm);
|
||||
void eapol_cancel(uint32_t ifindex);
|
||||
|
||||
void eapol_pae_open();
|
||||
void eapol_pae_close();
|
||||
|
||||
bool eapol_init();
|
||||
bool eapol_exit();
|
||||
|
22
src/netdev.c
22
src/netdev.c
@ -1125,25 +1125,8 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
|
||||
netdev->connect_cb = cb;
|
||||
netdev->user_data = user_data;
|
||||
memcpy(netdev->remote_addr, bss->addr, ETH_ALEN);
|
||||
|
||||
netdev->connected = true;
|
||||
|
||||
netdev->sm = sm;
|
||||
if (netdev->sm) {
|
||||
/*
|
||||
* Due to timing / race conditions, it is possible for
|
||||
* EAPoL packets to arrive before the netdev events
|
||||
* are received. Here we 'prime' the socket, so that
|
||||
* the data is available as soon as we call eapol_start
|
||||
*
|
||||
* If this isn't done, then we might 'miss' the first EAPoL
|
||||
* packet from the AP, and have to wait for the AP to
|
||||
* retransmit. This delays our handshake by 1-2 seconds
|
||||
*/
|
||||
netdev->eapol_io = eapol_open_pae(netdev->index);
|
||||
if (!netdev->eapol_io)
|
||||
l_error("Failed to open PAE socket");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1507,6 +1490,9 @@ static void netdev_create_from_genl(struct l_genl_msg *msg)
|
||||
|
||||
l_queue_push_tail(netdev_list, netdev);
|
||||
|
||||
if (l_queue_length(netdev_list) == 1)
|
||||
eapol_pae_open();
|
||||
|
||||
l_debug("Created interface %s[%d]", netdev->name, netdev->index);
|
||||
|
||||
/* Query interface flags */
|
||||
@ -1729,4 +1715,6 @@ void netdev_shutdown(void)
|
||||
|
||||
l_queue_destroy(netdev_list, netdev_free);
|
||||
netdev_list = NULL;
|
||||
|
||||
eapol_pae_close();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user