3a5d1a7e6d
Add hostapd support for interacting with the NAN discovery engine to allow single-channel (i.e., the AP's operating channel) USD as Publisher or Subscriber. Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
267 lines
6.4 KiB
C
267 lines
6.4 KiB
C
/*
|
|
* NAN unsynchronized service discovery (USD)
|
|
* 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 "common/wpa_ctrl.h"
|
|
#include "common/nan_de.h"
|
|
#include "hostapd.h"
|
|
#include "ap_drv_ops.h"
|
|
#include "nan_usd_ap.h"
|
|
|
|
|
|
static int hostapd_nan_de_tx(void *ctx, unsigned int freq,
|
|
unsigned int wait_time,
|
|
const u8 *dst, const u8 *src, const u8 *bssid,
|
|
const struct wpabuf *buf)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
|
|
wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
|
|
" A3=" MACSTR " len=%zu",
|
|
MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
|
|
wpabuf_len(buf));
|
|
|
|
/* TODO: Force use of OFDM */
|
|
return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
|
|
wpabuf_head(buf), wpabuf_len(buf));
|
|
}
|
|
|
|
|
|
static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
|
|
unsigned int duration)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
|
|
enum nan_service_protocol_type srv_proto_type,
|
|
const u8 *ssi, size_t ssi_len,
|
|
int peer_publish_id, const u8 *peer_addr,
|
|
bool fsd, bool fsd_gas)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
char *ssi_hex;
|
|
|
|
ssi_hex = os_zalloc(2 * ssi_len + 1);
|
|
if (!ssi_hex)
|
|
return;
|
|
if (ssi)
|
|
wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
|
|
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
|
|
"subscribe_id=%d publish_id=%d address=" MACSTR
|
|
" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
|
|
subscribe_id, peer_publish_id, MAC2STR(peer_addr),
|
|
fsd, fsd_gas, srv_proto_type, ssi_hex);
|
|
os_free(ssi_hex);
|
|
}
|
|
|
|
|
|
static void
|
|
hostapd_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr,
|
|
int peer_subscribe_id,
|
|
enum nan_service_protocol_type srv_proto_type,
|
|
const u8 *ssi, size_t ssi_len)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
char *ssi_hex;
|
|
|
|
ssi_hex = os_zalloc(2 * ssi_len + 1);
|
|
if (!ssi_hex)
|
|
return;
|
|
if (ssi)
|
|
wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
|
|
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_REPLIED
|
|
"publish_id=%d address=" MACSTR
|
|
" subscribe_id=%d srv_proto_type=%u ssi=%s",
|
|
publish_id, MAC2STR(peer_addr), peer_subscribe_id,
|
|
srv_proto_type, ssi_hex);
|
|
os_free(ssi_hex);
|
|
}
|
|
|
|
|
|
static const char * nan_reason_txt(enum nan_de_reason reason)
|
|
{
|
|
switch (reason) {
|
|
case NAN_DE_REASON_TIMEOUT:
|
|
return "timeout";
|
|
case NAN_DE_REASON_USER_REQUEST:
|
|
return "user-request";
|
|
case NAN_DE_REASON_FAILURE:
|
|
return "failure";
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
|
|
static void hostapd_nan_de_publish_terminated(void *ctx, int publish_id,
|
|
enum nan_de_reason reason)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
|
|
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_PUBLISH_TERMINATED
|
|
"publish_id=%d reason=%s",
|
|
publish_id, nan_reason_txt(reason));
|
|
}
|
|
|
|
|
|
static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
|
|
enum nan_de_reason reason)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
|
|
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
|
|
"subscribe_id=%d reason=%s",
|
|
subscribe_id, nan_reason_txt(reason));
|
|
}
|
|
|
|
|
|
static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id,
|
|
const u8 *ssi, size_t ssi_len,
|
|
const u8 *peer_addr)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
char *ssi_hex;
|
|
|
|
ssi_hex = os_zalloc(2 * ssi_len + 1);
|
|
if (!ssi_hex)
|
|
return;
|
|
if (ssi)
|
|
wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
|
|
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_RECEIVE
|
|
"id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
|
|
id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
|
|
os_free(ssi_hex);
|
|
}
|
|
|
|
|
|
int hostapd_nan_usd_init(struct hostapd_data *hapd)
|
|
{
|
|
struct nan_callbacks cb;
|
|
|
|
os_memset(&cb, 0, sizeof(cb));
|
|
cb.ctx = hapd;
|
|
cb.tx = hostapd_nan_de_tx;
|
|
cb.listen = hostapd_nan_de_listen;
|
|
cb.discovery_result = hostapd_nan_de_discovery_result;
|
|
cb.replied = hostapd_nan_de_replied;
|
|
cb.publish_terminated = hostapd_nan_de_publish_terminated;
|
|
cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
|
|
cb.receive = hostapd_nan_de_receive;
|
|
|
|
hapd->nan_de = nan_de_init(hapd->own_addr, true, &cb);
|
|
if (!hapd->nan_de)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
|
|
{
|
|
nan_de_deinit(hapd->nan_de);
|
|
hapd->nan_de = NULL;
|
|
}
|
|
|
|
|
|
void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
|
|
unsigned int freq, const u8 *buf, size_t len)
|
|
{
|
|
if (!hapd->nan_de)
|
|
return;
|
|
nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
|
|
}
|
|
|
|
|
|
void hostapd_nan_usd_flush(struct hostapd_data *hapd)
|
|
{
|
|
if (!hapd->nan_de)
|
|
return;
|
|
nan_de_flush(hapd->nan_de);
|
|
}
|
|
|
|
|
|
int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
|
|
enum nan_service_protocol_type srv_proto_type,
|
|
const struct wpabuf *ssi,
|
|
struct nan_publish_params *params)
|
|
{
|
|
int publish_id;
|
|
struct wpabuf *elems = NULL;
|
|
|
|
if (!hapd->nan_de)
|
|
return -1;
|
|
|
|
publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
|
|
ssi, elems, params);
|
|
wpabuf_free(elems);
|
|
return publish_id;
|
|
}
|
|
|
|
|
|
void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id)
|
|
{
|
|
if (!hapd->nan_de)
|
|
return;
|
|
nan_de_cancel_publish(hapd->nan_de, publish_id);
|
|
}
|
|
|
|
|
|
int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
|
|
const struct wpabuf *ssi)
|
|
{
|
|
int ret;
|
|
|
|
if (!hapd->nan_de)
|
|
return -1;
|
|
ret = nan_de_update_publish(hapd->nan_de, publish_id, ssi);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
|
|
const char *service_name,
|
|
enum nan_service_protocol_type srv_proto_type,
|
|
const struct wpabuf *ssi,
|
|
struct nan_subscribe_params *params)
|
|
{
|
|
int subscribe_id;
|
|
struct wpabuf *elems = NULL;
|
|
|
|
if (!hapd->nan_de)
|
|
return -1;
|
|
|
|
subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
|
|
srv_proto_type, ssi, elems, params);
|
|
wpabuf_free(elems);
|
|
return subscribe_id;
|
|
}
|
|
|
|
|
|
void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
|
|
int subscribe_id)
|
|
{
|
|
if (!hapd->nan_de)
|
|
return;
|
|
nan_de_cancel_subscribe(hapd->nan_de, subscribe_id);
|
|
}
|
|
|
|
|
|
int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
|
|
const struct wpabuf *ssi,
|
|
const struct wpabuf *elems,
|
|
const u8 *peer_addr, u8 req_instance_id)
|
|
{
|
|
if (!hapd->nan_de)
|
|
return -1;
|
|
return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
|
|
req_instance_id);
|
|
}
|