diff --git a/src/eapol.c b/src/eapol.c index b31ff5f8..a2b580b8 100644 --- a/src/eapol.c +++ b/src/eapol.c @@ -603,6 +603,7 @@ struct eapol_sm { 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; @@ -619,6 +620,7 @@ 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); @@ -1462,14 +1464,12 @@ void eapol_sm_set_8021x_config(struct eapol_sm *sm, struct l_settings *settings) eap_load_settings(sm->eap, settings, "EAP-"); } -void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa, - const uint8_t *frame, size_t len) +static void eapol_rx_packet(struct eapol_sm *sm, + const uint8_t *frame, size_t len) { const struct eapol_header *eh; - struct eapol_sm *sm; /* Validate Header */ - if (len < sizeof(struct eapol_header)) return; @@ -1482,10 +1482,6 @@ void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa, if (len < (size_t) 4 + L_BE16_TO_CPU(eh->packet_len)) return; - sm = eapol_find_sm(ifindex, spa, aa); - if (!sm) - return; - switch (eh->packet_type) { case 0: /* EAPOL-EAP */ if (!sm->eap) { @@ -1512,7 +1508,7 @@ void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa, if (!sm->have_pmk) return; - eapol_key_handle(sm, ifindex, frame, len); + eapol_key_handle(sm, sm->ifindex, frame, len); break; default: @@ -1520,6 +1516,17 @@ 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 *spa, const uint8_t *aa, + const uint8_t *frame, size_t len) +{ + struct eapol_sm *sm = eapol_find_sm(ifindex, spa, aa); + + if (!sm) + return; + + eapol_rx_packet(sm, frame, len); +} + void __eapol_update_replay_counter(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa, uint64_t replay_counter) { @@ -1608,6 +1615,33 @@ struct l_io *eapol_open_pae(uint32_t index) 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; +} + /* * Default implementation of the frame transmission function. * This function expects an fd to be passed as user_data @@ -1649,6 +1683,12 @@ 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); } diff --git a/src/eapol.h b/src/eapol.h index d5d48f9c..00259345 100644 --- a/src/eapol.h +++ b/src/eapol.h @@ -202,7 +202,7 @@ 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 eapol_sm *sm); +void eapol_start(uint32_t ifindex, struct l_io *io, struct eapol_sm *sm); void eapol_cancel(uint32_t ifindex); bool eapol_init(); diff --git a/src/netdev.c b/src/netdev.c index bed855d7..f85e04a7 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -53,7 +53,6 @@ struct netdev { char name[IFNAMSIZ]; uint32_t type; uint8_t addr[ETH_ALEN]; - struct l_io *eapol_io; struct device *device; netdev_event_func_t event_filter; @@ -62,6 +61,7 @@ struct netdev { void *user_data; struct l_genl_msg *associate_msg; struct eapol_sm *sm; + struct l_io *eapol_io; uint8_t remote_addr[ETH_ALEN]; uint32_t pairwise_new_key_cmd_id; uint32_t pairwise_set_key_cmd_id; @@ -93,31 +93,6 @@ static void do_debug(const char *str, void *user_data) l_info("%s%s", prefix, str); } -static bool eapol_read(struct l_io *io, void *user_data) -{ - struct netdev *netdev = 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; - } - - __eapol_rx_packet(netdev->index, netdev->addr, sll.sll_addr, - frame, bytes); - - return true; -} - struct cb_data { netdev_command_func_t callback; void *user_data; @@ -233,6 +208,9 @@ static void netdev_free(void *data) if (netdev->sm) { eapol_sm_free(netdev->sm); netdev->sm = NULL; + + l_io_destroy(netdev->eapol_io); + netdev->eapol_io = NULL; } if (netdev->associate_msg) { @@ -240,9 +218,6 @@ static void netdev_free(void *data) netdev->associate_msg = NULL; } - l_io_destroy(netdev->eapol_io); - netdev->eapol_io = NULL; - netdev_set_linkmode_and_operstate(netdev->index, 0, IF_OPER_DOWN, netdev_operstate_down_cb, L_UINT_TO_PTR(netdev->index)); @@ -838,6 +813,21 @@ static void netdev_set_rekey_offload(uint32_t ifindex, } +static void netdev_connect_failed(struct netdev *netdev, + enum netdev_result result) +{ + if (netdev->sm) { + eapol_sm_free(netdev->sm); + netdev->sm = NULL; + + l_io_destroy(netdev->eapol_io); + netdev->eapol_io = NULL; + } + + if (netdev->connect_cb) + netdev->connect_cb(netdev, result, netdev->user_data); +} + static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev) { @@ -873,9 +863,10 @@ static void netdev_connect_event(struct l_genl_msg *msg, if (netdev->sm) { eapol_sm_set_tx_user_data(netdev->sm, L_INT_TO_PTR(l_io_get_fd(netdev->eapol_io))); - eapol_start(netdev->index, netdev->sm); + eapol_start(netdev->index, netdev->eapol_io, netdev->sm); netdev->sm = NULL; + netdev->eapol_io = NULL; if (netdev->event_filter) netdev->event_filter(netdev, @@ -888,13 +879,7 @@ static void netdev_connect_event(struct l_genl_msg *msg, return; error: - if (netdev->sm) { - eapol_sm_free(netdev->sm); - netdev->sm = NULL; - - if (netdev->connect_cb) - netdev->connect_cb(netdev, NETDEV_RESULT_ASSOCIATION_FAILED, - netdev->user_data); + netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED); } static void netdev_authenticate_event(struct l_genl_msg *msg, @@ -923,9 +908,7 @@ static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data) return; } - if (netdev->connect_cb) - netdev->connect_cb(netdev, NETDEV_RESULT_ASSOCIATION_FAILED, - netdev->user_data); + netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED); } static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev, @@ -1003,9 +986,25 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss, netdev->event_filter = event_filter; netdev->connect_cb = cb; netdev->user_data = user_data; - netdev->sm = sm; memcpy(netdev->remote_addr, bss->addr, ETH_ALEN); + 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; } @@ -1259,13 +1258,6 @@ static void netdev_get_interface_callback(struct l_genl_msg *msg, memcpy(netdev->addr, ifaddr, sizeof(netdev->addr)); memcpy(netdev->name, ifname, ifname_len); - netdev->eapol_io = eapol_open_pae(netdev->index); - if (netdev->eapol_io) - l_io_set_read_handler(netdev->eapol_io, eapol_read, - netdev, NULL); - else - l_error("Failed to open PAE socket"); - l_queue_push_tail(netdev_list, netdev); netdev_set_linkmode_and_operstate(netdev->index, 1,