From e3f9ab3c3a01d6d0070c02f7539c837dad7eb835 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 10 Feb 2024 11:57:23 +0200 Subject: [PATCH] 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 --- src/common/wpa_ctrl.h | 7 + src/drivers/driver_nl80211.c | 7 + wpa_supplicant/Android.mk | 6 + wpa_supplicant/Makefile | 6 + wpa_supplicant/README-NAN-USD | 147 +++++++++ wpa_supplicant/android.config | 3 + wpa_supplicant/ctrl_iface.c | 356 +++++++++++++++++++++ wpa_supplicant/defconfig | 3 + wpa_supplicant/events.c | 24 ++ wpa_supplicant/nan_usd.c | 513 ++++++++++++++++++++++++++++++ wpa_supplicant/nan_usd.h | 46 +++ wpa_supplicant/wpa_supplicant.c | 10 + wpa_supplicant/wpa_supplicant_i.h | 6 + 13 files changed, 1134 insertions(+) create mode 100644 wpa_supplicant/README-NAN-USD create mode 100644 wpa_supplicant/nan_usd.c create mode 100644 wpa_supplicant/nan_usd.h diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 154bac8fe..c5bb9abd7 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -232,6 +232,13 @@ extern "C" { #define DPP_EVENT_PB_RESULT "DPP-PB-RESULT " #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 */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 01d8a5ab7..aa5ed58b2 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2564,6 +2564,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) 5) < 0) ret = -1; #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 /* DPP Public Action */ if (nl80211_register_action_frame(bss, diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index c80c17a0b..b6fcebd69 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -281,6 +281,12 @@ L_CFLAGS += -DCONFIG_DPP3 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 L_CFLAGS += -DCONFIG_OWE NEED_ECC=y diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 60042bcc7..288f85536 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -315,6 +315,12 @@ CFLAGS += -DCONFIG_DPP3 endif endif +ifdef CONFIG_NAN_USD +OBJS += ../src/common/nan_de.o +OBJS += nan_usd.o +CFLAGS += -DCONFIG_NAN_USD +endif + ifdef CONFIG_OWE CFLAGS += -DCONFIG_OWE NEED_ECC=y diff --git a/wpa_supplicant/README-NAN-USD b/wpa_supplicant/README-NAN-USD new file mode 100644 index 000000000..5dfe6eeed --- /dev/null +++ b/wpa_supplicant/README-NAN-USD @@ -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= [ttl=] [freq=] [freq_list=] [srv_proto_type=] [ssi=] [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= + +This command maps to the CancelPublish() method in the NAN Discovery +Engine. + +NAN_UPDATE_PUBLISH publish_id= [ssi=] + +This command maps to the UpdatePublish() method in the NAN Discovery +Engine. + +NAN_SUBSCRIBE service_name= [active=1] [ttl=] [freq=] [srv_proto_type=] [ssi=] + +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= + +This command maps to the CancelSubscribe() method in the NAN Discovery Engine. + +NAN_TRANSMIT handle= req_instance= address= [ssi=] + +This command maps to the Transmit() method in the NAN Discovery Engine. + +Following control interface events are used: + +NAN-DISCOVERY-RESULT subscribe_id= publish_id= address= fsd=<0/1> fsd_gas=<0/1> srv_proto_type= ssi= + +This event maps to the DiscoveryResult() event in the NAN Discovery +Engine. + +NAN-REPLIED publish_id= address= subscribe_id= srv_proto_type= ssi= + +This event maps to the Replied() event in the NAN Discovery Engine. + +NAN-PUBLISH-TERMINATED publish_id= reason= + +This event maps to the PublishTerminated() event in the NAN Discovery +Engine. + +NAN-SUBSCRIBE-TERMINATED subscribe_id= reason= + +This event maps to the SubscribeTerminate() event in the NAN Discovery +Engine. + +NAN-RECEIVE id= peer_instance_id= address= ssi= + +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 diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index 9aff882e2..5ae3808fb 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -554,4 +554,7 @@ CONFIG_WEP=y # Disable support for WMM admission control #CONFIG_NO_WMM_AC=y +# Wi-Fi Aware unsynchronized service discovery (NAN USD) +#CONFIG_NAN_USD=y + include $(wildcard $(LOCAL_PATH)/android_config_*.inc) diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index f3a31160f..500f4d1b5 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -22,6 +22,7 @@ #ifdef CONFIG_DPP #include "common/dpp.h" #endif /* CONFIG_DPP */ +#include "common/nan_de.h" #include "common/ptksa_cache.h" #include "crypto/tls.h" #include "ap/hostapd.h" @@ -58,6 +59,7 @@ #include "mesh.h" #include "dpp_supplicant.h" #include "sme.h" +#include "nan_usd.h" #ifdef __NetBSD__ #include @@ -4875,6 +4877,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #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 if (os_strcmp(field, "sae") == 0 && (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); 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 */ +#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(¶ms, 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, ¶ms); + 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(¶ms, 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, + ¶ms); + 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 *buf, size_t *resp_len) { @@ -13151,6 +13487,26 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; #endif /* CONFIG_DPP3 */ #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 } else if (os_strncmp(buf, "PASN_START ", 11) == 0) { if (wpas_ctrl_iface_pasn_start(wpa_s, buf + 11) < 0) diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 60d1bfc57..52befd8f1 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -683,3 +683,6 @@ CONFIG_DPP2=y # Disable support for WMM admission control #CONFIG_NO_WMM_AC=y + +# Wi-Fi Aware unsynchronized service discovery (NAN USD) +#CONFIG_NAN_USD=y diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index e168cf38b..201e43f67 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -50,6 +50,7 @@ #include "mesh.h" #include "mesh_mpm.h" #include "wmm_ac.h" +#include "nan_usd.h" #include "dpp_supplicant.h" @@ -5363,6 +5364,17 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #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 if (category == WLAN_ACTION_PUBLIC && plen >= 5 && 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, data->remain_on_channel.duration); #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; case EVENT_CANCEL_REMAIN_ON_CHANNEL: #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( wpa_s, data->remain_on_channel.freq); #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; case EVENT_EAPOL_RX: 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 wpas_dpp_tx_wait_expire(wpa_s); #endif /* CONFIG_DPP */ +#ifdef CONFIG_NAN_USD + wpas_nan_usd_tx_wait_expire(wpa_s); +#endif /* CONFIG_NAN_USD */ break; case EVENT_TID_LINK_MAP: if (data) diff --git a/wpa_supplicant/nan_usd.c b/wpa_supplicant/nan_usd.c new file mode 100644 index 000000000..657b302c1 --- /dev/null +++ b/wpa_supplicant/nan_usd.c @@ -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; +} diff --git a/wpa_supplicant/nan_usd.h b/wpa_supplicant/nan_usd.h new file mode 100644 index 000000000..149ac9e60 --- /dev/null +++ b/wpa_supplicant/nan_usd.h @@ -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 */ diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 172a863cb..91a213dac 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -65,6 +65,7 @@ #include "wpas_kay.h" #include "mesh.h" #include "dpp_supplicant.h" +#include "nan_usd.h" #ifdef CONFIG_MESH #include "ap/ap_config.h" #include "ap/hostapd.h" @@ -750,6 +751,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ +#ifdef CONFIG_NAN_USD + wpas_nan_usd_deinit(wpa_s); +#endif /* CONFIG_NAN_USD */ + #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); #endif /* CONFIG_PASN */ @@ -7333,6 +7338,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; #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) return -1; wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 933fc3626..6cacfe485 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1599,6 +1599,12 @@ struct wpa_supplicant { * owe_transition_search == 1 */ int *owe_trans_scan_freq; #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 */ };