From ceb6da81b3d11acf7ccf801f7ff4d613e002e217 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 12 Jan 2022 10:08:40 -0800 Subject: [PATCH] dpp: support retransmitting frames with no ACK The DPP spec says nothing about how to handle re-transmits but it was found in testing this can happen relatively easily for a few reasons. If the configurator requests a channel switch but does not get onto the new channel quick enough the enrollee may have already sent the authenticate response and it was missed. Also by nature of how the kernel goes offchannel there are moments in time between ROC when the card is idle and not receiving any frames. Only frames where there was no ACK will be retransmitted. If the peer received the frame and dropped it resending the same frame wont do any good. --- src/dpp.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/src/dpp.c b/src/dpp.c index 17d0bd90..5f24de57 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -52,10 +52,15 @@ #include "src/scan.h" #include "src/network.h" #include "src/handshake.h" +#include "src/nl80211util.h" + +#define DPP_FRAME_MAX_RETRIES 5 static uint32_t netdev_watch; static struct l_genl_family *nl80211; static uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +static struct l_queue *dpp_list; +static uint32_t mlme_watch; enum dpp_state { DPP_STATE_NOTHING, @@ -118,6 +123,8 @@ struct dpp_sm { struct dpp_configuration *config; uint32_t connect_scan_id; + uint64_t frame_cookie; + uint8_t frame_retry; struct l_dbus_message *pending; }; @@ -174,6 +181,8 @@ static void dpp_reset(struct dpp_sm *dpp) dpp->state = DPP_STATE_NOTHING; dpp->new_freq = 0; + dpp->frame_retry = 0; + dpp->frame_cookie = 0; explicit_bzero(dpp->r_nonce, dpp->nonce_len); explicit_bzero(dpp->i_nonce, dpp->nonce_len); @@ -209,8 +218,16 @@ static void dpp_free(struct dpp_sm *dpp) static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data) { - if (l_genl_msg_get_error(msg) < 0) + struct dpp_sm *dpp = user_data; + + if (l_genl_msg_get_error(msg) < 0) { l_error("Error sending frame"); + return; + } + + if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &dpp->frame_cookie, + NL80211_ATTR_UNSPEC) < 0) + l_error("Error parsing frame cookie"); } static void dpp_send_frame(struct dpp_sm *dpp, @@ -1576,6 +1593,59 @@ static void dpp_handle_frame(const struct mmpdu_header *frame, } } +static bool match_wdev(const void *a, const void *b) +{ + const struct dpp_sm *dpp = a; + const uint64_t *wdev_id = b; + + return *wdev_id == dpp->wdev_id; +} + +static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data) +{ + struct dpp_sm *dpp; + uint64_t wdev_id = 0; + uint64_t cookie = 0; + bool ack = false; + struct iovec iov; + uint8_t cmd = l_genl_msg_get_command(msg); + + if (cmd != NL80211_CMD_FRAME_TX_STATUS) + return; + + if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id, + NL80211_ATTR_COOKIE, &cookie, + NL80211_ATTR_ACK, &ack, + NL80211_ATTR_FRAME, &iov, + NL80211_ATTR_UNSPEC) < 0) + return; + + dpp = l_queue_find(dpp_list, match_wdev, &wdev_id); + if (!dpp) + return; + + if (dpp->state <= DPP_STATE_PRESENCE) + return; + + /* + * Only want to handle the no-ACK case. Re-transmitting an ACKed + * frame likely wont do any good, at least in the case of DPP. + */ + if (dpp->frame_cookie != cookie || ack) + return; + + if (dpp->frame_retry > DPP_FRAME_MAX_RETRIES) { + dpp_reset(dpp); + return; + } + + l_debug("No ACK from peer, re-transmitting"); + + dpp->frame_retry++; + + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); +} + static void dpp_create(struct netdev *netdev) { struct l_dbus *dbus = dbus_get_bus(); @@ -1613,6 +1683,8 @@ static void dpp_create(struct netdev *netdev) dpp_conf_request_prefix, sizeof(dpp_conf_request_prefix), dpp_handle_config_request_frame, dpp, NULL); + + l_queue_push_tail(dpp_list, dpp); } static void dpp_netdev_watch(struct netdev *netdev, @@ -1819,6 +1891,8 @@ static void dpp_destroy_interface(void *user_data) { struct dpp_sm *dpp = user_data; + l_queue_remove(dpp_list, dpp); + dpp_free(dpp); } @@ -1835,6 +1909,12 @@ static int dpp_init(void) l_dbus_register_interface(dbus_get_bus(), IWD_DPP_INTERFACE, dpp_setup_interface, dpp_destroy_interface, false); + + mlme_watch = l_genl_family_register(nl80211, "mlme", dpp_mlme_notify, + NULL, NULL); + + dpp_list = l_queue_new(); + return 0; } @@ -1843,6 +1923,11 @@ static void dpp_exit(void) l_debug(""); netdev_watch_remove(netdev_watch); + + l_genl_family_unregister(nl80211, mlme_watch); + mlme_watch = 0; + + l_queue_destroy(dpp_list, (l_queue_destroy_func_t) dpp_free); } IWD_MODULE(dpp, dpp_init, dpp_exit);