@@ -277,5 +277,5 @@ int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
if (!hapd->nan_de)
return -1;
return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
- req_instance_id, NULL);
+ req_instance_id, NULL, NULL);
}
@@ -105,6 +105,19 @@ struct nan_de_service {
#define NAN_DE_RSSI_CLOSE_PROXIMITY (-70) /* dBm */
+struct nan_de_tracked_tx {
+ struct dl_list list;
+ u8 dst[ETH_ALEN];
+ u32 cookie;
+ u8 digest[SHA256_MAC_LEN];
+ bool with_wait;
+};
+
+enum nan_de_flush_tracked_tx_reason {
+ NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL,
+ NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED,
+};
+
struct nan_de {
u8 nmi[ETH_ALEN];
u8 cluster_id[ETH_ALEN];
@@ -132,6 +145,12 @@ struct nan_de {
/* RSSI threshold for close proximity, or zero if not limited */
int rssi_threshold;
+ /*
+ * list of transmit requests for which the caller requested
+ * status indicating if the frame was acknowledged or not.
+ */
+ struct dl_list tracked_tx;
+
#ifdef CONFIG_TESTING_OPTIONS
/*
* When set, multicast follow-up SDFs will be sent as Protected Dual of
@@ -175,6 +194,7 @@ struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
de->cfg.n_max = NAN_DE_N_MAX;
de->rssi_threshold = NAN_DE_RSSI_CLOSE_PROXIMITY;
+ dl_list_init(&de->tracked_tx);
return de;
}
@@ -210,14 +230,144 @@ static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv,
}
+static void nan_de_flush_tracked_tx(struct nan_de *de,
+ enum nan_de_flush_tracked_tx_reason reason)
+{
+ struct nan_de_tracked_tx *tx, *tmp;
+
+ dl_list_for_each_safe(tx, tmp, &de->tracked_tx,
+ struct nan_de_tracked_tx,
+ list) {
+ if (reason == NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED &&
+ !tx->with_wait)
+ continue;
+
+ de->cb.transmit_req_status(de->cb.ctx, tx->cookie,
+ false);
+ dl_list_del(&tx->list);
+ os_free(tx);
+ }
+}
+
+
static void nan_de_clear_pending(struct nan_de *de)
{
+ nan_de_flush_tracked_tx(de,
+ NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL);
+
de->listen_freq = 0;
de->tx_wait_status_freq = 0;
de->tx_wait_end_freq = 0;
}
+static int nan_de_track_tx_digest(const u8 *data, size_t len, u8 *digest)
+{
+ return sha256_vector(1, &data, &len, digest);
+}
+
+
+static struct nan_de_tracked_tx *
+nan_de_add_tracked_tx(struct nan_de *de, const u8 *dst, bool with_wait,
+ u32 cookie, const struct wpabuf *buf)
+{
+ struct nan_de_tracked_tx *tx;
+ u8 digest[SHA256_MAC_LEN];
+
+ if (!de->cb.transmit_req_status) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: No tx_status callback, cannot track Tx");
+ return NULL;
+ }
+
+ if (!cookie) {
+ wpa_printf(MSG_DEBUG, "NAN: Invalid cookie for Tx tracking");
+ return NULL;
+ }
+
+ if (nan_de_track_tx_digest(wpabuf_head(buf),
+ wpabuf_len(buf), digest)) {
+ wpa_printf(MSG_DEBUG, "NAN: Failed to compute Tx digest");
+ return NULL;
+ }
+
+ dl_list_for_each(tx, &de->tracked_tx, struct nan_de_tracked_tx, list) {
+ if (!ether_addr_equal(tx->dst, dst))
+ continue;
+
+ if (tx->cookie == cookie) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Already tracking Tx cookie %u to "
+ MACSTR, cookie, MAC2STR(dst));
+ return NULL;
+ }
+
+ if (os_memcmp(tx->digest, digest, SHA256_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Already tracking identical payload to "
+ MACSTR " (cookie %u)",
+ MAC2STR(dst), tx->cookie);
+ return NULL;
+ }
+ }
+
+ tx = os_zalloc(sizeof(*tx));
+ if (!tx)
+ return NULL;
+
+ os_memcpy(tx->dst, dst, ETH_ALEN);
+ tx->cookie = cookie;
+ tx->with_wait = with_wait;
+ os_memcpy(tx->digest, digest, SHA256_MAC_LEN);
+
+ dl_list_add(&de->tracked_tx, &tx->list);
+
+ wpa_printf(MSG_DEBUG, "NAN: Track Tx cookie %u", tx->cookie);
+ wpa_hexdump(MSG_DEBUG, "NAN: Track Tx digest",
+ tx->digest, SHA256_MAC_LEN);
+
+ return tx;
+}
+
+
+static void nan_de_tx_status_match(struct nan_de *de, const u8 *data,
+ size_t len, u8 acked)
+{
+ struct nan_de_tracked_tx *tx;
+ const struct ieee80211_mgmt *mgmt = (void *)data;
+ const u8 *pos = (void *)&mgmt->u.action;
+
+ if (len <= offsetof(struct ieee80211_mgmt, u.action))
+ return;
+
+ len = data + len - pos;
+
+ {
+ u8 digest[SHA256_MAC_LEN];
+
+ if (nan_de_track_tx_digest(pos, len, digest))
+ return;
+
+ dl_list_for_each(tx, &de->tracked_tx,
+ struct nan_de_tracked_tx, list) {
+ if (!ether_addr_equal(tx->dst, mgmt->da) ||
+ os_memcmp(tx->digest, digest, SHA256_MAC_LEN))
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Tx status for cookie=%u ack=%u",
+ tx->cookie, acked);
+
+ de->cb.transmit_req_status(de->cb.ctx, tx->cookie,
+ acked);
+ dl_list_del(&tx->list);
+ os_free(tx);
+ return;
+ }
+ }
+}
+
+
void nan_de_flush(struct nan_de *de)
{
unsigned int i;
@@ -294,16 +444,30 @@ static struct wpabuf * nan_de_alloc_sdf(struct nan_de *de, const u8 *dst,
static int nan_de_tx(struct nan_de *de, unsigned int freq,
unsigned int wait_time,
const u8 *dst, const u8 *src, const u8 *bssid,
- const struct wpabuf *buf)
+ const struct wpabuf *buf,
+ u32 *cookie)
{
+ struct nan_de_tracked_tx *tracked_tx = NULL;
int res;
if (!de->cb.tx)
return -1;
+ if (cookie) {
+ tracked_tx = nan_de_add_tracked_tx(de, dst, !!wait_time,
+ *cookie, buf);
+ if (!tracked_tx)
+ return -1;
+ }
+
res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf);
- if (res < 0)
+ if (res < 0) {
+ if (tracked_tx) {
+ dl_list_del(&tracked_tx->list);
+ os_free(tracked_tx);
+ }
return res;
+ }
de->tx_wait_status_freq = freq;
de->tx_wait_end_freq = wait_time ? freq : 0;
@@ -337,7 +501,8 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
enum nan_service_control_type type,
const u8 *dst, const u8 *a3, u8 req_instance_id,
const struct wpabuf *ssi,
- const struct wpabuf *attrs)
+ const struct wpabuf *attrs,
+ u32 *cookie)
{
struct wpabuf *buf;
size_t len = 0, sda_len, sdea_len;
@@ -514,7 +679,7 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
}
nan_de_tx(de, srv->sync ? 0 : srv->freq, srv->sync ? 0 : wait_time,
- dst, forced_addr, a3, buf);
+ dst, forced_addr, a3, buf, cookie);
wpabuf_free(buf);
}
@@ -619,7 +784,7 @@ static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv,
}
nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid,
- req_instance_id, srv->ssi, NULL);
+ req_instance_id, srv->ssi, NULL, NULL);
os_get_reltime(&srv->last_multicast);
}
@@ -1058,6 +1223,8 @@ void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst,
{
if (freq == de->tx_wait_status_freq)
de->tx_wait_status_freq = 0;
+
+ nan_de_tx_status_match(de, data, data_len, ack);
}
@@ -1067,6 +1234,10 @@ void nan_de_tx_wait_ended(struct nan_de *de)
wpa_printf(MSG_DEBUG,
"NAN: TX wait for response ended (freq=%u)",
de->tx_wait_end_freq);
+
+ nan_de_flush_tracked_tx(de,
+ NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED);
+
de->tx_wait_end_freq = 0;
nan_de_run_timer(de);
}
@@ -1525,7 +1696,7 @@ static bool nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
* Service Specific Info field if it received a matching
* unsolicited Publish message. */
nan_de_transmit(de, srv->id, NULL, NULL, peer_addr,
- instance_id, NULL);
+ instance_id, NULL, NULL);
}
send_event:
@@ -1630,7 +1801,8 @@ static bool nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_PUBLISH,
srv->publish.solicited_multicast ?
- network_id : peer_addr, a3, instance_id, srv->ssi, NULL);
+ network_id : peer_addr, a3, instance_id, srv->ssi, NULL,
+ NULL);
if (!srv->is_p2p && !srv->sync)
nan_de_pause_state(srv, peer_addr, instance_id);
@@ -2457,7 +2629,8 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id)
int nan_de_transmit(struct nan_de *de, int handle,
const struct wpabuf *ssi, const struct wpabuf *elems,
const u8 *peer_addr, u8 req_instance_id,
- const struct wpabuf *nan_attrs)
+ const struct wpabuf *nan_attrs,
+ u32 *cookie)
{
struct nan_de_service *srv;
const u8 *a3;
@@ -2488,7 +2661,8 @@ int nan_de_transmit(struct nan_de *de, int handle,
else
a3 = network_id;
nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
- peer_addr, a3, req_instance_id, ssi, nan_attrs);
+ peer_addr, a3, req_instance_id, ssi, nan_attrs,
+ cookie);
srv->listen_stopped = false;
return 0;
@@ -82,6 +82,7 @@ struct nan_callbacks {
unsigned int freq);
void (*add_extra_attrs)(void *ctx, struct wpabuf *buf);
bool (*is_peer_paired)(void *ctx, const u8 *addr);
+ void (*transmit_req_status)(void *ctx, u32 cookie, bool ack);
};
bool nan_de_is_nan_network_id(const u8 *addr);
@@ -270,7 +271,7 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id);
int nan_de_transmit(struct nan_de *de, int handle,
const struct wpabuf *ssi, const struct wpabuf *elems,
const u8 *peer_addr, u8 req_instance_id,
- const struct wpabuf *nan_attrs);
+ const struct wpabuf *nan_attrs, u32 *cookie);
void nan_de_dw_trigger(struct nan_de *de, int freq);
void nan_de_set_cluster_id(struct nan_de *de, const u8 *cluster_id);
@@ -1021,7 +1021,7 @@ static int wpas_nan_transmit_followup_cb(void *ctx, const u8 *peer_nmi,
return -1;
return nan_de_transmit(wpa_s->nan_de, handle, NULL, NULL,
- peer_nmi, req_instance_id, attrs);
+ peer_nmi, req_instance_id, attrs, NULL);
}
@@ -4729,7 +4729,7 @@ int wpas_nan_transmit(struct wpa_supplicant *wpa_s, int handle,
if (!wpa_s->nan_de)
return -1;
return nan_de_transmit(wpa_s->nan_de, handle, ssi, elems, peer_addr,
- req_instance_id, NULL);
+ req_instance_id, NULL, NULL);
}