]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DPP: Authentication exchange retries and channel iteration in hostapd
authorJouni Malinen <jouni@codeaurora.org>
Mon, 8 Jan 2018 03:19:05 +0000 (05:19 +0200)
committerJouni Malinen <jouni@qca.qualcomm.com>
Mon, 8 Jan 2018 03:19:05 +0000 (05:19 +0200)
This extends hostapd with previoiusly implemented wpa_supplicant
functionality to retry DPP Authentication Request/Response and to
iterate over possible negotiation channels.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/ap/ap_drv_ops.h
src/ap/dpp_hostapd.c
src/ap/hostapd.h

index bf8169ddd8565fc80b364557194e4ad72df135d8..db93fde7d6e3a005eea54fa04c740596555dd0d6 100644 (file)
@@ -103,6 +103,14 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
                                     unsigned int freq,
                                     unsigned int wait, const u8 *dst,
                                     const u8 *data, size_t len);
+static inline void
+hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
+{
+       if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
+           !hapd->drv_priv)
+               return;
+       hapd->driver->send_action_cancel_wait(hapd->drv_priv);
+}
 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
                         u16 auth_alg);
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
index 23b8ec6b5ff4056779d721f585ee8c34bb4cffc2..a606a7da1fbf383d15464fa4ac1937aa9fc0a72e 100644 (file)
 #include "dpp_hostapd.h"
 
 
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -278,9 +281,68 @@ int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
 }
 
 
+static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
+                                               void *timeout_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       struct dpp_authentication *auth = hapd->dpp_auth;
+
+       if (!auth || !auth->resp_msg)
+               return;
+
+       wpa_printf(MSG_DEBUG,
+                  "DPP: Retry Authentication Response after timeout");
+       wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+               " freq=%u type=%d",
+               MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+               DPP_PA_AUTHENTICATION_RESP);
+       hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
+                               wpabuf_head(auth->resp_msg),
+                               wpabuf_len(auth->resp_msg));
+}
+
+
+static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
+{
+       struct dpp_authentication *auth = hapd->dpp_auth;
+       unsigned int wait_time, max_tries;
+
+       if (!auth || !auth->resp_msg)
+               return;
+
+       if (hapd->dpp_resp_max_tries)
+               max_tries = hapd->dpp_resp_max_tries;
+       else
+               max_tries = 5;
+       auth->auth_resp_tries++;
+       if (auth->auth_resp_tries >= max_tries) {
+               wpa_printf(MSG_INFO,
+                          "DPP: No confirm received from initiator - stopping exchange");
+               hostapd_drv_send_action_cancel_wait(hapd);
+               dpp_auth_deinit(hapd->dpp_auth);
+               hapd->dpp_auth = NULL;
+               return;
+       }
+
+       if (hapd->dpp_resp_retry_time)
+               wait_time = hapd->dpp_resp_retry_time;
+       else
+               wait_time = 1000;
+       wpa_printf(MSG_DEBUG,
+                  "DPP: Schedule retransmission of Authentication Response frame in %u ms",
+               wait_time);
+       eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+       eloop_register_timeout(wait_time / 1000,
+                              (wait_time % 1000) * 1000,
+                              hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+}
+
+
 void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
                           const u8 *data, size_t data_len, int ok)
 {
+       struct dpp_authentication *auth = hapd->dpp_auth;
+
        wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
                   MAC2STR(dst), ok);
        wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
@@ -295,15 +357,133 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
        if (hapd->dpp_auth->remove_on_tx_status) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Terminate authentication exchange due to an earlier error");
+               eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+               eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+                                    hapd, NULL);
+               eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+                                    NULL);
+               hostapd_drv_send_action_cancel_wait(hapd);
                dpp_auth_deinit(hapd->dpp_auth);
                hapd->dpp_auth = NULL;
                return;
        }
 
-       if (hapd->dpp_auth_ok_on_ack) {
-               hapd->dpp_auth_ok_on_ack = 0;
+       if (hapd->dpp_auth_ok_on_ack)
                hostapd_dpp_auth_success(hapd, 1);
+
+       if (!is_broadcast_ether_addr(dst) && !ok) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Unicast DPP Action frame was not ACKed");
+               if (auth->waiting_auth_resp) {
+                       /* In case of DPP Authentication Request frame, move to
+                        * the next channel immediately. */
+                       hostapd_drv_send_action_cancel_wait(hapd);
+                       hostapd_dpp_auth_init_next(hapd);
+                       return;
+               }
+               if (auth->waiting_auth_conf) {
+                       hostapd_dpp_auth_resp_retry(hapd);
+                       return;
+               }
+       }
+
+       if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
+               /* Allow timeout handling to stop iteration if no response is
+                * received from a peer that has ACKed a request. */
+               auth->auth_req_ack = 1;
+       }
+
+       if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
+           hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+                          hapd->dpp_auth->curr_freq,
+                          hapd->dpp_auth->neg_freq);
+               hostapd_drv_send_action_cancel_wait(hapd);
+
+               if (hapd->dpp_auth->neg_freq !=
+                   hapd->iface->freq && hapd->iface->freq > 0) {
+                       /* TODO: Listen operation on non-operating channel */
+                       wpa_printf(MSG_INFO,
+                                  "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+                                  hapd->dpp_auth->neg_freq, hapd->iface->freq);
+               }
+       }
+
+       if (hapd->dpp_auth_ok_on_ack)
+               hapd->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       struct dpp_authentication *auth = hapd->dpp_auth;
+       unsigned int freq;
+       struct os_reltime now, diff;
+       unsigned int wait_time, diff_ms;
+
+       if (!auth || !auth->waiting_auth_resp)
+               return;
+
+       wait_time = hapd->dpp_resp_wait_time ?
+               hapd->dpp_resp_wait_time : 2000;
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
+       diff_ms = diff.sec * 1000 + diff.usec / 1000;
+       wpa_printf(MSG_DEBUG,
+                  "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
+                  wait_time, diff_ms);
+
+       if (auth->auth_req_ack && diff_ms >= wait_time) {
+               /* Peer ACK'ed Authentication Request frame, but did not reply
+                * with Authentication Response frame within two seconds. */
+               wpa_printf(MSG_INFO,
+                          "DPP: No response received from responder - stopping initiation attempt");
+               wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+               hostapd_drv_send_action_cancel_wait(hapd);
+               hostapd_dpp_listen_stop(hapd);
+               dpp_auth_deinit(auth);
+               hapd->dpp_auth = NULL;
+               return;
+       }
+
+       if (diff_ms >= wait_time) {
+               /* Authentication Request frame was not ACK'ed and no reply
+                * was receiving within two seconds. */
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Continue Initiator channel iteration");
+               hostapd_drv_send_action_cancel_wait(hapd);
+               hostapd_dpp_listen_stop(hapd);
+               hostapd_dpp_auth_init_next(hapd);
+               return;
        }
+
+       /* Driver did not support 2000 ms long wait_time with TX command, so
+        * schedule listen operation to continue waiting for the response.
+        *
+        * DPP listen operations continue until stopped, so simply schedule a
+        * new call to this function at the point when the two second reply
+        * wait has expired. */
+       wait_time -= diff_ms;
+
+       freq = auth->curr_freq;
+       if (auth->neg_freq > 0)
+               freq = auth->neg_freq;
+       wpa_printf(MSG_DEBUG,
+                  "DPP: Continue reply wait on channel %u MHz for %u ms",
+                  freq, wait_time);
+       hapd->dpp_in_response_listen = 1;
+
+       if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
+               /* TODO: Listen operation on non-operating channel */
+               wpa_printf(MSG_INFO,
+                          "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+                          freq, hapd->iface->freq);
+       }
+
+       eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+                              hostapd_dpp_reply_wait_timeout, hapd, NULL);
 }
 
 
@@ -468,14 +648,110 @@ fail:
 }
 
 
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+
+       if (!hapd->dpp_auth)
+               return;
+       wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+       hostapd_dpp_auth_init_next(hapd);
+}
+
+
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
+{
+       struct dpp_authentication *auth = hapd->dpp_auth;
+       const u8 *dst;
+       unsigned int wait_time, max_wait_time, freq, max_tries, used;
+       struct os_reltime now, diff;
+
+       if (!auth)
+               return -1;
+
+       if (auth->freq_idx == 0)
+               os_get_reltime(&hapd->dpp_init_iter_start);
+
+       if (auth->freq_idx >= auth->num_freq) {
+               auth->num_freq_iters++;
+               if (hapd->dpp_init_max_tries)
+                       max_tries = hapd->dpp_init_max_tries;
+               else
+                       max_tries = 5;
+               if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
+                       wpa_printf(MSG_INFO,
+                                  "DPP: No response received from responder - stopping initiation attempt");
+                       wpa_msg(hapd->msg_ctx, MSG_INFO,
+                               DPP_EVENT_AUTH_INIT_FAILED);
+                       eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+                                            hapd, NULL);
+                       hostapd_drv_send_action_cancel_wait(hapd);
+                       dpp_auth_deinit(hapd->dpp_auth);
+                       hapd->dpp_auth = NULL;
+                       return -1;
+               }
+               auth->freq_idx = 0;
+               eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+               if (hapd->dpp_init_retry_time)
+                       wait_time = hapd->dpp_init_retry_time;
+               else
+                       wait_time = 10000;
+               os_get_reltime(&now);
+               os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
+               used = diff.sec * 1000 + diff.usec / 1000;
+               if (used > wait_time)
+                       wait_time = 0;
+               else
+                       wait_time -= used;
+               wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
+                          wait_time);
+               eloop_register_timeout(wait_time / 1000,
+                                      (wait_time % 1000) * 1000,
+                                      hostapd_dpp_init_timeout, hapd,
+                                      NULL);
+               return 0;
+       }
+       freq = auth->freq[auth->freq_idx++];
+       auth->curr_freq = freq;
+
+       if (is_zero_ether_addr(auth->peer_bi->mac_addr))
+               dst = broadcast;
+       else
+               dst = auth->peer_bi->mac_addr;
+       hapd->dpp_auth_ok_on_ack = 0;
+       eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+       wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+       max_wait_time = hapd->dpp_resp_wait_time ?
+               hapd->dpp_resp_wait_time : 2000;
+       if (wait_time > max_wait_time)
+               wait_time = max_wait_time;
+       wait_time += 10; /* give the driver some extra time to complete */
+       eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+                              hostapd_dpp_reply_wait_timeout, hapd, NULL);
+       wait_time -= 10;
+       if (auth->neg_freq > 0 && freq != auth->neg_freq) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
+                          freq, auth->neg_freq);
+       }
+       wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+               " freq=%u type=%d",
+               MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
+       auth->auth_req_ack = 0;
+       os_get_reltime(&hapd->dpp_last_init);
+       return hostapd_drv_send_action(hapd, freq, wait_time,
+                                      dst,
+                                      wpabuf_head(hapd->dpp_auth->req_msg),
+                                      wpabuf_len(hapd->dpp_auth->req_msg));
+}
+
+
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
 {
        const char *pos;
        struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
-       const u8 *dst;
-       int res;
        u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
-       struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+       unsigned int neg_freq = 0;
 
        pos = os_strstr(cmd, " peer=");
        if (!pos)
@@ -520,41 +796,37 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
                        goto fail;
        }
 
-       if (hapd->dpp_auth)
+       pos = os_strstr(cmd, " neg_freq=");
+       if (pos)
+               neg_freq = atoi(pos + 10);
+
+       if (hapd->dpp_auth) {
+               eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+               eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+                                    hapd, NULL);
+               eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+                                    NULL);
+               hostapd_drv_send_action_cancel_wait(hapd);
                dpp_auth_deinit(hapd->dpp_auth);
-       /* TODO: hw_modes */
+       }
+
        hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi,
-                                      allowed_roles, 0, NULL, 0);
+                                      allowed_roles, neg_freq,
+                                      hapd->iface->hw_features,
+                                      hapd->iface->num_hw_features);
        if (!hapd->dpp_auth)
                goto fail;
        hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
        hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd);
 
-       /* TODO: Support iteration over all frequencies and filtering of
-        * frequencies based on locally enabled channels that allow initiation
-        * of transmission. */
+       hapd->dpp_auth->neg_freq = neg_freq;
 
-       if (is_zero_ether_addr(peer_bi->mac_addr)) {
-               dst = broadcast;
-       } else {
-               dst = peer_bi->mac_addr;
+       if (!is_zero_ether_addr(peer_bi->mac_addr))
                os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
                          ETH_ALEN);
-       }
-       hapd->dpp_auth_ok_on_ack = 0;
-
-       wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
-               " freq=%u type=%d",
-               MAC2STR(dst), hapd->dpp_auth->curr_freq,
-               DPP_PA_AUTHENTICATION_REQ);
-       res = hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
-                                     dst, wpabuf_head(hapd->dpp_auth->req_msg),
-                                     wpabuf_len(hapd->dpp_auth->req_msg));
 
-       return res;
+       return hostapd_dpp_auth_init_next(hapd);
 fail:
-       dpp_configuration_free(conf_sta);
-       dpp_configuration_free(conf_ap);
        return -1;
 }
 
@@ -880,7 +1152,8 @@ static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
 
 
 static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
-                                    const u8 *hdr, const u8 *buf, size_t len)
+                                    const u8 *hdr, const u8 *buf, size_t len,
+                                    unsigned int freq)
 {
        struct dpp_authentication *auth = hapd->dpp_auth;
        struct wpabuf *msg;
@@ -901,6 +1174,15 @@ static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
                return;
        }
 
+       eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+
+       if (auth->curr_freq != freq && auth->neg_freq == freq) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Responder accepted request for different negotiation channel");
+               auth->curr_freq = freq;
+       }
+
+       eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
        msg = dpp_auth_resp_rx(auth, hdr, buf, len);
        if (!msg) {
                if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
@@ -1392,7 +1674,7 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
                hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
                break;
        case DPP_PA_AUTHENTICATION_RESP:
-               hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len);
+               hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
                break;
        case DPP_PA_AUTHENTICATION_CONF:
                hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
@@ -1461,6 +1743,10 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
        if (!hapd->dpp_auth)
                return;
 
+       eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+       eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+       hostapd_drv_send_action_cancel_wait(hapd);
+
        if (ok)
                wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
        else
@@ -1718,6 +2004,9 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
 #endif /* CONFIG_TESTING_OPTIONS */
        if (!hapd->dpp_init_done)
                return;
+       eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+       eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+       eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
        dpp_auth_deinit(hapd->dpp_auth);
        hapd->dpp_auth = NULL;
        hostapd_dpp_pkex_remove(hapd, "*");
index 32de734ff80a084ef93db35b66e8bbaae3715d13..53d0f9a9aa01df1df008049dbed23bed6bdb0690 100644 (file)
@@ -347,6 +347,7 @@ struct hostapd_data {
        u8 dpp_allowed_roles;
        int dpp_qr_mutual;
        int dpp_auth_ok_on_ack;
+       int dpp_in_response_listen;
        struct gas_query_ap *gas;
        struct dpp_pkex *dpp_pkex;
        struct dpp_bootstrap_info *dpp_pkex_bi;
@@ -354,6 +355,13 @@ struct hostapd_data {
        char *dpp_pkex_identifier;
        char *dpp_pkex_auth_cmd;
        char *dpp_configurator_params;
+       struct os_reltime dpp_last_init;
+       struct os_reltime dpp_init_iter_start;
+       unsigned int dpp_init_max_tries;
+       unsigned int dpp_init_retry_time;
+       unsigned int dpp_resp_wait_time;
+       unsigned int dpp_resp_max_tries;
+       unsigned int dpp_resp_retry_time;
 #ifdef CONFIG_TESTING_OPTIONS
        char *dpp_config_obj_override;
        char *dpp_discovery_override;