From patchwork Wed Apr 10 13:32:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "lorenzo@kernel.org" X-Patchwork-Id: 1922006 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=My0b3xsK; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20201202 header.b=EuREB4mJ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VF3gP6MHfz1yZY for ; Wed, 10 Apr 2024 23:33:21 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=q7IhzyFEMbM178u74HKdjxO6o3TbZ3MTViObhJ/mxAs=; b=My0b3xsK3IoApr SlDqvsSAL+XJYSMZkUHljD+62BgLoA2PhTY/aASPV/izMam1grKoC0ljyg4aOu81HQ+ThGf+18iV8 qqB/Udoobm50c49QnYyxsZPO339e+yn/TDBlboDpZYotjAnQQbydhHL4iRHO8NOue+hJAMd0AcByr iRRh1QYFzTkTgRl6335HAtZ44+h3shyvcrlrOb+ZR7TMHSt/pVqQlnpv5KuNPaHKy3gnP+hNQGp4z N8T8qBcA/ZgZVGDPK+Jb96cRbwYzQPmNnsIyEb8Ygk0RBv1V3L7chySfPcN7rh19Sme5xz3qETXZV s8dC+cIg8q04Xnf38Dyw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1ruY4L-00000007BPy-3W3F; Wed, 10 Apr 2024 13:32:53 +0000 Received: from dfw.source.kernel.org ([139.178.84.217]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1ruY4H-00000007BO3-2m2s for hostap@lists.infradead.org; Wed, 10 Apr 2024 13:32:51 +0000 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id C86AA61CD6; Wed, 10 Apr 2024 13:32:48 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id DD089C433F1; Wed, 10 Apr 2024 13:32:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1712755968; bh=Iq1/mhc/cIM+qrrgyVAiB/qI/72MD3YioLn3/wBXdHg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EuREB4mJI5bNpCw/Rkbr7m9zMyxtn5NCPKxkyJfupXIlYtyDpRM2dNHkbo5UUOsfI Pr3QYV4tct2cHsQdsQIFX5iAg/J6/56iugXKfrH1eXZO4Ztj1HdMRdI1bHr+yaOGlr GCEnQJILhVEt98Ch/UdW73crlOBexyoB9iXH7+U6/e+NpD1k80AZwZk/Lreb2bR40g WMyC7BIRSbKpzynC6DJzmNsWGEiE/GdMDhxiXqxp4mG8Ks64J6SKAu2ftfY8+UhWYq US+j6h2WJT/pJnKspIntPGIxo3FgPLwhOLnGHIsBV61c29W4BFQUjI9CJ2q3dceWJ9 HW2SAUoav8x7Q== From: Lorenzo Bianconi To: hostap@lists.infradead.org Cc: j@w1.fi, lorenzo.bianconi83@gmail.com, ryder.lee@mediatek.com, evelyn.tsai@mediatek.com, nbd@nbd.name, allen.ye@mediatek.com Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support Date: Wed, 10 Apr 2024 15:32:19 +0200 Message-ID: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> X-Mailer: git-send-email 2.44.0 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240410_063249_882526_690192FF X-CRM114-Status: GOOD ( 22.09 ) X-Spam-Score: -7.2 (-------) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Introduce Automated Frequency Coordination Daemon (AFCD) support for UNII-5 and UNII-7 6GHz bands. AFCD will be used by hostapd AFC client in order to forward the AFC request to the AFC coordinator an [...] Content analysis details: (-7.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at https://www.dnswl.org/, high trust [139.178.84.217 listed in list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -2.0 DKIMWL_WL_HIGH DKIMwl.org - High trust sender X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Introduce Automated Frequency Coordination Daemon (AFCD) support for UNII-5 and UNII-7 6GHz bands. AFCD will be used by hostapd AFC client in order to forward the AFC request to the AFC coordinator and decouple AFC connection management from hostapd. AFC is required for Standard Power Devices (SPDs) to determine a lists of channels and EIRP/PSD powers that are available in the 6GHz spectrum. AFCD is tested with AFC DUT Test Harness [0]. Add afc-reply.json as reference for replies from the AFC coordinator. [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- afc/.gitignore | 1 + afc/Makefile | 31 +++++ afc/afc-reply.json | 215 +++++++++++++++++++++++++++++++++ afc/afcd.c | 292 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 539 insertions(+) create mode 100644 afc/.gitignore create mode 100644 afc/Makefile create mode 100644 afc/afc-reply.json create mode 100644 afc/afcd.c diff --git a/afc/.gitignore b/afc/.gitignore new file mode 100644 index 000000000..8d8cca905 --- /dev/null +++ b/afc/.gitignore @@ -0,0 +1 @@ +afcd diff --git a/afc/Makefile b/afc/Makefile new file mode 100644 index 000000000..a83bd01db --- /dev/null +++ b/afc/Makefile @@ -0,0 +1,31 @@ +ALL=afcd + +include ../src/build.rules + +CFLAGS += -I../src/utils +CFLAGS += -I../src + +OBJS=afcd.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif +OBJS += ../src/utils/os_$(CONFIG_OS).o + +LIBS += -lcurl + +_OBJS_VAR := OBJS +include ../src/objs.mk +afcd: $(OBJS) + $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS) + @$(E) " LD " $@ + +clean: common-clean + rm -f core *~ diff --git a/afc/afc-reply.json b/afc/afc-reply.json new file mode 100644 index 000000000..35882c74c --- /dev/null +++ b/afc/afc-reply.json @@ -0,0 +1,215 @@ +{ + "availableSpectrumInquiryResponses":[ + { + "availabilityExpireTime":"2023-02-23T12:53:18Z", + "availableChannelInfo":[ + { + "channelCfi":[ + 1, + 5, + 9, + 13, + 17, + 21, + 25, + 29, + 33, + 37, + 41, + 45, + 49, + 53, + 57, + 61, + 65, + 69, + 73, + 77, + 81, + 85, + 89, + 93, + 117, + 121, + 125, + 129, + 133, + 137, + 141, + 145, + 149, + 153, + 157, + 161, + 165, + 169, + 173, + 177, + 181 + ], + "globalOperatingClass":131, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 3, + 11, + 19, + 27, + 35, + 43, + 51, + 59, + 67, + 75, + 83, + 91, + 123, + 131, + 139, + 147, + 155, + 163, + 171, + 179 + ], + "globalOperatingClass":132, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 7, + 23, + 39, + 55, + 71, + 87, + 135, + 151, + 167 + ], + "globalOperatingClass":133, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 15, + 47, + 79, + 143 + ], + "globalOperatingClass":134, + "maxEirp":[ + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + ], + "globalOperatingClass":135, + "maxEirp":[ + ] + } + ], + "availableFrequencyInfo":[ + { + "frequencyRange":{ + "highFrequency":6425, + "lowFrequency":5925 + }, + "maxPSD":3.98970004336019 + }, + { + "frequencyRange":{ + "highFrequency":6865, + "lowFrequency":6525 + }, + "maxPSD":3.98970004336019 + } + ], + "requestId":"11235814", + "response":{ + "responseCode":0, + "shortDescription":"Success" + }, + "rulesetId":"US_47_CFR_PART_15_SUBPART_E" + } + ], + "version":"1.1" +} diff --git a/afc/afcd.c b/afc/afcd.c new file mode 100644 index 000000000..445db7c39 --- /dev/null +++ b/afc/afcd.c @@ -0,0 +1,292 @@ +/* + * Automated Frequency Coordination Daemon + * Copyright (c) 2024, Lorenzo Bianconi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include +#include + +#include "utils/includes.h" +#include "utils/common.h" + +#define CURL_TIMEOUT 60 +#define AFCD_SOCK "afcd.sock" + +struct curl_ctx { + char *buf; + size_t buf_len; +}; + +static volatile bool exiting; + +static char *path = "/var/run"; +static char *bearer_token; +static char *url; +static int port = 443; + + +static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct curl_ctx *ctx = userdata; + char *buf; + + buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1); + if (!buf) + return 0; + + ctx->buf = buf; + os_memcpy(buf + ctx->buf_len, ptr, size * nmemb); + buf[ctx->buf_len + size * nmemb] = '\0'; + ctx->buf_len += size * nmemb; + + return size * nmemb; +} + + +static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request) +{ + struct curl_slist *headers = NULL; + CURL *curl; + int ret; + + wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url); + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if (!curl) + return -ENOMEM; + + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, + "Content-Type: application/json"); + headers = curl_slist_append(headers, "charset: utf-8"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_PORT, port); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + afcd_curl_cb_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1"); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + if (bearer_token) + curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); + + ret = curl_easy_perform(curl); + if (ret != CURLE_OK) + wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s", + curl_easy_strerror(ret)); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return ret == CURLE_OK ? 0 : -EINVAL; +} + + +static void handle_term(int sig) +{ + wpa_printf(MSG_ERROR, "Received signal %d", sig); + exiting = true; +} + + +static void usage(void) +{ + wpa_printf(MSG_ERROR, + "%s:\n" + "afcd -u [-p][-t][-D][-P][-dB]", + __func__); +} + + +#define BUFSIZE 8192 +static int afcd_server_run(void) +{ + size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK); + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + int sockfd, ret = 0; + char *fname = NULL; + unsigned char *buf; + fd_set read_set; + + if (len >= sizeof(addr.sun_path)) + return -EINVAL; + + if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) + return -EINVAL; + + buf = os_malloc(BUFSIZE); + if (!buf) + return -ENOMEM; + + fname = os_malloc(len + 1); + if (!fname) { + ret = -ENOMEM; + goto free_buf; + } + + os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK); + fname[len] = '\0'; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "Failed creating socket"); + ret = -errno; + goto unlink; + } + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "Failed to bind socket"); + ret = -errno; + goto close; + } + + if (listen(sockfd, 10) < 0) { + wpa_printf(MSG_ERROR, "Failed to listen on socket"); + ret = -errno; + goto close; + } + + FD_ZERO(&read_set); + while (!exiting) { + socklen_t addr_len = sizeof(addr); + struct sockaddr_in6 c_addr; + struct timeval timeout = { + .tv_sec = 1, + }; + struct curl_ctx ctx = {}; + int fd; + + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) { + if (errno != EINTR) { + wpa_printf(MSG_ERROR, + "Select failed on socket"); + ret = -errno; + break; + } + continue; + } + + if (!FD_ISSET(sockfd, &read_set)) + continue; + + fd = accept(sockfd, (struct sockaddr *)&c_addr, + &addr_len); + if (fd < 0) { + if (errno != EINTR) { + wpa_printf(MSG_ERROR, + "Failed accepting connections"); + ret = -errno; + break; + } + continue; + } + + os_memset(buf, 0, BUFSIZE); + if (recv(fd, buf, BUFSIZE - 1, 0) <= 0) { + close(fd); + continue; + } + + wpa_printf(MSG_DEBUG, "Received request: %s", buf); + if (!afcd_send_request(&ctx, buf)) { + wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf); + send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL); + free(ctx.buf); + } + close(fd); + } +close: + close(sockfd); +unlink: + unlink(fname); + os_free(fname); +free_buf: + os_free(buf); + + return ret; +} + + +int main(int argc, char **argv) +{ + bool daemonize = false; + char *pid_file = NULL; + + if (os_program_init()) + return -1; + + for (;;) { + int c = getopt(argc, argv, "u:p:t:D:P:hdB"); + + if (c < 0) + break; + + switch (c) { + case 'h': + usage(); + return 0; + case 'B': + daemonize = true; + break; + case 'D': + path = optarg; + break; + case 'P': + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); + break; + case 'u': + url = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'd': + if (wpa_debug_level > 0) + wpa_debug_level--; + break; + case 't': + bearer_token = optarg; + break; + default: + usage(); + return -EINVAL; + } + } + + if (!url) { + usage(); + return -EINVAL; + } + + if (daemonize && os_daemonize(pid_file)) { + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); + return -EINVAL; + } + + signal(SIGTERM, handle_term); + signal(SIGINT, handle_term); + + return afcd_server_run(); +}