blob: 12fad3112bdc96e930dff0ed78c64211a183856b [file] [log] [blame]
/*
* NAN Discovery Engine
* Copyright (c) 2024, Qualcomm Innovation Center, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "ieee802_11_defs.h"
#include "nan.h"
#include "nan_de.h"
static const u8 nan_network_id[ETH_ALEN] =
{ 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
static const u8 wildcard_bssid[ETH_ALEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
enum nan_de_service_type {
NAN_DE_PUBLISH,
NAN_DE_SUBSCRIBE,
};
struct nan_de_service {
int id;
enum nan_de_service_type type;
char *service_name;
u8 service_id[NAN_SERVICE_ID_LEN];
struct nan_publish_params publish;
struct nan_subscribe_params subscribe;
enum nan_service_protocol_type srv_proto_type;
struct wpabuf *ssi;
struct wpabuf *elems;
struct os_reltime time_started;
struct os_reltime end_time;
struct os_reltime last_multicast;
struct os_reltime first_discovered;
struct os_reltime last_followup;
bool needs_fsd;
unsigned int freq;
unsigned int default_freq;
int *freq_list;
/* pauseState information for Publish function */
struct os_reltime pause_state_end;
u8 sel_peer_id;
u8 sel_peer_addr[ETH_ALEN];
/* Publish state - channel iteration */
bool in_multi_chan;
bool first_multi_chan;
int multi_chan_idx; /* index to freq_list[] */
struct os_reltime next_publish_state;
struct os_reltime next_publish_chan;
unsigned int next_publish_duration;
};
struct nan_de {
u8 nmi[ETH_ALEN];
bool ap;
struct nan_callbacks cb;
struct nan_de_service *service[NAN_DE_MAX_SERVICE];
unsigned int num_service;
int next_handle;
unsigned int ext_listen_freq;
unsigned int listen_freq;
unsigned int tx_wait_status_freq;
unsigned int tx_wait_end_freq;
};
struct nan_de * nan_de_init(const u8 *nmi, bool ap,
const struct nan_callbacks *cb)
{
struct nan_de *de;
de = os_zalloc(sizeof(*de));
if (!de)
return NULL;
os_memcpy(de->nmi, nmi, ETH_ALEN);
de->ap = ap;
os_memcpy(&de->cb, cb, sizeof(*cb));
return de;
}
static void nan_de_service_free(struct nan_de_service *srv)
{
os_free(srv->service_name);
wpabuf_free(srv->ssi);
wpabuf_free(srv->elems);
os_free(srv->freq_list);
os_free(srv);
}
static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv,
enum nan_de_reason reason)
{
if (!srv)
return;
if (srv->type == NAN_DE_PUBLISH && de->cb.publish_terminated)
de->cb.publish_terminated(de->cb.ctx, srv->id, reason);
if (srv->type == NAN_DE_SUBSCRIBE && de->cb.subscribe_terminated)
de->cb.subscribe_terminated(de->cb.ctx, srv->id, reason);
nan_de_service_free(srv);
}
static void nan_de_clear_pending(struct nan_de *de)
{
de->listen_freq = 0;
de->tx_wait_status_freq = 0;
de->tx_wait_end_freq = 0;
}
void nan_de_flush(struct nan_de *de)
{
unsigned int i;
if (!de)
return;
for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
nan_de_service_deinit(de, de->service[i],
NAN_DE_REASON_USER_REQUEST);
de->service[i] = NULL;
}
de->num_service = 0;
nan_de_clear_pending(de);
}
static void nan_de_pause_state(struct nan_de_service *srv, const u8 *peer_addr,
u8 peer_id)
{
wpa_printf(MSG_DEBUG, "NAN: Start pauseState");
os_get_reltime(&srv->pause_state_end);
srv->pause_state_end.sec += 60;
os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN);
srv->sel_peer_id = peer_id;
}
static void nan_de_unpause_state(struct nan_de_service *srv)
{
wpa_printf(MSG_DEBUG, "NAN: Stop pauseState");
srv->pause_state_end.sec = 0;
srv->pause_state_end.usec = 0;
os_memset(srv->sel_peer_addr, 0, ETH_ALEN);
srv->sel_peer_id = 0;
}
static struct wpabuf * nan_de_alloc_sdf(size_t len)
{
struct wpabuf *buf;
buf = wpabuf_alloc(2 + 4 + len);
if (buf) {
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
wpabuf_put_be32(buf, NAN_SDF_VENDOR_TYPE);
}
return buf;
}
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)
{
int res;
if (!de->cb.tx)
return -1;
res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf);
if (res < 0)
return res;
de->tx_wait_status_freq = freq;
de->tx_wait_end_freq = wait_time ? freq : 0;
return res;
}
static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
unsigned int wait_time,
enum nan_service_control_type type,
const u8 *dst, u8 req_instance_id,
const struct wpabuf *ssi)
{
struct wpabuf *buf;
size_t len = 0, sda_len, sdea_len;
u8 ctrl = type;
u16 sdea_ctrl = 0;
/* Service Descriptor attribute */
sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1;
len += NAN_ATTR_HDR_LEN + sda_len;
/* Service Descriptor Extension attribute */
sdea_len = 1 + 2;
if (ssi)
sdea_len += 2 + 4 + wpabuf_len(ssi);
len += NAN_ATTR_HDR_LEN + sdea_len;
/* Element Container attribute */
if (srv->elems)
len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems);
buf = nan_de_alloc_sdf(len);
if (!buf)
return;
/* Service Descriptor attribute */
wpabuf_put_u8(buf, NAN_ATTR_SDA);
wpabuf_put_le16(buf, sda_len);
wpabuf_put_data(buf, srv->service_id, NAN_SERVICE_ID_LEN);
wpabuf_put_u8(buf, srv->id); /* Instance ID */
wpabuf_put_u8(buf, req_instance_id); /* Requestor Instance ID */
wpabuf_put_u8(buf, ctrl);
/* Service Descriptor Extension attribute */
if (srv->type == NAN_DE_PUBLISH || ssi) {
wpabuf_put_u8(buf, NAN_ATTR_SDEA);
wpabuf_put_le16(buf, sdea_len);
wpabuf_put_u8(buf, srv->id); /* Instance ID */
if (srv->type == NAN_DE_PUBLISH) {
if (srv->publish.fsd)
sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ;
if (srv->publish.fsd_gas)
sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS;
}
wpabuf_put_le16(buf, sdea_ctrl);
if (ssi) {
wpabuf_put_le16(buf, 4 + wpabuf_len(ssi));
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, srv->srv_proto_type);
wpabuf_put_buf(buf, ssi);
}
}
/* Element Container attribute */
if (srv->elems) {
wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER);
wpabuf_put_le16(buf, 1 + wpabuf_len(srv->elems));
wpabuf_put_u8(buf, 0); /* Map ID */
wpabuf_put_buf(buf, srv->elems);
}
/* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
* but there is no synchronization in USD as as such, no NAN Cluster
* either. Use Wildcard BSSID instead. */
nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, wildcard_bssid, buf);
wpabuf_free(buf);
}
static int nan_de_time_to_next_chan_change(struct nan_de_service *srv)
{
struct os_reltime tmp, diff, now;
if (os_reltime_before(&srv->next_publish_state,
&srv->next_publish_chan))
tmp = srv->next_publish_state;
else if (srv->in_multi_chan)
tmp = srv->next_publish_chan;
else
tmp = srv->next_publish_state;
os_get_reltime(&now);
os_reltime_sub(&tmp, &now, &diff);
return os_reltime_in_ms(&diff);
}
static void nan_de_set_publish_times(struct nan_de_service *srv)
{
os_get_reltime(&srv->next_publish_state);
srv->next_publish_chan = srv->next_publish_state;
/* Swap single/multi channel state in N * 100 TU */
os_reltime_add_ms(&srv->next_publish_state,
srv->next_publish_duration * 1024 / 1000);
/* Swap channel in multi channel state after 150 ms */
os_reltime_add_ms(&srv->next_publish_chan, 150);
}
static void nan_de_check_chan_change(struct nan_de_service *srv)
{
if (srv->next_publish_duration) {
/* Update end times for the first operation of the publish
* iteration */
nan_de_set_publish_times(srv);
srv->next_publish_duration = 0;
} else if (srv->in_multi_chan) {
if (!os_reltime_initialized(&srv->pause_state_end)) {
srv->multi_chan_idx++;
if (srv->freq_list[srv->multi_chan_idx] == 0)
srv->multi_chan_idx = 0;
srv->freq = srv->freq_list[srv->multi_chan_idx];
wpa_printf(MSG_DEBUG,
"NAN: Publish multi-channel change to %u MHz",
srv->freq);
}
os_get_reltime(&srv->next_publish_chan);
os_reltime_add_ms(&srv->next_publish_chan, 150);
}
}
static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv,
u8 req_instance_id)
{
enum nan_service_control_type type;
unsigned int wait_time = 100;
if (srv->type == NAN_DE_PUBLISH) {
int ms;
type = NAN_SRV_CTRL_PUBLISH;
nan_de_check_chan_change(srv);
ms = nan_de_time_to_next_chan_change(srv);
if (ms < 100)
ms = 100;
wait_time = ms;
} else if (srv->type == NAN_DE_SUBSCRIBE) {
type = NAN_SRV_CTRL_SUBSCRIBE;
} else {
return;
}
nan_de_tx_sdf(de, srv, wait_time, type, nan_network_id,
req_instance_id, srv->ssi);
os_get_reltime(&srv->last_multicast);
}
static void nan_de_add_srv(struct nan_de *de, struct nan_de_service *srv)
{
int ttl;
os_get_reltime(&srv->time_started);
ttl = srv->type == NAN_DE_PUBLISH ? srv->publish.ttl :
srv->subscribe.ttl;
if (ttl) {
srv->end_time = srv->time_started;
srv->end_time.sec += ttl;
}
de->service[srv->id - 1] = srv;
de->num_service++;
}
static void nan_de_del_srv(struct nan_de *de, struct nan_de_service *srv,
enum nan_de_reason reason)
{
de->service[srv->id - 1] = NULL;
nan_de_service_deinit(de, srv, reason);
de->num_service--;
if (de->num_service == 0)
nan_de_clear_pending(de);
}
static bool nan_de_srv_expired(struct nan_de_service *srv,
struct os_reltime *now)
{
if (os_reltime_initialized(&srv->end_time))
return os_reltime_before(&srv->end_time, now);
if (srv->type == NAN_DE_PUBLISH) {
/* Time out after one transmission (and wait for FSD) */
if (!os_reltime_initialized(&srv->last_multicast))
return false;
if (!srv->publish.fsd)
return true;
if (os_reltime_initialized(&srv->last_followup) &&
!os_reltime_expired(now, &srv->last_followup, 1))
return false;
if (os_reltime_expired(now, &srv->last_multicast, 1))
return true;
}
if (srv->type == NAN_DE_SUBSCRIBE) {
/* Time out after first DiscoveryResult event (and wait for
* FSD) */
if (!os_reltime_initialized(&srv->first_discovered))
return false;
if (!srv->needs_fsd)
return true;
if (os_reltime_initialized(&srv->last_followup) &&
!os_reltime_expired(now, &srv->last_followup, 1))
return false;
if (os_reltime_expired(now, &srv->first_discovered, 1))
return true;
}
return false;
}
static int nan_de_next_multicast(struct nan_de *de, struct nan_de_service *srv,
struct os_reltime *now)
{
unsigned int period;
struct os_reltime next, diff;
if (srv->type == NAN_DE_PUBLISH && !srv->publish.unsolicited)
return -1;
if (srv->type == NAN_DE_SUBSCRIBE && !srv->subscribe.active)
return -1;
if (!os_reltime_initialized(&srv->last_multicast))
return 0;
if (srv->type == NAN_DE_PUBLISH && srv->publish.ttl == 0)
return -1;
if (srv->type == NAN_DE_PUBLISH &&
os_reltime_initialized(&srv->pause_state_end))
return -1;
period = srv->type == NAN_DE_PUBLISH ?
srv->publish.announcement_period :
srv->subscribe.query_period;
if (period == 0)
period = 100;
next = srv->last_multicast;
os_reltime_add_ms(&next, period);
if (srv->type == NAN_DE_PUBLISH) {
if (!de->tx_wait_end_freq && srv->publish.unsolicited &&
os_reltime_before(&next, now))
return 0;
next = srv->next_publish_state;
}
if (os_reltime_before(&next, now))
return 0;
os_reltime_sub(&next, now, &diff);
return os_reltime_in_ms(&diff);
}
static int nan_de_srv_time_to_next(struct nan_de *de,
struct nan_de_service *srv,
struct os_reltime *now)
{
struct os_reltime diff;
int next = -1, tmp;
if (os_reltime_initialized(&srv->end_time)) {
os_reltime_sub(&srv->end_time, now, &diff);
tmp = os_reltime_in_ms(&diff);
if (next == -1 || tmp < next)
next = tmp;
}
tmp = nan_de_next_multicast(de, srv, now);
if (tmp >= 0 && (next == -1 || tmp < next))
next = tmp;
if (srv->type == NAN_DE_PUBLISH &&
os_reltime_initialized(&srv->last_multicast)) {
/* Time out after one transmission (and wait for FSD) */
tmp = srv->publish.fsd ? 1000 : 100;
if (next == -1 || tmp < next)
next = tmp;
}
if (srv->type == NAN_DE_SUBSCRIBE &&
os_reltime_initialized(&srv->first_discovered)) {
/* Time out after first DiscoveryResult event (and wait for
* FSD) */
tmp = srv->needs_fsd ? 1000 : 100;
if (next == -1 || tmp < next)
next = tmp;
}
if (os_reltime_initialized(&srv->next_publish_state)) {
os_reltime_sub(&srv->next_publish_state, now, &diff);
if (diff.sec < 0 || (diff.sec == 0 && diff.usec < 0))
tmp = 0;
else
tmp = os_reltime_in_ms(&diff);
if (next == -1 || tmp < next)
next = tmp;
}
return next;
}
static void nan_de_start_new_publish_state(struct nan_de_service *srv,
bool force_single)
{
unsigned int n;
if (force_single || !srv->freq_list || srv->freq_list[0] == 0)
srv->in_multi_chan = false;
else
srv->in_multi_chan = !srv->in_multi_chan;
/* Use hardcoded Nmin=5 and Nmax=10 and pick a random N from that range.
* Use same values for M. */
n = 5 + os_random() % 5;
srv->next_publish_duration = n * 100;
nan_de_set_publish_times(srv);
if (os_reltime_initialized(&srv->pause_state_end))
return;
if (srv->in_multi_chan && srv->freq_list && srv->freq_list[0]) {
if (!srv->first_multi_chan)
srv->multi_chan_idx++;
if (srv->freq_list[srv->multi_chan_idx] == 0)
srv->multi_chan_idx = 0;
srv->first_multi_chan = false;
srv->freq = srv->freq_list[srv->multi_chan_idx];
} else {
srv->freq = srv->default_freq;
}
wpa_printf(MSG_DEBUG,
"NAN: Publish in %s channel state for %u TU; starting with %u MHz",
srv->in_multi_chan ? "multi" : "single", n * 100, srv->freq);
}
static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
{
struct nan_de *de = eloop_ctx;
unsigned int i;
int next = -1;
bool started = false;
struct os_reltime now;
os_get_reltime(&now);
for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
struct nan_de_service *srv = de->service[i];
int srv_next;
if (!srv)
continue;
if (nan_de_srv_expired(srv, &now)) {
wpa_printf(MSG_DEBUG, "NAN: Service id %d expired",
srv->id);
nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT);
continue;
}
if (os_reltime_initialized(&srv->next_publish_state) &&
os_reltime_before(&srv->next_publish_state, &now))
nan_de_start_new_publish_state(srv, false);
if (srv->type == NAN_DE_PUBLISH &&
os_reltime_initialized(&srv->pause_state_end) &&
(os_reltime_before(&srv->pause_state_end, &now) ||
(srv->publish.fsd &&
os_reltime_initialized(&srv->last_followup) &&
os_reltime_expired(&now, &srv->last_followup, 1))))
nan_de_unpause_state(srv);
srv_next = nan_de_srv_time_to_next(de, srv, &now);
if (srv_next >= 0 && (next == -1 || srv_next < next))
next = srv_next;
if (srv_next == 0 && !started &&
de->listen_freq == 0 && de->ext_listen_freq == 0 &&
de->tx_wait_end_freq == 0 &&
nan_de_next_multicast(de, srv, &now) == 0) {
started = true;
nan_de_tx_multicast(de, srv, 0);
}
if (!started && de->cb.listen &&
de->listen_freq == 0 && de->ext_listen_freq == 0 &&
de->tx_wait_end_freq == 0 &&
((srv->type == NAN_DE_PUBLISH &&
!srv->publish.unsolicited && srv->publish.solicited) ||
(srv->type == NAN_DE_SUBSCRIBE &&
!srv->subscribe.active))) {
int duration = 1000;
if (srv->type == NAN_DE_PUBLISH) {
nan_de_check_chan_change(srv);
duration = nan_de_time_to_next_chan_change(srv);
if (duration < 150)
duration = 150;
}
started = true;
if (de->cb.listen(de->cb.ctx, srv->freq, duration) == 0)
de->listen_freq = srv->freq;
}
}
if (next < 0)
return;
if (next == 0)
next = 1;
wpa_printf(MSG_DEBUG, "NAN: Next timer in %u ms", next);
eloop_register_timeout(next / 1000, (next % 1000) * 1000, nan_de_timer,
de, NULL);
}
static void nan_de_run_timer(struct nan_de *de)
{
eloop_cancel_timeout(nan_de_timer, de, NULL);
eloop_register_timeout(0, 0, nan_de_timer, de, NULL);
}
void nan_de_deinit(struct nan_de *de)
{
eloop_cancel_timeout(nan_de_timer, de, NULL);
nan_de_flush(de);
os_free(de);
}
void nan_de_listen_started(struct nan_de *de, unsigned int freq,
unsigned int duration)
{
if (freq != de->listen_freq)
de->ext_listen_freq = freq;
}
void nan_de_listen_ended(struct nan_de *de, unsigned int freq)
{
if (freq == de->ext_listen_freq)
de->ext_listen_freq = 0;
if (freq == de->listen_freq) {
de->listen_freq = 0;
nan_de_run_timer(de);
}
}
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;
}
void nan_de_tx_wait_ended(struct nan_de *de)
{
de->tx_wait_end_freq = 0;
nan_de_run_timer(de);
}
static const u8 *
nan_de_get_attr(const u8 *buf, size_t len, enum nan_attr_id id,
unsigned int skip)
{
const u8 *pos = buf, *end = buf + len;
while (end - pos >= NAN_ATTR_HDR_LEN) {
const u8 *attr = pos;
u8 attr_id;
u16 attr_len;
attr_id = *pos++;
attr_len = WPA_GET_LE16(pos);
pos += 2;
if (attr_len > end - pos) {
wpa_printf(MSG_DEBUG,
"NAN: Truncated attribute %u (len %u; left %zu)",
attr_id, attr_len, end - pos);
break;
}
if (attr_id == id) {
if (skip == 0)
return attr;
skip--;
}
pos += attr_len;
}
return NULL;
}
static void nan_de_get_sdea(const u8 *buf, size_t len, u8 instance_id,
u16 *sdea_control,
enum nan_service_protocol_type *srv_proto_type,
const u8 **ssi, size_t *ssi_len)
{
unsigned int skip;
const u8 *sdea, *end;
u16 sdea_len;
for (skip = 0; ; skip++) {
sdea = nan_de_get_attr(buf, len, NAN_ATTR_SDEA, skip);
if (!sdea)
break;
sdea++;
sdea_len = WPA_GET_LE16(sdea);
sdea += 2;
if (sdea_len < 1 + 2)
continue;
end = sdea + sdea_len;
if (instance_id != *sdea++)
continue; /* Mismatching Instance ID */
*sdea_control = WPA_GET_LE16(sdea);
sdea += 2;
if (*sdea_control & NAN_SDEA_CTRL_RANGE_LIMIT) {
if (end - sdea < 4)
continue;
sdea += 4;
}
if (*sdea_control & NAN_SDEA_CTRL_SRV_UPD_INDIC) {
if (end - sdea < 1)
continue;
sdea++;
}
if (end - sdea >= 2) {
u16 srv_info_len;
srv_info_len = WPA_GET_LE16(sdea);
sdea += 2;
if (srv_info_len > end - sdea)
continue;
if (srv_info_len >= 4 &&
WPA_GET_BE24(sdea) == OUI_WFA) {
*srv_proto_type = sdea[3];
*ssi = sdea + 4;
*ssi_len = srv_info_len - 4;
}
}
}
}
static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
const u8 *peer_addr, u8 instance_id,
u8 req_instance_id, u16 sdea_control,
enum nan_service_protocol_type srv_proto_type,
const u8 *ssi, size_t ssi_len)
{
/* Subscribe function processing of a receive Publish message */
if (!os_reltime_initialized(&srv->first_discovered)) {
os_get_reltime(&srv->first_discovered);
srv->needs_fsd = sdea_control & NAN_SDEA_CTRL_FSD_REQ;
nan_de_run_timer(de);
}
if (srv->subscribe.active && req_instance_id == 0) {
/* Active subscriber replies with a Subscribe message if it
* received a matching unsolicited Publish message. */
nan_de_tx_multicast(de, srv, instance_id);
}
if (!srv->subscribe.active && req_instance_id == 0) {
/* Passive subscriber replies with a Follow-up message without
* Service Specific Info field if it received a matching
* unsolicited Publish message. */
nan_de_transmit(de, srv->id, NULL, NULL, peer_addr,
instance_id);
}
if (de->cb.discovery_result)
de->cb.discovery_result(
de->cb.ctx, srv->id, srv_proto_type,
ssi, ssi_len, instance_id,
peer_addr,
sdea_control & NAN_SDEA_CTRL_FSD_REQ,
sdea_control & NAN_SDEA_CTRL_FSD_GAS);
}
static bool nan_de_filter_match(struct nan_de_service *srv,
const u8 *matching_filter,
size_t matching_filter_len)
{
const u8 *pos, *end;
/* Since we do not currently support matching_filter_rx values for the
* local Publish function, any matching filter with at least one
* <length,value> pair with length larger than zero implies a mismatch.
*/
if (!matching_filter)
return true;
pos = matching_filter;
end = matching_filter + matching_filter_len;
while (pos < end) {
u8 len;
len = *pos++;
if (len > end - pos)
break;
if (len) {
/* A non-empty Matching Filter entry: no match since
* there is no local matching_filter_rx. */
return false;
}
}
return true;
}
static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
const u8 *peer_addr, u8 instance_id,
const u8 *matching_filter,
size_t matching_filter_len,
enum nan_service_protocol_type srv_proto_type,
const u8 *ssi, size_t ssi_len)
{
struct wpabuf *buf;
size_t len = 0, sda_len, sdea_len;
u8 ctrl = 0;
u16 sdea_ctrl = 0;
/* Publish function processing of a receive Subscribe message */
if (!nan_de_filter_match(srv, matching_filter, matching_filter_len))
return;
if (!srv->publish.solicited)
return;
if (os_reltime_initialized(&srv->pause_state_end) &&
(!ether_addr_equal(peer_addr, srv->sel_peer_addr) ||
instance_id != srv->sel_peer_id)) {
wpa_printf(MSG_DEBUG,
"NAN: In pauseState - ignore Subscribe message from another subscriber");
return;
}
/* Reply with a solicited Publish message */
/* Service Descriptor attribute */
sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1;
len += NAN_ATTR_HDR_LEN + sda_len;
/* Service Descriptor Extension attribute */
sdea_len = 1 + 2;
if (srv->ssi)
sdea_len += 2 + 4 + wpabuf_len(srv->ssi);
len += NAN_ATTR_HDR_LEN + sdea_len;
/* Element Container attribute */
if (srv->elems)
len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems);
buf = nan_de_alloc_sdf(len);
if (!buf)
return;
/* Service Descriptor attribute */
wpabuf_put_u8(buf, NAN_ATTR_SDA);
wpabuf_put_le16(buf, sda_len);
wpabuf_put_data(buf, srv->service_id, NAN_SERVICE_ID_LEN);
wpabuf_put_u8(buf, srv->id); /* Instance ID */
wpabuf_put_u8(buf, instance_id); /* Requestor Instance ID */
ctrl |= NAN_SRV_CTRL_PUBLISH;
wpabuf_put_u8(buf, ctrl);
/* Service Descriptor Extension attribute */
if (srv->type == NAN_DE_PUBLISH || srv->ssi) {
wpabuf_put_u8(buf, NAN_ATTR_SDEA);
wpabuf_put_le16(buf, sdea_len);
wpabuf_put_u8(buf, srv->id); /* Instance ID */
if (srv->type == NAN_DE_PUBLISH) {
if (srv->publish.fsd)
sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ;
if (srv->publish.fsd_gas)
sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS;
}
wpabuf_put_le16(buf, sdea_ctrl);
if (srv->ssi) {
wpabuf_put_le16(buf, 4 + wpabuf_len(srv->ssi));
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, srv->srv_proto_type);
wpabuf_put_buf(buf, srv->ssi);
}
}
/* Element Container attribute */
if (srv->elems) {
wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER);
wpabuf_put_le16(buf, 1 + wpabuf_len(srv->elems));
wpabuf_put_u8(buf, 0); /* Map ID */
wpabuf_put_buf(buf, srv->elems);
}
/* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
* but there is no synchronization in USD as as such, no NAN Cluster
* either. Use Wildcard BSSID instead. */
nan_de_tx(de, srv->freq, 100,
srv->publish.solicited_multicast ? nan_network_id : peer_addr,
de->nmi, wildcard_bssid, buf);
wpabuf_free(buf);
nan_de_pause_state(srv, peer_addr, instance_id);
if (!srv->publish.disable_events && de->cb.replied)
de->cb.replied(de->cb.ctx, srv->id, peer_addr, instance_id,
srv_proto_type, ssi, ssi_len);
}
static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv,
const u8 *peer_addr, u8 instance_id,
const u8 *ssi, size_t ssi_len)
{
/* Follow-up function processing of a receive Follow-up message for a
* Subscribe or Publish instance */
if (srv->type == NAN_DE_PUBLISH &&
os_reltime_initialized(&srv->pause_state_end) &&
(!ether_addr_equal(peer_addr, srv->sel_peer_addr) ||
instance_id != srv->sel_peer_id ||
!ssi)) {
wpa_printf(MSG_DEBUG,
"NAN: In pauseState - ignore Follow-up message from another subscriber or without ssi");
return;
}
os_get_reltime(&srv->last_followup);
if (srv->type == NAN_DE_PUBLISH && !ssi)
nan_de_pause_state(srv, peer_addr, instance_id);
if (de->cb.receive)
de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len,
peer_addr);
}
static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
unsigned int freq, const u8 *buf, size_t len,
const u8 *sda, size_t sda_len)
{
const u8 *service_id;
u8 instance_id, req_instance_id, ctrl;
u16 sdea_control = 0;
unsigned int i;
enum nan_service_control_type type = 0;
enum nan_service_protocol_type srv_proto_type = 0;
const u8 *ssi = NULL;
size_t ssi_len = 0;
bool first = true;
const u8 *end;
const u8 *matching_filter = NULL;
size_t matching_filter_len = 0;
if (sda_len < NAN_SERVICE_ID_LEN + 1 + 1 + 1)
return;
end = sda + sda_len;
service_id = sda;
sda += NAN_SERVICE_ID_LEN;
instance_id = *sda++;
req_instance_id = *sda++;
ctrl = *sda;
type = ctrl & NAN_SRV_CTRL_TYPE_MASK;
wpa_printf(MSG_DEBUG,
"NAN: SDA - Service ID %02x%02x%02x%02x%02x%02x Instance ID %u Requestor Instance ID %u Service Control 0x%x (Service Control Type %u)",
MAC2STR(service_id), instance_id, req_instance_id,
ctrl, type);
if (type != NAN_SRV_CTRL_PUBLISH &&
type != NAN_SRV_CTRL_SUBSCRIBE &&
type != NAN_SRV_CTRL_FOLLOW_UP) {
wpa_printf(MSG_DEBUG,
"NAN: Discard SDF with unknown Service Control Type %u",
type);
return;
}
if (ctrl & NAN_SRV_CTRL_BINDING_BITMAP) {
if (end - sda < 2)
return;
sda += 2;
}
if (ctrl & NAN_SRV_CTRL_MATCHING_FILTER) {
u8 flen;
if (end - sda < 1)
return;
flen = *sda++;
if (end - sda < flen)
return;
matching_filter = sda;
matching_filter_len = flen;
sda += flen;
}
if (ctrl & NAN_SRV_CTRL_RESP_FILTER) {
u8 flen;
if (end - sda < 1)
return;
flen = *sda++;
if (end - sda < flen)
return;
sda += flen;
}
if (ctrl & NAN_SRV_CTRL_SRV_INFO) {
u8 flen;
if (end - sda < 1)
return;
flen = *sda++;
if (end - sda < flen)
return;
if (flen >= 4 && WPA_GET_BE24(sda) == OUI_WFA) {
srv_proto_type = sda[3];
ssi = sda + 4;
ssi_len = flen - 4;
wpa_printf(MSG_DEBUG, "NAN: Service Protocol Type %d",
srv_proto_type);
wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len);
}
sda += flen;
}
for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
struct nan_de_service *srv = de->service[i];
if (!srv)
continue;
if (os_memcmp(srv->service_id, service_id,
NAN_SERVICE_ID_LEN) != 0)
continue;
if (type == NAN_SRV_CTRL_PUBLISH) {
if (srv->type == NAN_DE_PUBLISH)
continue;
if (req_instance_id && srv->id != req_instance_id)
continue;
}
if (type == NAN_SRV_CTRL_SUBSCRIBE &&
srv->type == NAN_DE_SUBSCRIBE)
continue;
wpa_printf(MSG_DEBUG, "NAN: Received SDF matches service ID %u",
i + 1);
if (first) {
first = false;
nan_de_get_sdea(buf, len, instance_id, &sdea_control,
&srv_proto_type, &ssi, &ssi_len);
if (ssi) {
wpa_printf(MSG_DEBUG,
"NAN: Service Protocol Type %d",
srv_proto_type);
wpa_hexdump(MSG_MSGDUMP, "NAN: ssi",
ssi, ssi_len);
}
}
switch (type) {
case NAN_SRV_CTRL_PUBLISH:
nan_de_rx_publish(de, srv, peer_addr, instance_id,
req_instance_id,
sdea_control, srv_proto_type,
ssi, ssi_len);
break;
case NAN_SRV_CTRL_SUBSCRIBE:
nan_de_rx_subscribe(de, srv, peer_addr, instance_id,
matching_filter,
matching_filter_len,
srv_proto_type,
ssi, ssi_len);
break;
case NAN_SRV_CTRL_FOLLOW_UP:
nan_de_rx_follow_up(de, srv, peer_addr, instance_id,
ssi, ssi_len);
break;
}
}
}
void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
const u8 *buf, size_t len)
{
const u8 *sda;
u16 sda_len;
unsigned int skip;
if (!de->num_service)
return;
wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR " freq=%u len=%zu",
MAC2STR(peer_addr), freq, len);
wpa_hexdump(MSG_MSGDUMP, "NAN: SDF payload", buf, len);
for (skip = 0; ; skip++) {
sda = nan_de_get_attr(buf, len, NAN_ATTR_SDA, skip);
if (!sda)
break;
sda++;
sda_len = WPA_GET_LE16(sda);
sda += 2;
nan_de_rx_sda(de, peer_addr, freq, buf, len, sda, sda_len);
}
}
static int nan_de_get_handle(struct nan_de *de)
{
int i = de->next_handle;
if (de->num_service >= NAN_DE_MAX_SERVICE)
goto fail;
do {
if (!de->service[i]) {
de->next_handle = (i + 1) % NAN_DE_MAX_SERVICE;
return i + 1;
}
i = (i + 1) % NAN_DE_MAX_SERVICE;
} while (i != de->next_handle);
fail:
wpa_printf(MSG_DEBUG, "NAN: No more room for a new service");
return -1;
}
static int nan_de_derive_service_id(struct nan_de_service *srv)
{
u8 hash[SHA256_MAC_LEN];
char *name, *pos;
int ret;
const u8 *addr[1];
size_t len[1];
name = os_strdup(srv->service_name);
if (!name)
return -1;
pos = name;
while (*pos) {
*pos = tolower(*pos);
pos++;
}
addr[0] = (u8 *) name;
len[0] = os_strlen(name);
ret = sha256_vector(1, addr, len, hash);
os_free(name);
if (ret == 0)
os_memcpy(srv->service_id, hash, NAN_SERVICE_ID_LEN);
return ret;
}
int nan_de_publish(struct nan_de *de, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi, const struct wpabuf *elems,
struct nan_publish_params *params)
{
int publish_id;
struct nan_de_service *srv;
if (!service_name) {
wpa_printf(MSG_DEBUG, "NAN: Publish() - no service_name");
return -1;
}
if (!params->unsolicited && !params->solicited) {
wpa_printf(MSG_INFO,
"NAN: Publish() - both unsolicited and solicited disabled is invalid");
return -1;
}
publish_id = nan_de_get_handle(de);
if (publish_id < 1)
return -1;
srv = os_zalloc(sizeof(*srv));
if (!srv)
return -1;
srv->type = NAN_DE_PUBLISH;
srv->freq = srv->default_freq = params->freq;
srv->service_name = os_strdup(service_name);
if (!srv->service_name)
goto fail;
if (nan_de_derive_service_id(srv) < 0)
goto fail;
os_memcpy(&srv->publish, params, sizeof(*params));
if (params->freq_list) {
size_t len;
len = (int_array_len(params->freq_list) + 1) * sizeof(int);
srv->freq_list = os_memdup(params->freq_list, len);
if (!srv->freq_list)
goto fail;
}
srv->publish.freq_list = NULL;
srv->srv_proto_type = srv_proto_type;
if (ssi) {
srv->ssi = wpabuf_dup(ssi);
if (!srv->ssi)
goto fail;
}
if (elems) {
srv->elems = wpabuf_dup(elems);
if (!srv->elems)
goto fail;
}
/* Prepare for single and multi-channel states; starting with
* single channel */
srv->first_multi_chan = true;
nan_de_start_new_publish_state(srv, true);
wpa_printf(MSG_DEBUG, "NAN: Assigned new publish handle %d for %s",
publish_id, service_name);
srv->id = publish_id;
nan_de_add_srv(de, srv);
nan_de_run_timer(de);
return publish_id;
fail:
nan_de_service_free(srv);
return -1;
}
void nan_de_cancel_publish(struct nan_de *de, int publish_id)
{
struct nan_de_service *srv;
wpa_printf(MSG_DEBUG, "NAN: CancelPublish(publish_id=%d)", publish_id);
if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE)
return;
srv = de->service[publish_id - 1];
if (!srv || srv->type != NAN_DE_PUBLISH)
return;
nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST);
}
int nan_de_update_publish(struct nan_de *de, int publish_id,
const struct wpabuf *ssi)
{
struct nan_de_service *srv;
wpa_printf(MSG_DEBUG, "NAN: UpdatePublish(publish_id=%d)", publish_id);
if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE)
return -1;
srv = de->service[publish_id - 1];
if (!srv || srv->type != NAN_DE_PUBLISH)
return -1;
wpabuf_free(srv->ssi);
srv->ssi = NULL;
if (!ssi)
return 0;
srv->ssi = wpabuf_dup(ssi);
if (!srv->ssi)
return -1;
return 0;
}
int nan_de_subscribe(struct nan_de *de, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi, const struct wpabuf *elems,
struct nan_subscribe_params *params)
{
int subscribe_id;
struct nan_de_service *srv;
if (!service_name) {
wpa_printf(MSG_DEBUG, "NAN: Subscribe() - no service_name");
return -1;
}
subscribe_id = nan_de_get_handle(de);
if (subscribe_id < 1)
return -1;
srv = os_zalloc(sizeof(*srv));
if (!srv)
return -1;
srv->type = NAN_DE_SUBSCRIBE;
srv->freq = params->freq;
srv->service_name = os_strdup(service_name);
if (!srv->service_name)
goto fail;
if (nan_de_derive_service_id(srv) < 0)
goto fail;
os_memcpy(&srv->subscribe, params, sizeof(*params));
srv->srv_proto_type = srv_proto_type;
if (ssi) {
srv->ssi = wpabuf_dup(ssi);
if (!srv->ssi)
goto fail;
}
if (elems) {
srv->elems = wpabuf_dup(elems);
if (!srv->elems)
goto fail;
}
wpa_printf(MSG_DEBUG, "NAN: Assigned new subscribe handle %d for %s",
subscribe_id, service_name);
srv->id = subscribe_id;
nan_de_add_srv(de, srv);
nan_de_run_timer(de);
return subscribe_id;
fail:
nan_de_service_free(srv);
return -1;
}
void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id)
{
struct nan_de_service *srv;
if (subscribe_id < 1 || subscribe_id > NAN_DE_MAX_SERVICE)
return;
srv = de->service[subscribe_id - 1];
if (!srv || srv->type != NAN_DE_SUBSCRIBE)
return;
nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST);
}
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)
{
struct nan_de_service *srv;
if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
return -1;
srv = de->service[handle - 1];
if (!srv)
return -1;
nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
peer_addr, req_instance_id, ssi);
os_get_reltime(&srv->last_followup);
return 0;
}