NAN: USD in hostapd

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>
This commit is contained in:
Jouni Malinen 2024-02-15 17:41:05 +02:00 committed by Jouni Malinen
parent e3f9ab3c3a
commit 3a5d1a7e6d
13 changed files with 703 additions and 0 deletions

View file

@ -42,6 +42,7 @@
#include "dpp_hostapd.h"
#include "fils_hlp.h"
#include "neighbor_db.h"
#include "nan_usd_ap.h"
#ifdef CONFIG_FILS
@ -1622,6 +1623,23 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
return;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
if (mgmt->u.action.category == WLAN_ACTION_PUBLIC && plen >= 5 &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
OUI_WFA &&
mgmt->u.action.u.vs_public_action.variable[0] == NAN_OUI_TYPE) {
const u8 *pos, *end;
pos = mgmt->u.action.u.vs_public_action.variable;
end = drv_mgmt->frame + drv_mgmt->frame_len;
pos++;
hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq,
pos, end - pos);
return;
}
#endif /* CONFIG_NAN_USD */
}
#endif /* NEED_AP_MLME */

View file

@ -35,6 +35,7 @@
#include "wpa_auth.h"
#include "wps_hostapd.h"
#include "dpp_hostapd.h"
#include "nan_usd_ap.h"
#include "gas_query_ap.h"
#include "hw_features.h"
#include "wpa_auth_glue.h"
@ -528,6 +529,9 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
gas_query_ap_deinit(hapd->gas);
hapd->gas = NULL;
#endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
hostapd_nan_usd_deinit(hapd);
#endif /* CONFIG_NAN_USD */
authsrv_deinit(hapd);
@ -1516,6 +1520,11 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
return -1;
#endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
if (hostapd_nan_usd_init(hapd) < 0)
return -1;
#endif /* CONFIG_NAN_USD */
if (authsrv_init(hapd) < 0)
return -1;

View file

@ -475,6 +475,10 @@ struct hostapd_data {
u8 eht_mld_link_removal_count;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_NAN_USD
struct nan_de *nan_de;
#endif /* CONFIG_NAN_USD */
};

View file

@ -56,6 +56,7 @@
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
#include "comeback_token.h"
#include "nan_usd_ap.h"
#include "pasn/pasn_common.h"
@ -6034,6 +6035,25 @@ static int handle_action(struct hostapd_data *hapd,
return 1;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
if (mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
len >= IEEE80211_HDRLEN + 5 &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
OUI_WFA &&
mgmt->u.action.u.vs_public_action.variable[0] ==
NAN_OUI_TYPE) {
const u8 *pos, *end;
pos = mgmt->u.action.u.vs_public_action.variable;
end = ((const u8 *) mgmt) + len;
pos++;
hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq,
pos, end - pos);
return 1;
}
#endif /* CONFIG_NAN_USD */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len, freq);
@ -6141,6 +6161,10 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
int ret = 0;
unsigned int freq;
int ssi_signal = fi ? fi->ssi_signal : 0;
#ifdef CONFIG_NAN_USD
static const u8 nan_network_id[ETH_ALEN] =
{ 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
#endif /* CONFIG_NAN_USD */
if (len < 24)
return 0;
@ -6207,6 +6231,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
!(hapd->conf->mld_ap &&
ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_NAN_USD
!ether_addr_equal(mgmt->da, nan_network_id) &&
#endif /* CONFIG_NAN_USD */
!ether_addr_equal(mgmt->da, hapd->own_addr)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,

267
src/ap/nan_usd_ap.c Normal file
View file

@ -0,0 +1,267 @@
/*
* 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);
}

46
src/ap/nan_usd_ap.h Normal file
View file

@ -0,0 +1,46 @@
/*
* 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.
*/
#ifndef NAN_USD_AP_H
#define NAN_USD_AP_H
struct nan_subscribe_params;
struct nan_publish_params;
enum nan_service_protocol_type;
int hostapd_nan_usd_init(struct hostapd_data *hapd);
void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
unsigned int freq, const u8 *buf, size_t len);
void hostapd_nan_usd_flush(struct hostapd_data *hapd);
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);
void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id);
int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
const struct wpabuf *ssi);
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);
void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
int 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);
void hostapd_nan_usd_remain_on_channel_cb(struct hostapd_data *hapd,
unsigned int freq,
unsigned int duration);
void hostapd_nan_usd_cancel_remain_on_channel_cb(struct hostapd_data *hapd,
unsigned int freq);
void hostapd_nan_usd_tx_wait_expire(struct hostapd_data *hapd);
#endif /* NAN_USD_AP_H */