blob: 9c4f7a4e1db81014380a21ac13fedaf6629eb2a8 [file] [log] [blame]
developer617abbd2024-04-23 14:50:01 +08001From 84123bd3df810acd8d463a31d519005cfd0cc8d0 Mon Sep 17 00:00:00 2001
2From: Evelyn Tsai <evelyn.tsai@mediatek.com>
3Date: Wed, 20 Mar 2024 07:13:01 +0800
4Subject: [PATCH 023/104] backport: hostapd: afcd: add AFC daemon support
5
6Introduce Automated Frequency Coordination Daemon (AFCD) support
7for UNII-5 and UNII-7 6GHz bands.
8AFCD will be used by hostapd AFC client in order to forward the AFC
9request to the AFC coordinator and decouple AFC connection management
10from hostapd.
11AFC is required for Standard Power Devices (SPDs) to determine a lists
12of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
13AFCD is tested with AFC DUT Test Harness [0].
14
15[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
16
17Tested-by: Allen Ye <allen.ye@mediatek.com>
18Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
19---
20 afc/.gitignore | 1 +
21 afc/Makefile | 31 ++++++
22 afc/afcd.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++++
23 3 files changed, 324 insertions(+)
24 create mode 100644 afc/.gitignore
25 create mode 100644 afc/Makefile
26 create mode 100644 afc/afcd.c
27
28diff --git a/afc/.gitignore b/afc/.gitignore
29new file mode 100644
30index 000000000..8d8cca905
31--- /dev/null
32+++ b/afc/.gitignore
33@@ -0,0 +1 @@
34+afcd
35diff --git a/afc/Makefile b/afc/Makefile
36new file mode 100644
37index 000000000..a83bd01db
38--- /dev/null
39+++ b/afc/Makefile
40@@ -0,0 +1,31 @@
41+ALL=afcd
42+
43+include ../src/build.rules
44+
45+CFLAGS += -I../src/utils
46+CFLAGS += -I../src
47+
48+OBJS=afcd.o
49+OBJS += ../src/utils/common.o
50+OBJS += ../src/utils/wpa_debug.o
51+OBJS += ../src/utils/wpabuf.o
52+
53+ifndef CONFIG_OS
54+ifdef CONFIG_NATIVE_WINDOWS
55+CONFIG_OS=win32
56+else
57+CONFIG_OS=unix
58+endif
59+endif
60+OBJS += ../src/utils/os_$(CONFIG_OS).o
61+
62+LIBS += -lcurl
63+
64+_OBJS_VAR := OBJS
65+include ../src/objs.mk
66+afcd: $(OBJS)
67+ $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
68+ @$(E) " LD " $@
69+
70+clean: common-clean
71+ rm -f core *~
72diff --git a/afc/afcd.c b/afc/afcd.c
73new file mode 100644
74index 000000000..f502846c5
75--- /dev/null
76+++ b/afc/afcd.c
77@@ -0,0 +1,292 @@
78+/*
79+ * Automated Frequency Coordination Daemon
80+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
81+ *
82+ * This software may be distributed under the terms of the BSD license.
83+ * See README for more details.
84+ */
85+
86+#include <curl/curl.h>
87+#include <sys/un.h>
88+#include <sys/stat.h>
89+
90+#include "utils/includes.h"
91+#include "utils/common.h"
92+
93+#define CURL_TIMEOUT 60
94+#define AFCD_SOCK "afcd.sock"
95+
96+struct curl_ctx {
97+ char *buf;
98+ size_t buf_len;
99+};
100+
101+static volatile bool exiting;
102+
103+static char *path = "/var/run";
104+static char *bearer_token;
105+static char *url;
106+static int port = 443;
107+
108+
109+static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
110+ void *userdata)
111+{
112+ struct curl_ctx *ctx = userdata;
113+ char *buf;
114+
115+ buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
116+ if (!buf)
117+ return 0;
118+
119+ ctx->buf = buf;
120+ os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
121+ buf[ctx->buf_len + size * nmemb] = '\0';
122+ ctx->buf_len += size * nmemb;
123+
124+ return size * nmemb;
125+}
126+
127+
128+static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
129+{
130+ struct curl_slist *headers = NULL;
131+ CURL *curl;
132+ int ret;
133+
134+ wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
135+
136+ curl_global_init(CURL_GLOBAL_ALL);
137+ curl = curl_easy_init();
138+ if (!curl)
139+ return -ENOMEM;
140+
141+ headers = curl_slist_append(headers, "Accept: application/json");
142+ headers = curl_slist_append(headers,
143+ "Content-Type: application/json");
144+ headers = curl_slist_append(headers, "charset: utf-8");
145+
146+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
147+ curl_easy_setopt(curl, CURLOPT_URL, url);
148+ curl_easy_setopt(curl, CURLOPT_PORT, port);
149+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
150+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
151+ afcd_curl_cb_write);
152+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
153+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
154+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
155+ curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
156+ if (bearer_token)
157+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
158+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
159+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
160+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
161+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
162+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
163+
164+ ret = curl_easy_perform(curl);
165+ if (ret != CURLE_OK)
166+ wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
167+ curl_easy_strerror(ret));
168+
169+ curl_easy_cleanup(curl);
170+ curl_global_cleanup();
171+
172+ return ret == CURLE_OK ? 0 : -EINVAL;
173+}
174+
175+
176+static void handle_term(int sig)
177+{
178+ wpa_printf(MSG_ERROR, "Received signal %d", sig);
179+ exiting = true;
180+}
181+
182+
183+static void usage(void)
184+{
185+ wpa_printf(MSG_ERROR,
186+ "%s:\n"
187+ "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
188+ __func__);
189+}
190+
191+
192+#define BUFSIZE 8192
193+static int afcd_server_run(void)
194+{
195+ size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
196+ struct sockaddr_un addr = {
197+ .sun_family = AF_UNIX,
198+#ifdef __FreeBSD__
199+ .sun_len = sizeof(addr),
200+#endif /* __FreeBSD__ */
201+ };
202+ int sockfd, ret = 0;
203+ char *fname = NULL;
204+ unsigned char *buf;
205+ fd_set read_set;
206+
207+ if (len >= sizeof(addr.sun_path))
208+ return -EINVAL;
209+
210+ if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
211+ return -EINVAL;
212+
213+ buf = os_malloc(BUFSIZE);
214+ if (!buf)
215+ return -ENOMEM;
216+
217+ fname = os_malloc(len + 1);
218+ if (!fname) {
219+ ret = -ENOMEM;
220+ goto free_buf;
221+ }
222+
223+ os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
224+ fname[len] = '\0';
225+ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
226+
227+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
228+ if (sockfd < 0) {
229+ wpa_printf(MSG_ERROR, "Failed creating socket");
230+ ret = -errno;
231+ goto unlink;
232+ }
233+
234+ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
235+ wpa_printf(MSG_ERROR, "Failed to bind socket");
236+ ret = -errno;
237+ goto close;
238+ }
239+
240+ if (listen(sockfd, 10) < 0) {
241+ wpa_printf(MSG_ERROR, "Failed to listen on socket");
242+ ret = -errno;
243+ goto close;
244+ }
245+
246+ FD_ZERO(&read_set);
247+ while (!exiting) {
248+ socklen_t addr_len = sizeof(addr);
249+ struct sockaddr_in6 c_addr;
250+ struct timeval timeout = {
251+ .tv_sec = 1,
252+ };
253+ struct curl_ctx ctx = {};
254+ int fd;
255+
256+ FD_SET(sockfd, &read_set);
257+ if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
258+ if (errno != EINTR) {
259+ wpa_printf(MSG_ERROR,
260+ "Select failed on socket");
261+ ret = -errno;
262+ break;
263+ }
264+ continue;
265+ }
266+
267+ if (!FD_ISSET(sockfd, &read_set))
268+ continue;
269+
270+ fd = accept(sockfd, (struct sockaddr *)&c_addr,
271+ &addr_len);
272+ if (fd < 0) {
273+ if (errno != EINTR) {
274+ wpa_printf(MSG_ERROR,
275+ "Failed accepting connections");
276+ ret = -errno;
277+ break;
278+ }
279+ continue;
280+ }
281+
282+ os_memset(buf, 0, BUFSIZE);
283+ if (recv(fd, buf, BUFSIZE, 0) <= 0) {
284+ close(fd);
285+ continue;
286+ }
287+
288+ wpa_printf(MSG_DEBUG, "Received request: %s", buf);
289+ if (!afcd_send_request(&ctx, buf)) {
290+ wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
291+ send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
292+ free(ctx.buf);
293+ }
294+ close(fd);
295+ }
296+close:
297+ close(sockfd);
298+unlink:
299+ unlink(fname);
300+ os_free(fname);
301+free_buf:
302+ os_free(buf);
303+
304+ return ret;
305+}
306+
307+
308+int main(int argc, char **argv)
309+{
310+ bool daemonize = false;
311+ char *pid_file = NULL;
312+
313+ if (os_program_init())
314+ return -1;
315+
316+ for (;;) {
317+ int c = getopt(argc, argv, "u:p:t:D:P:hdB");
318+
319+ if (c < 0)
320+ break;
321+
322+ switch (c) {
323+ case 'h':
324+ usage();
325+ return 0;
326+ case 'B':
327+ daemonize = true;
328+ break;
329+ case 'D':
330+ path = optarg;
331+ break;
332+ case 'P':
333+ os_free(pid_file);
334+ pid_file = os_rel2abs_path(optarg);
335+ break;
336+ case 'u':
337+ url = optarg;
338+ break;
339+ case 'p':
340+ port = atoi(optarg);
341+ break;
342+ case 'd':
343+ if (wpa_debug_level > 0)
344+ wpa_debug_level--;
345+ break;
346+ case 't':
347+ bearer_token = optarg;
348+ break;
349+ default:
350+ usage();
351+ return -EINVAL;
352+ }
353+ }
354+
355+ if (!url) {
356+ usage();
357+ return -EINVAL;
358+ }
359+
360+ if (daemonize && os_daemonize(pid_file)) {
361+ wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
362+ return -EINVAL;
363+ }
364+
365+ signal(SIGTERM, handle_term);
366+ signal(SIGINT, handle_term);
367+
368+ return afcd_server_run();
369+}
370--
3712.39.2
372