NAN: USD in wpa_supplicant

Add wpa_supplicant support for interacting with the NAN discovery engine
to allow USD as Publisher or Subscriber.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
Jouni Malinen 2024-02-10 11:57:23 +02:00 committed by Jouni Malinen
parent 9eb0bc1f0a
commit e3f9ab3c3a
13 changed files with 1134 additions and 0 deletions

View file

@ -232,6 +232,13 @@ extern "C" {
#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT " #define DPP_EVENT_PB_RESULT "DPP-PB-RESULT "
#define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER " #define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER "
/* Wi-Fi Aware (NAN USD) events */
#define NAN_DISCOVERY_RESULT "NAN-DISCOVERY-RESULT "
#define NAN_REPLIED "NAN-REPLIED "
#define NAN_PUBLISH_TERMINATED "NAN-PUBLISH-TERMINATED "
#define NAN_SUBSCRIBE_TERMINATED "NAN-SUBSCRIBE-TERMINATED "
#define NAN_RECEIVE "NAN-RECEIVE "
/* MESH events */ /* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED " #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "

View file

@ -2564,6 +2564,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
5) < 0) 5) < 0)
ret = -1; ret = -1;
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
#ifdef CONFIG_NAN_USD
/* NAN SDF Public Action */
if (nl80211_register_action_frame(bss,
(u8 *) "\x04\x09\x50\x6f\x9a\x13",
6) < 0)
ret = -1;
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_DPP #ifdef CONFIG_DPP
/* DPP Public Action */ /* DPP Public Action */
if (nl80211_register_action_frame(bss, if (nl80211_register_action_frame(bss,

View file

@ -281,6 +281,12 @@ L_CFLAGS += -DCONFIG_DPP3
endif endif
endif endif
ifdef CONFIG_NAN_USD
OBJS += src/common/nan_de.c
OBJS += nan_usd.c
L_CFLAGS += -DCONFIG_NAN_USD
endif
ifdef CONFIG_OWE ifdef CONFIG_OWE
L_CFLAGS += -DCONFIG_OWE L_CFLAGS += -DCONFIG_OWE
NEED_ECC=y NEED_ECC=y

View file

@ -315,6 +315,12 @@ CFLAGS += -DCONFIG_DPP3
endif endif
endif endif
ifdef CONFIG_NAN_USD
OBJS += ../src/common/nan_de.o
OBJS += nan_usd.o
CFLAGS += -DCONFIG_NAN_USD
endif
ifdef CONFIG_OWE ifdef CONFIG_OWE
CFLAGS += -DCONFIG_OWE CFLAGS += -DCONFIG_OWE
NEED_ECC=y NEED_ECC=y

View file

@ -0,0 +1,147 @@
Wi-Fi Aware unsynchronized service discovery (NAN USD)
======================================================
This document descibes how the unsynchronized service discovery defined
in the Wi-Fi Aware specification v4.0 can be used with wpa_spplicant.
More information about Wi-Fi Aware is available from this Wi-Fi
Alliance web page:
https://www.wi-fi.org/discover-wi-fi/wi-fi-aware
Build config setup
------------------
The following parameters must be included in the config file used to
compile hostapd and wpa_supplicant.
wpa_supplicant build config
---------------------------
Enable NAN USD in wpa_supplicant build config file
CONFIG_NAN_USD=y
Control interface commands and events
-------------------------------------
Following control interface commands can be used:
NAN_PUBLISH service_name=<name> [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [freq_list=<comma separate list of MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>] [solicited=0] [unsolicited=0] [fsd=0]
If ttl=0 or the parameter is not included, only one Publish message is
transmitted.
If freq is not included, the default frequency 2437 MHz (channel 6 on
the 2.4 GHz band) is used.
If freq_list is included, publisher iterates over all the listed
channels. A special freq_list=all value can be used to generate the
channel list automatically based on the list of allowed 2.4 and 5 GHz
channels.
srv_proto_type values are defined in the Service Protocol Types table in
the Wi-Fi Aware specification.
This command returns the assigned publish_id value or FAIL on failure.
This command maps to the Publish() method in the NAN Discovery Engine.
NAN_CANCEL_PUBLISH publish_id=<id from NAN_PUBLISH>
This command maps to the CancelPublish() method in the NAN Discovery
Engine.
NAN_UPDATE_PUBLISH publish_id=<id from NAN_PUBLISH> [ssi=<service specific information (hexdump)>]
This command maps to the UpdatePublish() method in the NAN Discovery
Engine.
NAN_SUBSCRIBE service_name=<name> [active=1] [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>]
If ttl=0 or the parameter is not included, operation is terminated once
the first matching publisher is found.
If freq is not included, the default frequency 2437 MHz (channel 6 on
the 2.4 GHz band) is used.
srv_proto_type values are defined in the Service Protocol Types table in
the Wi-Fi Aware specification.
This command returns the assigned subscribe_id value or FAIL on failure.
This command maps to the Subscribe() method in the NAN Discovery Engine.
NAN_CANCEL_SUBSCRIBE subscribe_id=<id from NAN_SUBSCRIBE>
This command maps to the CancelSubscribe() method in the NAN Discovery Engine.
NAN_TRANSMIT handle=<id from NAN_PUBLISH or NAN_SUBSCRIBE> req_instance=<peer's id> address=<peer's MAC address> [ssi=<service specific information (hexdump)>]
This command maps to the Transmit() method in the NAN Discovery Engine.
Following control interface events are used:
NAN-DISCOVERY-RESULT subscribe_id=<own id> publish_id=<peer's id> address=<peer MAC address> fsd=<0/1> fsd_gas=<0/1> srv_proto_type=<type> ssi=<service specific information (hexdump)>
This event maps to the DiscoveryResult() event in the NAN Discovery
Engine.
NAN-REPLIED publish_id=<own id> address=<peer MAC address> subscribe_id=<peer id> srv_proto_type=<ype> ssi=<service specific information (hexdump)>
This event maps to the Replied() event in the NAN Discovery Engine.
NAN-PUBLISH-TERMINATED publish_id=<own id> reason=<timeout/user-request/failure>
This event maps to the PublishTerminated() event in the NAN Discovery
Engine.
NAN-SUBSCRIBE-TERMINATED subscribe_id=<own id> reason=<timeout/user-request/failure>
This event maps to the SubscribeTerminate() event in the NAN Discovery
Engine.
NAN-RECEIVE id=<own id> peer_instance_id=<peer id> address=<peer MAC adress> ssi=<service specific information (hexdump)>
This event maps to the Receive() event in the NAN Discovery Engine.
Example operation
-----------------
Start Subscribe and Publish functions:
dev0: NAN_SUBSCRIBE service_name=_test srv_proto_type=3 ssi=1122334455
--> returns 7
dev1: NAN_PUBLISH service_name=_test srv_proto_type=3 ssi=6677
--> returns 5
Subscriber notification of a discovery:
event on dev0: <3>NAN-DISCOVERY-RESULT subscribe_id=7 publish_id=5 address=02:00:00:00:01:00 fsd=1 fsd_gas=0 srv_proto_type=3 ssi=6677
Publisher notification of a Follow-up message with no ssi (to enter
paused state to continue exchange with the subscriber):
event on dev1: <3>NAN-RECEIVE id=5 peer_instance_id=7 address=02:00:00:00:00:00 ssi=
Subscriber sending a Follow-up message:
dev0: NAN_TRANSMIT handle=7 req_instance_id=5 address=02:00:00:00:01:00 ssi=8899
Publisher receiving the Follow-up message:
event on dev1: <3>NAN-RECEIVE id=5 peer_instance_id=7 address=02:00:00:00:00:00 ssi=8899
Publisher sending a Follow-up message:
dev1: NAN_TRANSMIT handle=5 req_instance_id=7 address=02:00:00:00:00:00 ssi=aabbccdd
Subscriber receiving the Follow-up message:
event on dev0: <3>NAN-RECEIVE id=7 peer_instance_id=5 address=02:00:00:00:01:00 ssi=aabbccdd
Stop Subscribe and Publish functions:
dev0: NAN_CANCEL_SUBSCRIBE subscribe_id=7
dev1: NAN_CANCEL_PUBLIST publish_id=5

View file

@ -554,4 +554,7 @@ CONFIG_WEP=y
# Disable support for WMM admission control # Disable support for WMM admission control
#CONFIG_NO_WMM_AC=y #CONFIG_NO_WMM_AC=y
# Wi-Fi Aware unsynchronized service discovery (NAN USD)
#CONFIG_NAN_USD=y
include $(wildcard $(LOCAL_PATH)/android_config_*.inc) include $(wildcard $(LOCAL_PATH)/android_config_*.inc)

View file

@ -22,6 +22,7 @@
#ifdef CONFIG_DPP #ifdef CONFIG_DPP
#include "common/dpp.h" #include "common/dpp.h"
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#include "common/nan_de.h"
#include "common/ptksa_cache.h" #include "common/ptksa_cache.h"
#include "crypto/tls.h" #include "crypto/tls.h"
#include "ap/hostapd.h" #include "ap/hostapd.h"
@ -58,6 +59,7 @@
#include "mesh.h" #include "mesh.h"
#include "dpp_supplicant.h" #include "dpp_supplicant.h"
#include "sme.h" #include "sme.h"
#include "nan_usd.h"
#ifdef __NetBSD__ #ifdef __NetBSD__
#include <net/if_ether.h> #include <net/if_ether.h>
@ -4875,6 +4877,15 @@ static int wpa_supplicant_ctrl_iface_get_capability(
} }
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
if (os_strcmp(field, "nan") == 0) {
res = os_snprintf(buf, buflen, "USD");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_SAE #ifdef CONFIG_SAE
if (os_strcmp(field, "sae") == 0 && if (os_strcmp(field, "sae") == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
@ -8952,6 +8963,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
wpas_restore_permanent_mac_addr(wpa_s); wpas_restore_permanent_mac_addr(wpa_s);
wpa_s->conf->ignore_old_scan_res = 0; wpa_s->conf->ignore_old_scan_res = 0;
#ifdef CONFIG_NAN_USD
wpas_nan_usd_flush(wpa_s);
#endif /* CONFIG_NAN_USD */
} }
@ -12148,6 +12163,327 @@ static int wpas_ctrl_ml_probe(struct wpa_supplicant *wpa_s, char *cmd)
#endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_NAN_USD
static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
char *token, *context = NULL;
int publish_id;
struct nan_publish_params params;
const char *service_name = NULL;
struct wpabuf *ssi = NULL;
int ret = -1;
enum nan_service_protocol_type srv_proto_type = 0;
int *freq_list = NULL;
os_memset(&params, 0, sizeof(params));
/* USD shall use both solicited and unsolicited transmissions */
params.unsolicited = true;
params.solicited = true;
/* USD shall require FSD without GAS */
params.fsd = true;
params.freq = NAN_USD_DEFAULT_FREQ;
while ((token = str_token(cmd, " ", &context))) {
if (os_strncmp(token, "service_name=", 13) == 0) {
service_name = token + 13;
continue;
}
if (os_strncmp(token, "ttl=", 4) == 0) {
params.ttl = atoi(token + 4);
continue;
}
if (os_strncmp(token, "freq=", 5) == 0) {
params.freq = atoi(token + 5);
continue;
}
if (os_strncmp(token, "freq_list=", 10) == 0) {
char *pos = token + 10;
if (os_strcmp(pos, "all") == 0) {
os_free(freq_list);
freq_list = wpas_nan_usd_all_freqs(wpa_s);
params.freq_list = freq_list;
continue;
}
while (pos && pos[0]) {
int_array_add_unique(&freq_list, atoi(pos));
pos = os_strchr(pos, ',');
if (pos)
pos++;
}
params.freq_list = freq_list;
continue;
}
if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
srv_proto_type = atoi(token + 15);
continue;
}
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
if (os_strcmp(token, "solicited=0") == 0) {
params.solicited = false;
continue;
}
if (os_strcmp(token, "unsolicited=0") == 0) {
params.unsolicited = false;
continue;
}
if (os_strcmp(token, "fsd=0") == 0) {
params.fsd = false;
continue;
}
wpa_printf(MSG_INFO, "CTRL: Invalid NAN_PUBLISH parameter: %s",
token);
goto fail;
}
publish_id = wpas_nan_usd_publish(wpa_s, service_name, srv_proto_type,
ssi, &params);
if (publish_id > 0)
ret = os_snprintf(buf, buflen, "%d", publish_id);
fail:
wpabuf_free(ssi);
os_free(freq_list);
return ret;
}
static int wpas_ctrl_nan_cancel_publish(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
int publish_id = 0;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "publish_id=%i", &publish_id) == 1)
continue;
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_CANCEL_PUBLISH parameter: %s",
token);
return -1;
}
if (publish_id <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_CANCEL_PUBLISH publish_id");
return -1;
}
wpas_nan_usd_cancel_publish(wpa_s, publish_id);
return 0;
}
static int wpas_ctrl_nan_update_publish(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
int publish_id = 0;
struct wpabuf *ssi = NULL;
int ret = -1;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "publish_id=%i", &publish_id) == 1)
continue;
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_UPDATE_PUBLISH parameter: %s",
token);
goto fail;
}
if (publish_id <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_UPDATE_PUBLISH publish_id");
goto fail;
}
ret = wpas_nan_usd_update_publish(wpa_s, publish_id, ssi);
fail:
wpabuf_free(ssi);
return ret;
}
static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
char *token, *context = NULL;
int subscribe_id;
struct nan_subscribe_params params;
const char *service_name = NULL;
struct wpabuf *ssi = NULL;
int ret = -1;
enum nan_service_protocol_type srv_proto_type = 0;
os_memset(&params, 0, sizeof(params));
params.freq = NAN_USD_DEFAULT_FREQ;
while ((token = str_token(cmd, " ", &context))) {
if (os_strncmp(token, "service_name=", 13) == 0) {
service_name = token + 13;
continue;
}
if (os_strcmp(token, "active=1") == 0) {
params.active = true;
continue;
}
if (os_strncmp(token, "ttl=", 4) == 0) {
params.ttl = atoi(token + 4);
continue;
}
if (os_strncmp(token, "freq=", 5) == 0) {
params.freq = atoi(token + 5);
continue;
}
if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
srv_proto_type = atoi(token + 15);
continue;
}
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_SUBSCRIBE parameter: %s",
token);
goto fail;
}
subscribe_id = wpas_nan_usd_subscribe(wpa_s, service_name,
srv_proto_type, ssi,
&params);
if (subscribe_id > 0)
ret = os_snprintf(buf, buflen, "%d", subscribe_id);
fail:
wpabuf_free(ssi);
return ret;
}
static int wpas_ctrl_nan_cancel_subscribe(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
int subscribe_id = 0;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "subscribe_id=%i", &subscribe_id) == 1)
continue;
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_CANCEL_SUBSCRIBE parameter: %s",
token);
return -1;
}
if (subscribe_id <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_CANCEL_SUBSCRIBE subscribe_id");
return -1;
}
wpas_nan_usd_cancel_subscribe(wpa_s, subscribe_id);
return 0;
}
static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd)
{
char *token, *context = NULL;
int handle = 0;
int req_instance_id = 0;
struct wpabuf *ssi = NULL;
u8 peer_addr[ETH_ALEN];
int ret = -1;
os_memset(peer_addr, 0, ETH_ALEN);
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "handle=%i", &handle) == 1)
continue;
if (sscanf(token, "req_instance_id=%i", &req_instance_id) == 1)
continue;
if (os_strncmp(token, "address=", 8) == 0) {
if (hwaddr_aton(token + 8, peer_addr) < 0)
return -1;
continue;
}
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_TRANSMIT parameter: %s",
token);
goto fail;
}
if (handle <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_TRANSMIT handle");
goto fail;
}
if (is_zero_ether_addr(peer_addr)) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_TRANSMIT address");
goto fail;
}
ret = wpas_nan_usd_transmit(wpa_s, handle, ssi, NULL, peer_addr,
req_instance_id);
fail:
wpabuf_free(ssi);
return ret;
}
#endif /* CONFIG_NAN_USD */
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len) char *buf, size_t *resp_len)
{ {
@ -13151,6 +13487,26 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
reply_len = -1; reply_len = -1;
#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
} else if (os_strncmp(buf, "NAN_PUBLISH ", 12) == 0) {
reply_len = wpas_ctrl_nan_publish(wpa_s, buf + 12, reply,
reply_size);
} else if (os_strncmp(buf, "NAN_CANCEL_PUBLISH ", 19) == 0) {
if (wpas_ctrl_nan_cancel_publish(wpa_s, buf + 19) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NAN_UPDATE_PUBLISH ", 19) == 0) {
if (wpas_ctrl_nan_update_publish(wpa_s, buf + 19) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NAN_SUBSCRIBE ", 14) == 0) {
reply_len = wpas_ctrl_nan_subscribe(wpa_s, buf + 14, reply,
reply_size);
} else if (os_strncmp(buf, "NAN_CANCEL_SUBSCRIBE ", 21) == 0) {
if (wpas_ctrl_nan_cancel_subscribe(wpa_s, buf + 21) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) {
if (wpas_ctrl_nan_transmit(wpa_s, buf + 13) < 0)
reply_len = -1;
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_PASN #ifdef CONFIG_PASN
} else if (os_strncmp(buf, "PASN_START ", 11) == 0) { } else if (os_strncmp(buf, "PASN_START ", 11) == 0) {
if (wpas_ctrl_iface_pasn_start(wpa_s, buf + 11) < 0) if (wpas_ctrl_iface_pasn_start(wpa_s, buf + 11) < 0)

View file

@ -683,3 +683,6 @@ CONFIG_DPP2=y
# Disable support for WMM admission control # Disable support for WMM admission control
#CONFIG_NO_WMM_AC=y #CONFIG_NO_WMM_AC=y
# Wi-Fi Aware unsynchronized service discovery (NAN USD)
#CONFIG_NAN_USD=y

View file

@ -50,6 +50,7 @@
#include "mesh.h" #include "mesh.h"
#include "mesh_mpm.h" #include "mesh_mpm.h"
#include "wmm_ac.h" #include "wmm_ac.h"
#include "nan_usd.h"
#include "dpp_supplicant.h" #include "dpp_supplicant.h"
@ -5363,6 +5364,17 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
} }
#endif /* CONFIG_FST */ #endif /* CONFIG_FST */
#ifdef CONFIG_NAN_USD
if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE32(&payload[1]) == NAN_SDF_VENDOR_TYPE) {
payload += 5;
plen -= 5;
wpas_nan_usd_rx_sdf(wpa_s, mgmt->sa, freq, payload, plen);
return;
}
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_DPP #ifdef CONFIG_DPP
if (category == WLAN_ACTION_PUBLIC && plen >= 5 && if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
payload[0] == WLAN_PA_VENDOR_SPECIFIC && payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
@ -6495,6 +6507,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_s, data->remain_on_channel.freq, wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration); data->remain_on_channel.duration);
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
wpas_nan_usd_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
#endif /* CONFIG_NAN_USD */
break; break;
case EVENT_CANCEL_REMAIN_ON_CHANNEL: case EVENT_CANCEL_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL #ifdef CONFIG_OFFCHANNEL
@ -6507,6 +6524,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpas_dpp_cancel_remain_on_channel_cb( wpas_dpp_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq); wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
wpas_nan_usd_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_NAN_USD */
break; break;
case EVENT_EAPOL_RX: case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
@ -6842,6 +6863,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
#ifdef CONFIG_DPP #ifdef CONFIG_DPP
wpas_dpp_tx_wait_expire(wpa_s); wpas_dpp_tx_wait_expire(wpa_s);
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
wpas_nan_usd_tx_wait_expire(wpa_s);
#endif /* CONFIG_NAN_USD */
break; break;
case EVENT_TID_LINK_MAP: case EVENT_TID_LINK_MAP:
if (data) if (data)

513
wpa_supplicant/nan_usd.c Normal file
View file

@ -0,0 +1,513 @@
/*
* 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/nan_de.h"
#include "wpa_supplicant_i.h"
#include "offchannel.h"
#include "driver_i.h"
#include "nan_usd.h"
static const char *
tx_status_result_txt(enum offchannel_send_action_result result)
{
switch (result) {
case OFFCHANNEL_SEND_ACTION_SUCCESS:
return "success";
case OFFCHANNEL_SEND_ACTION_NO_ACK:
return "no-ack";
case OFFCHANNEL_SEND_ACTION_FAILED:
return "failed";
}
return "?";
}
static void wpas_nan_de_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result result)
{
if (!wpa_s->nan_de)
return;
wpa_printf(MSG_DEBUG, "NAN: TX status A1=" MACSTR " A2=" MACSTR
" A3=" MACSTR " freq=%d len=%zu result=%s",
MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), freq,
data_len, tx_status_result_txt(result));
nan_de_tx_status(wpa_s->nan_de, freq, dst);
}
struct wpas_nan_usd_tx_work {
unsigned int freq;
unsigned int wait_time;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
u8 bssid[ETH_ALEN];
struct wpabuf *buf;
};
static void wpas_nan_usd_tx_work_free(struct wpas_nan_usd_tx_work *twork)
{
if (!twork)
return;
wpabuf_free(twork->buf);
os_free(twork);
}
static void wpas_nan_usd_tx_work_done(struct wpa_supplicant *wpa_s)
{
struct wpas_nan_usd_tx_work *twork;
if (!wpa_s->nan_usd_tx_work)
return;
twork = wpa_s->nan_usd_tx_work->ctx;
wpas_nan_usd_tx_work_free(twork);
radio_work_done(wpa_s->nan_usd_tx_work);
wpa_s->nan_usd_tx_work = NULL;
}
static int wpas_nan_de_tx_send(struct wpa_supplicant *wpa_s, unsigned int freq,
unsigned int wait_time, const u8 *dst,
const u8 *src, const u8 *bssid,
const struct wpabuf *buf)
{
wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
" A3=" MACSTR " freq=%d len=%zu",
MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), freq,
wpabuf_len(buf));
return offchannel_send_action(wpa_s, freq, dst, src, bssid,
wpabuf_head(buf), wpabuf_len(buf),
wait_time, wpas_nan_de_tx_status, 1);
}
static void wpas_nan_usd_start_tx_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpas_nan_usd_tx_work *twork = work->ctx;
if (deinit) {
if (work->started) {
wpa_s->nan_usd_tx_work = NULL;
offchannel_send_action_done(wpa_s);
}
wpas_nan_usd_tx_work_free(twork);
return;
}
wpa_s->nan_usd_tx_work = work;
if (wpas_nan_de_tx_send(wpa_s, twork->freq, twork->wait_time,
twork->dst, twork->src, twork->bssid,
twork->buf) < 0)
wpas_nan_usd_tx_work_done(wpa_s);
}
static int wpas_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 wpa_supplicant *wpa_s = ctx;
struct wpas_nan_usd_tx_work *twork;
if (wpa_s->nan_usd_tx_work || wpa_s->nan_usd_listen_work) {
/* Reuse ongoing radio work */
return wpas_nan_de_tx_send(wpa_s, freq, wait_time, dst, src,
bssid, buf);
}
twork = os_zalloc(sizeof(*twork));
if (!twork)
return -1;
twork->freq = freq;
twork->wait_time = wait_time;
os_memcpy(twork->dst, dst, ETH_ALEN);
os_memcpy(twork->src, src, ETH_ALEN);
os_memcpy(twork->bssid, bssid, ETH_ALEN);
twork->buf = wpabuf_dup(buf);
if (!twork->buf) {
wpas_nan_usd_tx_work_free(twork);
return -1;
}
if (radio_add_work(wpa_s, freq, "nan-usd-tx", 0,
wpas_nan_usd_start_tx_cb, twork) < 0) {
wpas_nan_usd_tx_work_free(twork);
return -1;
}
return 0;
}
struct wpas_nan_usd_listen_work {
unsigned int freq;
unsigned int duration;
};
static void wpas_nan_usd_listen_work_done(struct wpa_supplicant *wpa_s)
{
struct wpas_nan_usd_listen_work *lwork;
if (!wpa_s->nan_usd_listen_work)
return;
lwork = wpa_s->nan_usd_listen_work->ctx;
os_free(lwork);
radio_work_done(wpa_s->nan_usd_listen_work);
wpa_s->nan_usd_listen_work = NULL;
}
static void wpas_nan_usd_start_listen_cb(struct wpa_radio_work *work,
int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpas_nan_usd_listen_work *lwork = work->ctx;
unsigned int duration;
if (deinit) {
if (work->started) {
wpa_s->nan_usd_listen_work = NULL;
wpa_drv_cancel_remain_on_channel(wpa_s);
}
os_free(lwork);
return;
}
wpa_s->nan_usd_listen_work = work;
duration = lwork->duration;
if (duration > wpa_s->max_remain_on_chan)
duration = wpa_s->max_remain_on_chan;
wpa_printf(MSG_DEBUG, "NAN: Start listen on %u MHz for %u ms",
lwork->freq, duration);
if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
wpa_printf(MSG_DEBUG,
"NAN: Failed to request the driver to remain on channel (%u MHz) for listen",
lwork->freq);
wpas_nan_usd_listen_work_done(wpa_s);
return;
}
}
static int wpas_nan_de_listen(void *ctx, unsigned int freq,
unsigned int duration)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpas_nan_usd_listen_work *lwork;
lwork = os_zalloc(sizeof(*lwork));
if (!lwork)
return -1;
lwork->freq = freq;
lwork->duration = duration;
if (radio_add_work(wpa_s, freq, "nan-usd-listen", 0,
wpas_nan_usd_start_listen_cb, lwork) < 0) {
os_free(lwork);
return -1;
}
return 0;
}
static void
wpas_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 wpa_supplicant *wpa_s = 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(wpa_s, 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 wpas_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 wpa_supplicant *wpa_s = 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(wpa_s, 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 wpas_nan_de_publish_terminated(void *ctx, int publish_id,
enum nan_de_reason reason)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED
"publish_id=%d reason=%s",
publish_id, nan_reason_txt(reason));
}
static void wpas_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
enum nan_de_reason reason)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
"subscribe_id=%d reason=%s",
subscribe_id, nan_reason_txt(reason));
}
static void wpas_nan_de_receive(void *ctx, int id, int peer_instance_id,
const u8 *ssi, size_t ssi_len,
const u8 *peer_addr)
{
struct wpa_supplicant *wpa_s = 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(wpa_s, 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 wpas_nan_usd_init(struct wpa_supplicant *wpa_s)
{
struct nan_callbacks cb;
os_memset(&cb, 0, sizeof(cb));
cb.ctx = wpa_s;
cb.tx = wpas_nan_de_tx;
cb.listen = wpas_nan_de_listen;
cb.discovery_result = wpas_nan_de_discovery_result;
cb.replied = wpas_nan_de_replied;
cb.publish_terminated = wpas_nan_de_publish_terminated;
cb.subscribe_terminated = wpas_nan_de_subscribe_terminated;
cb.receive = wpas_nan_de_receive;
wpa_s->nan_de = nan_de_init(wpa_s->own_addr, false, &cb);
if (!wpa_s->nan_de)
return -1;
return 0;
}
void wpas_nan_usd_deinit(struct wpa_supplicant *wpa_s)
{
nan_de_deinit(wpa_s->nan_de);
wpa_s->nan_de = NULL;
}
void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
unsigned int freq, const u8 *buf, size_t len)
{
if (!wpa_s->nan_de)
return;
nan_de_rx_sdf(wpa_s->nan_de, src, freq, buf, len);
}
void wpas_nan_usd_flush(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->nan_de)
return;
nan_de_flush(wpa_s->nan_de);
}
int wpas_nan_usd_publish(struct wpa_supplicant *wpa_s, 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 (!wpa_s->nan_de)
return -1;
publish_id = nan_de_publish(wpa_s->nan_de, service_name, srv_proto_type,
ssi, elems, params);
wpabuf_free(elems);
return publish_id;
}
void wpas_nan_usd_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id)
{
if (!wpa_s->nan_de)
return;
nan_de_cancel_publish(wpa_s->nan_de, publish_id);
}
int wpas_nan_usd_update_publish(struct wpa_supplicant *wpa_s, int publish_id,
const struct wpabuf *ssi)
{
if (!wpa_s->nan_de)
return -1;
return nan_de_update_publish(wpa_s->nan_de, publish_id, ssi);
}
int wpas_nan_usd_subscribe(struct wpa_supplicant *wpa_s,
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 (!wpa_s->nan_de)
return -1;
subscribe_id = nan_de_subscribe(wpa_s->nan_de, service_name,
srv_proto_type, ssi, elems, params);
wpabuf_free(elems);
return subscribe_id;
}
void wpas_nan_usd_cancel_subscribe(struct wpa_supplicant *wpa_s,
int subscribe_id)
{
if (!wpa_s->nan_de)
return;
nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id);
}
int wpas_nan_usd_transmit(struct wpa_supplicant *wpa_s, int handle,
const struct wpabuf *ssi, const struct wpabuf *elems,
const u8 *peer_addr, u8 req_instance_id)
{
if (!wpa_s->nan_de)
return -1;
return nan_de_transmit(wpa_s->nan_de, handle, ssi, elems, peer_addr,
req_instance_id);
}
void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration)
{
wpas_nan_usd_listen_work_done(wpa_s);
if (wpa_s->nan_de)
nan_de_listen_started(wpa_s->nan_de, freq, duration);
}
void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
if (wpa_s->nan_de)
nan_de_listen_ended(wpa_s->nan_de, freq);
}
void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s)
{
wpas_nan_usd_tx_work_done(wpa_s);
if (wpa_s->nan_de)
nan_de_tx_wait_ended(wpa_s->nan_de);
}
int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s)
{
int i, j;
int *freqs = NULL;
if (!wpa_s->hw.modes)
return NULL;
for (i = 0; i < wpa_s->hw.num_modes; i++) {
struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
for (j = 0; j < mode->num_channels; j++) {
struct hostapd_channel_data *chan = &mode->channels[j];
/* All 20 MHz channels on 2.4 and 5 GHz band */
if (chan->freq < 2412 || chan->freq > 5900)
continue;
/* that allow frames to be transmitted */
if (chan->flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_NO_IR |
HOSTAPD_CHAN_RADAR))
continue;
int_array_add_unique(&freqs, chan->freq);
}
}
return freqs;
}

46
wpa_supplicant/nan_usd.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_H
#define NAN_USD_H
struct nan_subscribe_params;
struct nan_publish_params;
enum nan_service_protocol_type;
int wpas_nan_usd_init(struct wpa_supplicant *wpa_s);
void wpas_nan_usd_deinit(struct wpa_supplicant *wpa_s);
void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
unsigned int freq, const u8 *buf, size_t len);
void wpas_nan_usd_flush(struct wpa_supplicant *wpa_s);
int wpas_nan_usd_publish(struct wpa_supplicant *wpa_s, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi,
struct nan_publish_params *params);
void wpas_nan_usd_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id);
int wpas_nan_usd_update_publish(struct wpa_supplicant *wpa_s, int publish_id,
const struct wpabuf *ssi);
int wpas_nan_usd_subscribe(struct wpa_supplicant *wpa_s,
const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi,
struct nan_subscribe_params *params);
void wpas_nan_usd_cancel_subscribe(struct wpa_supplicant *wpa_s,
int subscribe_id);
int wpas_nan_usd_transmit(struct wpa_supplicant *wpa_s, int handle,
const struct wpabuf *ssi, const struct wpabuf *elems,
const u8 *peer_addr, u8 req_instance_id);
void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq,
unsigned int duration);
void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq);
void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s);
int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s);
#endif /* NAN_USD_H */

View file

@ -65,6 +65,7 @@
#include "wpas_kay.h" #include "wpas_kay.h"
#include "mesh.h" #include "mesh.h"
#include "dpp_supplicant.h" #include "dpp_supplicant.h"
#include "nan_usd.h"
#ifdef CONFIG_MESH #ifdef CONFIG_MESH
#include "ap/ap_config.h" #include "ap/ap_config.h"
#include "ap/hostapd.h" #include "ap/hostapd.h"
@ -750,6 +751,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpa_s->dpp = NULL; wpa_s->dpp = NULL;
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
wpas_nan_usd_deinit(wpa_s);
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_PASN #ifdef CONFIG_PASN
wpas_pasn_auth_stop(wpa_s); wpas_pasn_auth_stop(wpa_s);
#endif /* CONFIG_PASN */ #endif /* CONFIG_PASN */
@ -7333,6 +7338,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
return -1; return -1;
#endif /* CONFIG_DPP */ #endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
if (wpas_nan_usd_init(wpa_s) < 0)
return -1;
#endif /* CONFIG_NAN_USD */
if (wpa_supplicant_init_eapol(wpa_s) < 0) if (wpa_supplicant_init_eapol(wpa_s) < 0)
return -1; return -1;
wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);

View file

@ -1599,6 +1599,12 @@ struct wpa_supplicant {
* owe_transition_search == 1 */ * owe_transition_search == 1 */
int *owe_trans_scan_freq; int *owe_trans_scan_freq;
#endif /* CONFIG_OWE */ #endif /* CONFIG_OWE */
#ifdef CONFIG_NAN_USD
struct nan_de *nan_de;
struct wpa_radio_work *nan_usd_listen_work;
struct wpa_radio_work *nan_usd_tx_work;
#endif /* CONFIG_NAN_USD */
}; };