1 /*
2  * NAN unsynchronized service discovery (USD)
3  * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 
11 #include "utils/common.h"
12 #include "common/wpa_ctrl.h"
13 #include "common/nan_de.h"
14 #include "hostapd.h"
15 #include "ap_drv_ops.h"
16 #include "nan_usd_ap.h"
17 
18 
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)19 static int hostapd_nan_de_tx(void *ctx, unsigned int freq,
20 			     unsigned int wait_time,
21 			     const u8 *dst, const u8 *src, const u8 *bssid,
22 			     const struct wpabuf *buf)
23 {
24 	struct hostapd_data *hapd = ctx;
25 
26 	wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
27 		   " A3=" MACSTR " len=%zu",
28 		   MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
29 		   wpabuf_len(buf));
30 
31 	/* TODO: Force use of OFDM */
32 	return hostapd_drv_send_action_forced_addr3(hapd, hapd->iface->freq, 0,
33 						    dst, bssid,
34 						    wpabuf_head(buf),
35 						    wpabuf_len(buf));
36 }
37 
38 
hostapd_nan_de_listen(void * ctx,unsigned int freq,unsigned int duration)39 static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
40 			      unsigned int duration)
41 {
42 	return 0;
43 }
44 
45 
46 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)47 hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
48 				enum nan_service_protocol_type srv_proto_type,
49 				const u8 *ssi, size_t ssi_len,
50 				int peer_publish_id, const u8 *peer_addr,
51 				bool fsd, bool fsd_gas)
52 {
53 	struct hostapd_data *hapd = ctx;
54 	char *ssi_hex;
55 
56 	ssi_hex = os_zalloc(2 * ssi_len + 1);
57 	if (!ssi_hex)
58 		return;
59 	if (ssi)
60 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
61 	wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
62 		"subscribe_id=%d publish_id=%d address=" MACSTR
63 		" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
64 		subscribe_id, peer_publish_id, MAC2STR(peer_addr),
65 		fsd, fsd_gas, srv_proto_type, ssi_hex);
66 	os_free(ssi_hex);
67 }
68 
69 
70 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)71 hostapd_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr,
72 		       int peer_subscribe_id,
73 		       enum nan_service_protocol_type srv_proto_type,
74 		       const u8 *ssi, size_t ssi_len)
75 {
76 	struct hostapd_data *hapd = ctx;
77 	char *ssi_hex;
78 
79 	ssi_hex = os_zalloc(2 * ssi_len + 1);
80 	if (!ssi_hex)
81 		return;
82 	if (ssi)
83 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
84 	wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_REPLIED
85 		"publish_id=%d address=" MACSTR
86 		" subscribe_id=%d srv_proto_type=%u ssi=%s",
87 		publish_id, MAC2STR(peer_addr), peer_subscribe_id,
88 		srv_proto_type, ssi_hex);
89 	os_free(ssi_hex);
90 }
91 
92 
nan_reason_txt(enum nan_de_reason reason)93 static const char * nan_reason_txt(enum nan_de_reason reason)
94 {
95 	switch (reason) {
96 	case NAN_DE_REASON_TIMEOUT:
97 		return "timeout";
98 	case NAN_DE_REASON_USER_REQUEST:
99 		return "user-request";
100 	case NAN_DE_REASON_FAILURE:
101 		return "failure";
102 	}
103 
104 	return "unknown";
105 }
106 
107 
hostapd_nan_de_publish_terminated(void * ctx,int publish_id,enum nan_de_reason reason)108 static void hostapd_nan_de_publish_terminated(void *ctx, int publish_id,
109 					      enum nan_de_reason reason)
110 {
111 	struct hostapd_data *hapd = ctx;
112 
113 	wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_PUBLISH_TERMINATED
114 		"publish_id=%d reason=%s",
115 		publish_id, nan_reason_txt(reason));
116 }
117 
118 
hostapd_nan_de_subscribe_terminated(void * ctx,int subscribe_id,enum nan_de_reason reason)119 static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
120 						enum nan_de_reason reason)
121 {
122 	struct hostapd_data *hapd = ctx;
123 
124 	wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
125 		"subscribe_id=%d reason=%s",
126 		subscribe_id, nan_reason_txt(reason));
127 }
128 
129 
hostapd_nan_de_receive(void * ctx,int id,int peer_instance_id,const u8 * ssi,size_t ssi_len,const u8 * peer_addr)130 static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id,
131 				   const u8 *ssi, size_t ssi_len,
132 				   const u8 *peer_addr)
133 {
134 	struct hostapd_data *hapd = ctx;
135 	char *ssi_hex;
136 
137 	ssi_hex = os_zalloc(2 * ssi_len + 1);
138 	if (!ssi_hex)
139 		return;
140 	if (ssi)
141 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
142 	wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_RECEIVE
143 		"id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
144 		id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
145 	os_free(ssi_hex);
146 }
147 
148 
hostapd_nan_usd_init(struct hostapd_data * hapd)149 int hostapd_nan_usd_init(struct hostapd_data *hapd)
150 {
151 	struct nan_callbacks cb;
152 
153 	os_memset(&cb, 0, sizeof(cb));
154 	cb.ctx = hapd;
155 	cb.tx = hostapd_nan_de_tx;
156 	cb.listen = hostapd_nan_de_listen;
157 	cb.discovery_result = hostapd_nan_de_discovery_result;
158 	cb.replied = hostapd_nan_de_replied;
159 	cb.publish_terminated = hostapd_nan_de_publish_terminated;
160 	cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
161 	cb.receive = hostapd_nan_de_receive;
162 
163 	hapd->nan_de = nan_de_init(hapd->own_addr, false, true, 0, &cb);
164 	if (!hapd->nan_de)
165 		return -1;
166 	return 0;
167 }
168 
169 
hostapd_nan_usd_deinit(struct hostapd_data * hapd)170 void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
171 {
172 	nan_de_deinit(hapd->nan_de);
173 	hapd->nan_de = NULL;
174 }
175 
176 
hostapd_nan_usd_rx_sdf(struct hostapd_data * hapd,const u8 * src,const u8 * a3,unsigned int freq,const u8 * buf,size_t len)177 void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
178 			    const u8 *a3, unsigned int freq,
179 			    const u8 *buf, size_t len)
180 {
181 	if (!hapd->nan_de)
182 		return;
183 	nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len);
184 }
185 
186 
hostapd_nan_usd_flush(struct hostapd_data * hapd)187 void hostapd_nan_usd_flush(struct hostapd_data *hapd)
188 {
189 	if (!hapd->nan_de)
190 		return;
191 	nan_de_flush(hapd->nan_de);
192 }
193 
194 
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,bool p2p)195 int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
196 			    enum nan_service_protocol_type srv_proto_type,
197 			    const struct wpabuf *ssi,
198 			    struct nan_publish_params *params, bool p2p)
199 {
200 	int publish_id;
201 	struct wpabuf *elems = NULL;
202 
203 	if (!hapd->nan_de)
204 		return -1;
205 
206 	publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
207 				    ssi, elems, params, p2p);
208 	wpabuf_free(elems);
209 	return publish_id;
210 }
211 
212 
hostapd_nan_usd_cancel_publish(struct hostapd_data * hapd,int publish_id)213 void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id)
214 {
215 	if (!hapd->nan_de)
216 		return;
217 	nan_de_cancel_publish(hapd->nan_de, publish_id);
218 }
219 
220 
hostapd_nan_usd_update_publish(struct hostapd_data * hapd,int publish_id,const struct wpabuf * ssi)221 int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
222 				   const struct wpabuf *ssi)
223 {
224 	int ret;
225 
226 	if (!hapd->nan_de)
227 		return -1;
228 	ret = nan_de_update_publish(hapd->nan_de, publish_id, ssi);
229 	return ret;
230 }
231 
232 
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,bool p2p)233 int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
234 			      const char *service_name,
235 			      enum nan_service_protocol_type srv_proto_type,
236 			      const struct wpabuf *ssi,
237 			      struct nan_subscribe_params *params, bool p2p)
238 {
239 	int subscribe_id;
240 	struct wpabuf *elems = NULL;
241 
242 	if (!hapd->nan_de)
243 		return -1;
244 
245 	subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
246 					srv_proto_type, ssi, elems, params, p2p);
247 	wpabuf_free(elems);
248 	return subscribe_id;
249 }
250 
251 
hostapd_nan_usd_cancel_subscribe(struct hostapd_data * hapd,int subscribe_id)252 void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
253 				      int subscribe_id)
254 {
255 	if (!hapd->nan_de)
256 		return;
257 	nan_de_cancel_subscribe(hapd->nan_de, subscribe_id);
258 }
259 
260 
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)261 int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
262 			     const struct wpabuf *ssi,
263 			     const struct wpabuf *elems,
264 			     const u8 *peer_addr,
265 			     u8 req_instance_id)
266 {
267 	if (!hapd->nan_de)
268 		return -1;
269 	return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
270 			       req_instance_id);
271 }
272