From patchwork Fri Feb 23 15:14:21 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: 1903344 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=aALUJV3K; 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=QDoXoIc/; 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 4ThDBc2bT9z23pm for ; Sat, 24 Feb 2024 02:16:56 +1100 (AEDT) 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=syJuscdO+pPYV80PKlr4jzL8kUY6yhgiZxMmn3Hplsw=; b=aALUJV3KvOSd98 FZ+FALVeWvm8hyHuqlyuFC4Xx8YW5G/8GMfGC1Bl6+rp7ZDl1ixQlduylpQofnSNWSFbUuPFLzS4f W8f28Ei/sn1YxnicniKijhKl+ot4dge8AZneIChxEG72viQP3R2KUPz/eRHh9CnzOfP2mtueuI+yx tSZYrREz7fJg4NWA7EVaYrTTaW0b+AkpEk1SNnLzEdS/S+1VQigsHngdpUQA7iohVd7iny8Nyxws7 tJSMOqglz21GiOKaoSqCnAulsfpNrjo8nM1p92rSwM4FHTbklXqxMGLwZaiXJkHEgobjbWKlIZKBS Z4hSFbAQYfxLxNYoi7Ng==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rdXHO-00000009w1M-2ubE; Fri, 23 Feb 2024 15:16:02 +0000 Received: from dfw.source.kernel.org ([2604:1380:4641:c500::1]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rdXGJ-00000009vqp-41fa for hostap@lists.infradead.org; Fri, 23 Feb 2024 15:15:08 +0000 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 7972A6352E; Fri, 23 Feb 2024 15:14:42 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id E125BC433F1; Fri, 23 Feb 2024 15:14:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1708701282; bh=i0SU/gTVVJ7YZHot4MFOb3+cuL/sHXLu095QIPFvSL0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QDoXoIc/Zi3zmV2IKfiSlRoLjLsKEpOaSmhH8Ty5pLtWDBOg3Cspkdr1KUHexGZNd wOntpnkc2Fsj58dO24EuxlG12Q19uk5BXjsyay0T/ovjNrlpbMZQFADxjbWCdMpPFn BgwEf6ZUqB+ph2OZ3eqDZNpEg+MfoxWinixMZfNBfKuzh6VHqwYcD01HQKinx/ZF9q fHk8Bx71qkG4tOxNXN/0QyAB3KjWV0oKkIQUzEbl3gujb+tGXB68TInJdJdhdLm2Vf JEZGU3dbubm/8jzsoa0dBq+d2MtJUh5mj2KKMrVETLziOny0kj6f7oo16aiXbq2bOb 0Ln2tDvkQ/glg== From: Lorenzo Bianconi To: hostap@lists.infradead.org Cc: j@w1.fi, lorenzo.bianconi@redhat.com, ryder.lee@mediatek.com, evelyn.tsai@mediatek.com, nbd@nbd.name Subject: [PATCH v2 1/3] hostapd: afcd: add AFC daemon support Date: Fri, 23 Feb 2024 16:14:21 +0100 Message-ID: <461c36f665436c95c0f0637b53046926e2e8957e.1708700902.git.lorenzo@kernel.org> X-Mailer: git-send-email 2.43.2 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240223_071456_384783_DAA4D0F2 X-CRM114-Status: GOOD ( 20.63 ) X-Spam-Score: -2.6 (--) 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: (-2.6 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -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 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [2604:1380:4641:c500:0:0:0:1 listed in] [list.dnswl.org] -0.0 T_SCC_BODY_TEXT_LINE No description available. -0.1 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. Signed-off-by: Lorenzo Bianconi --- afc/.gitignore | 1 + afc/Makefile | 31 ++++++ afc/afcd.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 afc/.gitignore create mode 100644 afc/Makefile 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/afcd.c b/afc/afcd.c new file mode 100644 index 000000000..4009b356c --- /dev/null +++ b/afc/afcd.c @@ -0,0 +1,275 @@ +/* + * 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 afc_curl_ctx { + char *buf; + size_t buf_len; +}; + +static volatile bool exiting; +static char *bearer_token; +static char *url; + + +static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct afc_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 afc_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_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); + 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); + + 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 -t [-p][-P][-dB]", + __func__); +} + + +#define BUFSIZE 4096 +static int afcd_server_run(const char *path) +{ + 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; + fd_set read_set; + + if (len >= sizeof(addr.sun_path)) + return -EINVAL; + + if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) + return -EINVAL; + + fname = os_malloc(len + 1); + if (!fname) + return -ENOMEM; + + 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); + unsigned char buf[BUFSIZE] = {}; + struct afc_curl_ctx ctx = {}; + struct sockaddr_in6 c_addr; + struct timeval timeout = { + .tv_sec = 1, + }; + 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; + } + + if (recv(fd, buf, sizeof(buf), 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, 0); + free(ctx.buf); + } + close(fd); + } +close: + close(sockfd); +unlink: + unlink(fname); + os_free(fname); + + return ret; +} + + +int main(int argc, char **argv) +{ + const char *path = "/var/run"; + bool daemonize = false; + char *pid_file = NULL; + + if (os_program_init()) + return -1; + + for (;;) { + int c = getopt(argc, argv, "u:t:p:P:hdB"); + + if (c < 0) + break; + + switch (c) { + case 'h': + usage(); + return 0; + case 'B': + daemonize = true; + break; + case 'p': + path = optarg; + break; + case 'P': + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); + break; + case 'u': + url = 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 || !bearer_token) { + 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(path); +} From patchwork Fri Feb 23 15:14:22 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: 1903345 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=u2uIMqwx; 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=fJJdOzHp; 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 4ThDBc5JlYz23pn for ; Sat, 24 Feb 2024 02:16:52 +1100 (AEDT) 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=H3ibbZ2tdnpF7wjcNoahokVcOy8/ISTobvLQCWq+NV4=; b=u2uIMqwxbvmTBM 6ClKXwUE/R7Rzh6cDcL8qM/86eVP4E86Akzmx2y7logBJ3gc9trkSxvoGcC1SX33kCMr+aFyAJJ3o CQmL6UpGWF+NM4Y51Ryy6Huvo+RPefEhKQotCiJQ28v0NAU0j5BZontOqrCSp2NCyz983p38TlEw6 t8uIkU9aAM/HwbRKCO1nbE97ufaElip8G/Da9BhGWckFhTmR7i82uino6Tz6ZUcLPcGzXVcaldMSx 5sNLt5kZr3F3TiSPDjaGBHvo3KUwW0WqsQVeILO1HTXw0S5jceHGCQvWwuruxxWFAjnjI6RhSxbij xwcfznYB2XP4QwgvOY0A==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rdXHR-00000009w2f-3Hva; Fri, 23 Feb 2024 15:16:05 +0000 Received: from sin.source.kernel.org ([145.40.73.55]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rdXGJ-00000009vqW-3b4Q for hostap@lists.infradead.org; Fri, 23 Feb 2024 15:15:17 +0000 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id F3496CE2C3A; Fri, 23 Feb 2024 15:14:44 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id AD6B5C433C7; Fri, 23 Feb 2024 15:14:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1708701284; bh=ZZG7apcjzsYA3BMu5eAl0usQlxNqgU6dK2+nCD57sGM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fJJdOzHpXwpNa09/EZEA28Cd1pYrM5PWuQdDwuARFbTnoi8FJ7ZxWZpGYX0qOapVM DsadNVeHrMTqgdqqIB7EqV+ORKXEIpFT805jaPhwrd5qjhxJnJYeQfR7Ibmuytq0xj qQO+vnvGjPtmt1NYEYbjiiREKxN7xM2BBSlpxZOOqydUNDUeH+jlbAlvP0RjlnIQCQ qLs6J9q6iezczfEhvMuvF0MIpRxxHvvBmpNyWC57RODLvZT5kUb8Kx+BIV5yemJqT3 NY305sTRATkPWdzPH5dU8oxlNxxxmAU82diP3O5u77owU7blzAj9HJ8mTs7ZQ5GLka PLTYrH0O62Myw== From: Lorenzo Bianconi To: hostap@lists.infradead.org Cc: j@w1.fi, lorenzo.bianconi@redhat.com, ryder.lee@mediatek.com, evelyn.tsai@mediatek.com, nbd@nbd.name Subject: [PATCH v2 2/3] hostapd: ap: add AFC client support Date: Fri, 23 Feb 2024 16:14:22 +0100 Message-ID: X-Mailer: git-send-email 2.43.2 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240223_071507_111821_11E3DFAF X-CRM114-Status: GOOD ( 22.61 ) X-Spam-Score: -2.6 (--) 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 (AFC) support for UNII-5 and UNII-7 6GHz bands. AFC client will connect to AFCD providing AP related parameter for AFC coordinator (e.g. geolocation, support [...] Content analysis details: (-2.6 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [145.40.73.55 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -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 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 T_SCC_BODY_TEXT_LINE No description available. -0.1 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 (AFC) support for UNII-5 and UNII-7 6GHz bands. AFC client will connect to AFCD providing AP related parameter for AFC coordinator (e.g. geolocation, supported frequencies, ..). 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. Signed-off-by: Lorenzo Bianconi --- hostapd/Makefile | 6 + hostapd/config_file.c | 262 +++++++++++++ hostapd/defconfig | 3 + hostapd/hostapd.conf | 42 +++ src/ap/afc.c | 850 ++++++++++++++++++++++++++++++++++++++++++ src/ap/ap_config.c | 16 + src/ap/ap_config.h | 47 +++ src/ap/hostapd.c | 53 +++ src/ap/hostapd.h | 29 ++ src/ap/hw_features.c | 2 + 10 files changed, 1310 insertions(+) create mode 100644 src/ap/afc.c diff --git a/hostapd/Makefile b/hostapd/Makefile index 94421d43b..0514b7bdc 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -103,6 +103,12 @@ CFLAGS += -DCONFIG_TAXONOMY OBJS += ../src/ap/taxonomy.o endif +ifdef CONFIG_AFC +CFLAGS += -DCONFIG_AFC +OBJS += ../src/ap/afc.o +LIBS += -ljson-c +endif + ifdef CONFIG_MODULE_TESTS CFLAGS += -DCONFIG_MODULE_TESTS OBJS += hapd_module_tests.o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 1af083917..fbebaa53d 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2436,6 +2436,191 @@ static int get_u16(const char *pos, int line, u16 *ret_val) #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_AFC +static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos) +{ + struct cert_id *c = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + c = os_realloc_array(c, count + 1, sizeof(*c)); + if (!c) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + c[i].rulset = os_malloc(os_strlen(pos) + 1); + if (!c[i].rulset) + goto error; + + os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1); + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + c[i].id = os_malloc(os_strlen(pos) + 1); + if (!c[i].id) + goto error; + + os_strlcpy(c[i].id, pos, os_strlen(pos) + 1); + pos = p; + } + + conf->afc.n_cert_ids = count; + conf->afc.cert_ids = c; + + return 0; + +error: + for (i = 0; i < count; i++) { + os_free(c[i].rulset); + os_free(c[i].id); + } + os_free(c); + + return -ENOMEM; +} + + +static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data, + unsigned int *n_linear_polygon_data, + char *pos) +{ + struct afc_linear_polygon *d = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p, *end; + + d = os_realloc_array(d, count + 1, sizeof(*d)); + if (!d) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + d[i].longitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + d[i].latitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + } + + *n_linear_polygon_data = count; + *data = d; + + return 0; + +error: + os_free(d); + return -ENOMEM; +} + + +static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos) +{ + struct afc_freq_range *f = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + f = os_realloc_array(f, count + 1, sizeof(*f)); + if (!f) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + f[i].low_freq = atoi(pos); + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + f[i].high_freq = atoi(pos); + pos = p; + } + + conf->afc.n_freq_range = count; + conf->afc.freq_range = f; + + return 0; + +error: + os_free(f); + return -ENOMEM; +} + + +static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos) +{ + unsigned int *oc = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + oc = os_realloc_array(oc, count + 1, sizeof(*oc)); + if (!oc) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + oc[i] = atoi(pos); + pos = p; + } + + conf->afc.n_op_class = count; + conf->afc.op_class = oc; + + return 0; +} +#endif /* CONFIG_AFC */ + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *buf, char *pos, int line) @@ -3800,6 +3985,83 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->unsol_bcast_probe_resp_interval = val; +#ifdef CONFIG_AFC + } else if (os_strcmp(buf, "afcd_sock") == 0) { + conf->afc.socket = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.socket) + return 1; + + os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_request_version") == 0) { + conf->afc.request.version = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.version) + return 1; + + os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_request_id") == 0) { + conf->afc.request.id = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.id) + return 1; + + os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_serial_number") == 0) { + conf->afc.request.sn = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.sn) + return 1; + + os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_cert_ids") == 0) { + if (hostapd_afc_parse_cert_ids(conf, pos)) + return 1; + } else if (os_strcmp(buf, "afc_location_type") == 0) { + conf->afc.location.type = atoi(pos); + if (conf->afc.location.type != ELLIPSE && + conf->afc.location.type != LINEAR_POLYGON && + conf->afc.location.type != RADIAL_POLYGON) + return 1; + } else if (os_strcmp(buf, "afc_linear_polygon") == 0) { + if (hostapd_afc_parse_position_data( + &conf->afc.location.linear_polygon_data, + &conf->afc.location.n_linear_polygon_data, + pos)) + return 1; + } else if (os_strcmp(buf, "afc_radial_polygon") == 0) { + if (hostapd_afc_parse_position_data( + (struct afc_linear_polygon **) + &conf->afc.location.radial_polygon_data, + &conf->afc.location.n_radial_polygon_data, + pos)) + return 1; + } else if (os_strcmp(buf, "afc_major_axis") == 0) { + conf->afc.location.major_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_minor_axis") == 0) { + conf->afc.location.minor_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_orientation") == 0) { + conf->afc.location.orientation = atoi(pos); + } else if (os_strcmp(buf, "afc_height") == 0) { + char *end; + + conf->afc.location.height = strtod(pos, &end); + if (*end) + return 1; + } else if (os_strcmp(buf, "afc_height_type") == 0) { + conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.location.height_type) + return 1; + + os_strlcpy(conf->afc.location.height_type, pos, + os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) { + conf->afc.location.vertical_tolerance = atoi(pos); + } else if (os_strcmp(buf, "afc_min_power") == 0) { + conf->afc.min_power = atoi(pos); + } else if (os_strcmp(buf, "afc_freq_range") == 0) { + if (hostapd_afc_parse_freq_range(conf, pos)) + return 1; + } else if (os_strcmp(buf, "afc_op_class") == 0) { + if (hostapd_afc_parse_op_class(conf, pos)) + return 1; +#endif /* CONFIG_AFC */ } else if (os_strcmp(buf, "mbssid") == 0) { int mbssid = atoi(pos); if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) { diff --git a/hostapd/defconfig b/hostapd/defconfig index 2f5f3f6d0..ce2cdc977 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -422,3 +422,6 @@ CONFIG_DPP2=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# Enable Automated Frequency Coordination for 6GHz outdoor +#CONFIG_AFC=y diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 13576499a..48ae3b033 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0 # Valid range: 0..20 TUs; default is 0 (disabled) #unsol_bcast_probe_resp_interval=0 +##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands ####### + +# AFC daemon connection socket +#afcd_sock=/var/run/afcd.sock + +# AFC request identification parameters +#afc_request_version=1.1 +#afc_request_id=11235813 +#afc_serial_number=abcdefg +#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000 +# +# AFC location type: +# 0 = ellipse +# 1 = linear polygon +# 2 = radial polygon +#afc_location_type=0 +# +# AFC ellipse or linear polygon coordinations +#afc_linear_polygon=-122.984157:37.425056 +# +# AFC radial polygon coordinations +#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33 +# +# AFC ellipse major/minor axis and orientation +#afc_major_axis=100 +#afc_minor_axis=50 +#afc_orientation=70 +# +# AFC device elevation parameters +#afc_height=3.0 +#afc_height_type=AGL +#afc_vertical_tolerance=7 +# +# AFC minimum desired TX power (dbm) +#afc_min_power=24 +# +# AFC request frequency ranges +#afc_freq_range=5925:6425,6525:6875 +# +# AFC request operation classes +#afc_op_class=131,132,133,134,136 + ##### IEEE 802.11be related configuration ##################################### #ieee80211be: Whether IEEE 802.11be (EHT) is enabled diff --git a/src/ap/afc.c b/src/ap/afc.c new file mode 100644 index 000000000..34398c28a --- /dev/null +++ b/src/ap/afc.c @@ -0,0 +1,850 @@ +/* + * Automated Frequency Coordination + * 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" +#include "utils/eloop.h" +#include "hostapd.h" +#include "acs.h" + +#define HOSTAPD_AFC_RETRY_TIMEOUT 180 +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ +#define HOSTAPD_AFC_BUFSIZE 4096 + +struct afc_curl_ctx { + int timeout; + char *buf; + size_t buf_len; +}; + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx); + + +static struct json_object * +hostapd_afc_build_location_request(struct hostapd_iface *iface) +{ + struct json_object *location_obj, *center_obj, *ellipse_obj; + struct json_object *elevation_obj, *str_obj; + struct hostapd_config *iconf = iface->conf; + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); + + location_obj = json_object_new_object(); + if (!location_obj) + return NULL; + + if (iconf->afc.location.type != LINEAR_POLYGON) { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[0]; + + ellipse_obj = json_object_new_object(); + if (!ellipse_obj) + goto error; + + center_obj = json_object_new_object(); + if (!center_obj) + goto error; + + json_object_object_add(ellipse_obj, "center", center_obj); + + str_obj = json_object_new_double(lp->longitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "longitude", str_obj); + str_obj = json_object_new_double(lp->latitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "latitude", str_obj); + + } + + switch (iconf->afc.location.type) { + case LINEAR_POLYGON: { + struct json_object *outer_boundary_obj; + int i; + + outer_boundary_obj = json_object_new_object(); + if (!outer_boundary_obj) + goto error; + + json_object_object_add(location_obj, "linearPolygon", + outer_boundary_obj); + ellipse_obj = json_object_new_array(); + if (!ellipse_obj) + goto error; + + json_object_object_add(outer_boundary_obj, "outerBoundary", + ellipse_obj); + for (i = 0; + i < iconf->afc.location.n_linear_polygon_data; i++) { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[i]; + + center_obj = json_object_new_object(); + if (!center_obj) + goto error; + + json_object_array_add(ellipse_obj, center_obj); + str_obj = json_object_new_double(lp->longitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "longitude", + str_obj); + str_obj = json_object_new_double(lp->latitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "latitude", + str_obj); + } + break; + } + case RADIAL_POLYGON: { + struct json_object *outer_boundary_obj; + int i; + + json_object_object_add(location_obj, "radialPolygon", + ellipse_obj); + + outer_boundary_obj = json_object_new_array(); + if (!outer_boundary_obj) + goto error; + + json_object_object_add(ellipse_obj, "outerBoundary", + outer_boundary_obj); + for (i = 0; + i < iconf->afc.location.n_radial_polygon_data; i++) { + struct afc_radial_polygon *rp = + &iconf->afc.location.radial_polygon_data[i]; + struct json_object *angle_obj; + + angle_obj = json_object_new_object(); + if (!angle_obj) + goto error; + + json_object_array_add(outer_boundary_obj, angle_obj); + + str_obj = json_object_new_double(rp->angle); + if (!str_obj) + goto error; + + json_object_object_add(angle_obj, "angle", str_obj); + str_obj = json_object_new_double(rp->length); + if (!str_obj) + goto error; + + json_object_object_add(angle_obj, "length", str_obj); + } + break; + } + case ELLIPSE: + default: + json_object_object_add(location_obj, "ellipse", ellipse_obj); + + str_obj = json_object_new_int(iconf->afc.location.major_axis); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "majorAxis", str_obj); + str_obj = json_object_new_int(iconf->afc.location.minor_axis); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "minorAxis", str_obj); + str_obj = json_object_new_int(iconf->afc.location.orientation); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "orientation", str_obj); + break; + } + + elevation_obj = json_object_new_object(); + if (!elevation_obj) + goto error; + + json_object_object_add(location_obj, "elevation", + elevation_obj); + str_obj = json_object_new_double(iconf->afc.location.height); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "height", str_obj); + str_obj = json_object_new_string(iconf->afc.location.height_type); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "heightType", str_obj); + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "verticalUncertainty", + str_obj); + str_obj = json_object_new_int(is_ap_indoor); + if (!str_obj) + goto error; + + json_object_object_add(location_obj, "indoorDeployment", str_obj); + + return location_obj; + +error: + json_object_put(location_obj); + return NULL; +} + + +static void +hostapd_afc_get_opclass_chan_list(struct hostapd_iface *iface, u8 op_class, + u8 *chan_list, u16 *n_chan_list) +{ + struct hostapd_hw_modes *mode = iface->current_mode; + int i, count = 0; + + memset(chan_list, 0, mode->num_channels * sizeof(*chan_list)); + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if (!is_6ghz_freq(chan->freq)) + continue; + + if (ieee80211_chan_to_freq(iface->conf->country, op_class, + chan->chan) < 0) + continue; + + chan_list[count++] = chan->chan; + } + *n_chan_list = count; +} + + +static struct json_object * +hostapd_afc_build_request(struct hostapd_iface *iface) +{ + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj; + struct json_object *s2_obj, *str_obj, *location_obj; + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_config *iconf = iface->conf; + u8 *chan_list = NULL; + int i; + + l1_obj = json_object_new_object(); + if (!l1_obj) + return NULL; + + if (iconf->afc.request.version) { + str_obj = json_object_new_string(iconf->afc.request.version); + if (!str_obj) + goto error; + + json_object_object_add(l1_obj, "version", str_obj); + } + + la1_obj = json_object_new_array(); + if (!la1_obj) + goto error; + + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests", + la1_obj); + l2_obj = json_object_new_object(); + if (!l2_obj) + goto error; + + json_object_array_add(la1_obj, l2_obj); + if (iconf->afc.request.id) { + str_obj = json_object_new_string(iconf->afc.request.id); + if (!str_obj) + goto error; + + json_object_object_add(l2_obj, "requestId", str_obj); + } + + s2_obj = json_object_new_object(); + if (!s2_obj) + goto error; + + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj); + if (iconf->afc.request.sn) { + str_obj = json_object_new_string(iconf->afc.request.sn); + if (!str_obj) + goto error; + + json_object_object_add(s2_obj, "serialNumber", str_obj); + } + + la2_obj = json_object_new_array(); + if (!la2_obj) + goto error; + + json_object_object_add(s2_obj, "certificationId", la2_obj); + for (i = 0; i < iconf->afc.n_cert_ids; i++) { + struct json_object *obj; + + obj = json_object_new_object(); + if (!obj) + goto error; + + json_object_array_add(la2_obj, obj); + str_obj = + json_object_new_string(iconf->afc.cert_ids[i].rulset); + if (!str_obj) + goto error; + + json_object_object_add(obj, "rulesetId", str_obj); + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id); + if (!str_obj) + goto error; + + json_object_object_add(obj, "id", str_obj); + } + + location_obj = hostapd_afc_build_location_request(iface); + if (!location_obj) + goto error; + + json_object_object_add(l2_obj, "location", location_obj); + str_obj = json_object_new_int(iconf->afc.min_power); + if (!str_obj) + goto error; + + json_object_object_add(l2_obj, "minDesiredPower", str_obj); + + if (iconf->afc.n_freq_range) { + struct json_object *freq_obj; + + freq_obj = json_object_new_array(); + if (!freq_obj) + goto error; + + json_object_object_add(l2_obj, "inquiredFrequencyRange", + freq_obj); + for (i = 0; i < iconf->afc.n_freq_range; i++) { + struct afc_freq_range *fr = &iconf->afc.freq_range[i]; + struct json_object *obj; + + obj = json_object_new_object(); + if (!obj) + goto error; + + json_object_array_add(freq_obj, obj); + str_obj = json_object_new_int(fr->low_freq); + if (!str_obj) + goto error; + + json_object_object_add(obj, "lowFrequency", str_obj); + str_obj = json_object_new_int(fr->high_freq); + if (!str_obj) + goto error; + + json_object_object_add(obj, "highFrequency", str_obj); + } + } + + if (iconf->afc.n_op_class) { + struct json_object *op_class_list_obj; + + chan_list = os_malloc(mode->num_channels * sizeof(*chan_list)); + if (!chan_list) + goto error; + + op_class_list_obj = json_object_new_array(); + if (!op_class_list_obj) + goto error; + + json_object_object_add(l2_obj, "inquiredChannels", + op_class_list_obj); + for (i = 0; i < iconf->afc.n_op_class; i++) { + struct json_object *op_class_obj, *chan_list_obj; + u16 n_chan_list = 0; + int j; + + hostapd_afc_get_opclass_chan_list( + iface, iconf->afc.op_class[i], + chan_list, &n_chan_list); + if (!n_chan_list) + continue; + + op_class_obj = json_object_new_object(); + if (!op_class_obj) + goto error; + + json_object_array_add(op_class_list_obj, op_class_obj); + str_obj = json_object_new_int(iconf->afc.op_class[i]); + if (!str_obj) + goto error; + + json_object_object_add(op_class_obj, + "globalOperatingClass", + str_obj); + chan_list_obj = json_object_new_array(); + if (!chan_list_obj) + goto error; + + json_object_object_add(op_class_obj, "channelCfi", + chan_list_obj); + for (j = 0; j < n_chan_list; j++) { + str_obj = json_object_new_int(chan_list[j]); + if (!str_obj) + goto error; + + json_object_array_add(chan_list_obj, str_obj); + } + } + free(chan_list); + } + + wpa_printf(MSG_DEBUG, "Pending AFC request: %s", + json_object_get_string(l1_obj)); + + return l1_obj; + +error: + free(chan_list); + json_object_put(l1_obj); + + return NULL; +} + + +static int +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface, + struct json_object *reply_elem_obj) +{ + struct hostapd_hw_modes *mode = iface->current_mode; + struct afc_freq_range_elem *f = NULL; + struct json_object *obj; + int i, count = 0; + + if (!json_object_object_get_ex(reply_elem_obj, + "availableFrequencyInfo", &obj)) + return 0; + + for (i = 0; i < json_object_array_length(obj); i++) { + struct json_object *range_elem_obj, *freq_range_obj; + struct json_object *high_freq_obj, *low_freq_obj; + struct json_object *max_psd_obj; + + range_elem_obj = json_object_array_get_idx(obj, i); + if (!json_object_object_get_ex(range_elem_obj, + "frequencyRange", + &freq_range_obj)) + continue; + + if (!json_object_object_get_ex(freq_range_obj, + "lowFrequency", + &low_freq_obj)) + continue; + + if (!json_object_object_get_ex(freq_range_obj, + "highFrequency", + &high_freq_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "maxPsd", + &max_psd_obj) && + !json_object_object_get_ex(range_elem_obj, "maxPSD", + &max_psd_obj)) + continue; + + f = os_realloc_array(f, count + 1, sizeof(*f)); + if (!f) + return -ENOMEM; + + f[count].low_freq = json_object_get_int(low_freq_obj); + f[count].high_freq = json_object_get_int(high_freq_obj); + f[count++].max_psd = json_object_get_int(max_psd_obj); + } + os_free(iface->afc.freq_range); + iface->afc.freq_range = f; + iface->afc.num_freq_range = count; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + int j; + + if (!is_6ghz_freq(chan->freq)) + continue; + + for (j = 0; j < count; j++) { + if (chan->freq >= f[j].low_freq && + chan->freq <= f[j].high_freq) + break; + } + + if (j == count) + chan->flag |= HOSTAPD_CHAN_DISABLED; + } + + return 0; +} + + +static int +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface, + struct json_object *reply_elem_obj) +{ + struct json_object *obj; + int i; + + if (!json_object_object_get_ex(reply_elem_obj, + "availableChannelInfo", &obj)) + return 0; + + for (i = 0; i < json_object_array_length(obj); i++) { + struct hostapd_hw_modes *mode = iface->current_mode; + struct json_object *range_elem_obj, *op_class_obj; + struct json_object *chan_cfi_obj, *max_eirp_obj; + struct afc_chan_info_elem *c = NULL; + int j, ch, op_class, count = 0; + + range_elem_obj = json_object_array_get_idx(obj, i); + if (!json_object_object_get_ex(range_elem_obj, + "globalOperatingClass", + &op_class_obj)) + continue; + + op_class = json_object_get_int(op_class_obj); + if (op_class != iface->conf->op_class) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "maxEirp", + &max_eirp_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "channelCfi", + &chan_cfi_obj)) + continue; + + for (ch = 0; + ch < json_object_array_length(chan_cfi_obj); ch++) { + struct json_object *pwr_obj; + struct json_object *ch_obj; + int channel, power; + + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch); + if (!ch_obj) + continue; + + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch); + if (!pwr_obj) + continue; + + channel = json_object_get_int(ch_obj); + power = json_object_get_int(pwr_obj); + + c = os_realloc_array(c, count + 1, sizeof(*c)); + if (!c) + return -ENOMEM; + + c[count].chan = channel; + c[count++].power = power; + } + os_free(iface->afc.chan_info_list); + iface->afc.chan_info_list = c; + iface->afc.num_chan_info = count; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan = &mode->channels[j]; + int n; + + if (!is_6ghz_freq(chan->freq)) + continue; + + for (n = 0; n < count; n++) { + if (chan->chan == c[n].chan) + break; + } + + if (n == count) + chan->flag |= HOSTAPD_CHAN_DISABLED; + } + } + + return 0; +} + + +static int hostad_afc_get_timeout(struct json_object *obj) +{ + time_t t, now; + struct tm tm; + + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec) <= 0) + return HOSTAPD_AFC_TIMEOUT; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + tm.tm_isdst = -1; + t = mktime(&tm); + time(&now); + + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100; +} + + +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply) +{ + int i, request_timeout = HOSTAPD_AFC_RETRY_TIMEOUT, ret = -EINVAL; + struct json_object *payload_obj, *reply_obj, *version_obj; + struct hostapd_config *iconf = iface->conf; + + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply); + payload_obj = json_tokener_parse(reply); + if (!payload_obj) + return -EINVAL; + + if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) + return -EINVAL; + + if (iconf->afc.request.version && + os_strcmp(iconf->afc.request.version, + json_object_get_string(version_obj))) + return -EINVAL; + + if (!json_object_object_get_ex(payload_obj, + "availableSpectrumInquiryResponses", + &reply_obj)) + return -EINVAL; + + for (i = 0; i < json_object_array_length(reply_obj); i++) { + struct json_object *reply_elem_obj, *obj, *status_obj; + int j, status = -EINVAL; + + reply_elem_obj = json_object_array_get_idx(reply_obj, i); + if (!reply_elem_obj) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "requestId", + &obj)) + continue; + + if (iconf->afc.request.id && + os_strcmp(iconf->afc.request.id, + json_object_get_string(obj))) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId", + &obj)) + continue; + + for (j = 0; j < iconf->afc.n_cert_ids; j++) { + if (!os_strcmp(iconf->afc.cert_ids[j].rulset, + json_object_get_string(obj))) + break; + } + + if (j == iconf->afc.n_cert_ids) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "response", + &obj)) + continue; + + if (json_object_object_get_ex(obj, "shortDescription", + &status_obj)) + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s", + i, json_object_get_string(status_obj)); + + if (json_object_object_get_ex(obj, "responseCode", + &status_obj)) + status = json_object_get_int(status_obj); + + if (status < 0) + continue; + + if (hostad_afc_parse_available_freq_info(iface, + reply_elem_obj) || + hostad_afc_parse_available_chan_info(iface, + reply_elem_obj)) + continue; + + if (json_object_object_get_ex(reply_elem_obj, + "availabilityExpireTime", + &obj)) { + int timeout = hostad_afc_get_timeout(obj); + + if (timeout < request_timeout) + request_timeout = timeout; + } + + ret = status; + } + + return ret < 0 ? ret : request_timeout; +} + + +static int hostapd_afc_send_receive(struct hostapd_iface *iface, + int *request_timeout) +{ + struct hostapd_config *iconf = iface->conf; + json_object *request_obj = NULL; + struct timeval sock_timeout = { + .tv_sec = 2, + }; + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + char buf[HOSTAPD_AFC_BUFSIZE] = {}; + const char *request; + int sockfd, ret; + fd_set read_set; + + *request_timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + if (iface->afc.data_valid) + return 0; + + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s", + iconf->afc.socket); + return -EINVAL; + } + + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path)); + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "Failed creating AFC socket"); + return sockfd; + } + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "Failed connecting AFC socket"); + ret = -EIO; + goto close_sock; + } + + request_obj = hostapd_afc_build_request(iface); + if (!request_obj) { + ret = -ENOMEM; + goto close_sock; + } + + request = json_object_to_json_string(request_obj); + if (send(sockfd, request, strlen(request), 0) < 0) { + wpa_printf(MSG_ERROR, "Failed sending AFC request"); + ret = -EIO; + goto close_sock; + } + + FD_ZERO(&read_set); + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) { + wpa_printf(MSG_ERROR, "Select failed on AFC socket"); + ret = -errno; + goto close_sock; + } + + if (!FD_ISSET(sockfd, &read_set)) { + ret = -EIO; + goto close_sock; + } + + ret = recv(sockfd, buf, sizeof(buf), 0); + if (ret <= 0) + goto close_sock; + + ret = hostapd_afc_parse_reply(iface, buf); + if (ret > 0) { + iface->afc.data_valid = true; + *request_timeout = ret; + ret = 0; + } + +close_sock: + json_object_put(request_obj); + close(sockfd); + + return ret; +} + + +int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + int ret, request_timeout; + + if (iface->afc.completed) + return 1; + + /* AFC is required just for standard power AP */ + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return 1; + + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq)) + return 1; + + ret = hostapd_afc_send_receive(iface, &request_timeout); + if (ret < 0) { + /* + * If the connection to the AFCD failed, resched for a + * future attempt. + */ + if (ret == -EIO) + ret = 0; + goto resched; + } + + /* Trigger a ACS freq scan */ + iface->freq = 0; + iconf->channel = 0; + iface->afc.completed = true; + + if (acs_init(iface) != HOSTAPD_CHAN_ACS) { + wpa_printf(MSG_ERROR, "Could not start ACS"); + ret = -EINVAL; + } + +resched: + eloop_register_timeout(request_timeout, 0, hostapd_afc_timeout_handler, + iface, NULL); + + return ret; +} + + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + int request_timeout; + + iface->afc.data_valid = false; + if (!hostapd_afc_send_receive(iface, &request_timeout)) { + struct hostapd_hw_modes *mode = iface->current_mode; + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if (chan->freq != iface->freq || + (chan->flag & HOSTAPD_CHAN_DISABLED)) + continue; + + eloop_register_timeout(request_timeout, 0, + hostapd_afc_timeout_handler, + iface, NULL); + return; + } + } + + /* Toggle interface to trigger new AFC connection */ + iface->afc.completed = false; + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); +} diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index f760e340b..abefb83f0 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1029,6 +1029,22 @@ void hostapd_config_free(struct hostapd_config *conf) #endif /* CONFIG_ACS */ wpabuf_free(conf->lci); wpabuf_free(conf->civic); +#ifdef CONFIG_AFC + os_free(conf->afc.socket); + os_free(conf->afc.request.version); + os_free(conf->afc.request.id); + os_free(conf->afc.request.sn); + for (i = 0; i < conf->afc.n_cert_ids; i++) { + os_free(conf->afc.cert_ids[i].rulset); + os_free(conf->afc.cert_ids[i].id); + } + os_free(conf->afc.cert_ids); + os_free(conf->afc.location.height_type); + os_free(conf->afc.location.linear_polygon_data); + os_free(conf->afc.location.radial_polygon_data); + os_free(conf->afc.freq_range); + os_free(conf->afc.op_class); +#endif /* CONFIG_AFC */ os_free(conf); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index de02ddafd..fb04cbb73 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1217,6 +1217,53 @@ struct hostapd_config { MBSSID_ENABLED = 1, ENHANCED_MBSSID_ENABLED = 2, } mbssid; + +#ifdef CONFIG_AFC + struct { + char *socket; + struct { + char *version; + char *id; + char *sn; + } request; + unsigned int n_cert_ids; + struct cert_id { + char *rulset; + char *id; + } *cert_ids; + struct { + enum afc_location_type { + ELLIPSE, + LINEAR_POLYGON, + RADIAL_POLYGON, + } type; + unsigned int n_linear_polygon_data; + struct afc_linear_polygon { + double longitude; + double latitude; + } *linear_polygon_data; + unsigned int n_radial_polygon_data; + struct afc_radial_polygon { + double length; + double angle; + } *radial_polygon_data; + int major_axis; + int minor_axis; + int orientation; + double height; + char *height_type; + int vertical_tolerance; + } location; + unsigned int n_freq_range; + struct afc_freq_range { + int low_freq; + int high_freq; + } *freq_range; + unsigned int n_op_class; + unsigned int *op_class; + int min_power; + } afc; +#endif /* CONFIG_AFC */ }; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index ddbcabc49..d0ea86811 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -2415,6 +2415,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } #endif /* CONFIG_MESH */ +#ifdef CONFIG_AFC + /* check AFC for 6GHz channels. */ + res = hostapd_afc_handle_request(iface); + if (res <= 0) { + if (res < 0) + goto fail; + return res; + } +#endif /* CONFIG_AFC */ + if (!delay_apply_cfg && hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, @@ -2847,6 +2857,10 @@ void hostapd_interface_free(struct hostapd_iface *iface) os_free(iface->bss[j]); } hostapd_cleanup_iface(iface); +#ifdef CONFIG_AFC + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); +#endif } @@ -4423,3 +4437,42 @@ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd, return NULL; } #endif /* CONFIG_IEEE80211BE */ + + +void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ +#ifdef CONFIG_AFC + struct hostapd_hw_modes *mode; + int i; + + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A) + return; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) + return; + + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A]; + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + int j; + + if (!is_6ghz_freq(iface->freq)) + continue; + + for (j = 0; j < iface->afc.num_freq_range; j++) { + if (chan->freq >= iface->afc.freq_range[j].low_freq || + chan->freq <= iface->afc.freq_range[j].high_freq) + break; + } + if (j && j == iface->afc.num_freq_range) + chan->flag |= HOSTAPD_CHAN_DISABLED; + + for (j = 0; j < iface->afc.num_chan_info; j++) { + if (chan->chan == iface->afc.chan_info_list[j].chan) + break; + } + if (j && j == iface->afc.num_chan_info) + chan->flag |= HOSTAPD_CHAN_DISABLED; + } +#endif /* CONFIG_AFC */ +} diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 333955486..4b1feb1a8 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -674,9 +674,38 @@ struct hostapd_iface { /* Configured freq of interface is NO_IR */ bool is_no_ir; + +#ifdef CONFIG_AFC + struct { + bool data_valid; + bool completed; + unsigned int num_freq_range; + struct afc_freq_range_elem { + int low_freq; + int high_freq; + /** + * max eirp power spectral density received from + * the AFC coordinator for this band + */ + int max_psd; + } *freq_range; + unsigned int num_chan_info; + struct afc_chan_info_elem { + int chan; + /** + * max eirp power received from the AFC coordinator + * for this channel + */ + int power; + } *chan_info_list; + } afc; +#endif /* CONFIG_AFC */ }; /* hostapd.c */ +void hostap_afc_disable_channels(struct hostapd_iface *iface); +int hostapd_afc_handle_request(struct hostapd_iface *iface); + int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 596f2f020..62a53c269 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -116,6 +116,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) iface->hw_features = modes; iface->num_hw_features = num_modes; + hostap_afc_disable_channels(iface); + for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; int dfs_enabled = hapd->iconf->ieee80211h && From patchwork Fri Feb 23 15:14:23 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: 1903343 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=Gind9URb; 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=ru+xs/wS; 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 4ThDBb4nZLz23l1 for ; Sat, 24 Feb 2024 02:16:55 +1100 (AEDT) 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=CeaX0rZvjExGIbVlIiKpI7B/QMIygYFBD07pI7tnIVM=; b=Gind9URbKOyhCk QOH1VGGEU+++/4CRZ/Tkq9l7SCosNn+V3XdaC0EkQ72dXl/NrA+uKwCurwYp82hj4AEQ5APF8dMyU fECbiFr0YAFHvFcGj5w3kCzpx8MkJUt6Pw8/+WuPC/sxJ9Y+IYUe6Q6zXt26l04mHqN4DJH7O2A0u y3C4MyJRC7WBxJCvMRChda8LKm+0QOiaGs2MGZh8lYT7EhsLMfkPj1K8donlBBwjTwOyhWchngF3a JQTYr4ccN/FpgaJ2M+sv90LmupJx03JnfDj46qbAzX3wXPMbCVPAPvJa/ypN7wCc4V+G8Kwuifx6j Z27WnOxQx85cndeB/U4w==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rdXHQ-00000009w2D-2PNL; Fri, 23 Feb 2024 15:16:04 +0000 Received: from sin.source.kernel.org ([145.40.73.55]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rdXGJ-00000009vr1-41v4 for hostap@lists.infradead.org; Fri, 23 Feb 2024 15:15:15 +0000 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id BE466CE2C94; Fri, 23 Feb 2024 15:14:46 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id A0CFDC43390; Fri, 23 Feb 2024 15:14:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1708701286; bh=nnLzITL+JqY87dBJH8Y861oWZPONqLRQGhUzkDOAp2E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ru+xs/wSeSB4O1pCNPzm2kIVvnAINwbNFaqvVHeo9Xr+d13yXuQ3FV74MUzbEPwJ2 UNtUhHWN2tlg7hbmMHz0J9n8Tb+5BAcmIIX4hd+TWN62DL2cnswdhE4EEgE9VR9Lak Nqk+vWgKOsXhqxQQzvqJFVodV+tHd2jR66zFSZ5eKH46S1FREuTi9IZsRYIhXKyMqd QdjgxI0yRnkiKGV/9nb5UUQVRaps6McnK1H5p4g9VfFvHd8Nbskv+7e0dQxSFqIu25 HESeHRyu7+zfBKu1ZKwPJdLgfW9n5DcGYoknuuC8YNiVVGy10joBc1s58j9NJD2s8U KYPpjIJX0vwyQ== From: Lorenzo Bianconi To: hostap@lists.infradead.org Cc: j@w1.fi, lorenzo.bianconi@redhat.com, ryder.lee@mediatek.com, evelyn.tsai@mediatek.com, nbd@nbd.name Subject: [PATCH v2 3/3] hostapd: update TPE IE according to AFC Date: Fri, 23 Feb 2024 16:14:23 +0100 Message-ID: X-Mailer: git-send-email 2.43.2 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240223_071507_089895_443A9850 X-CRM114-Status: GOOD ( 17.00 ) X-Spam-Score: -2.6 (--) 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: Update Transmit Power Envelope (TPE) IE according to the reply from AFC coordinator on UNII-5 or UNII-7 6GHz bands. Signed-off-by: Lorenzo Bianconi --- src/ap/hostapd.c | 39 +++++++++++++++++++++++++++++++++++++ src/ap/hostapd.h | 2 ++ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++------ [...] Content analysis details: (-2.6 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [145.40.73.55 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -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 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 T_SCC_BODY_TEXT_LINE No description available. -0.1 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 Update Transmit Power Envelope (TPE) IE according to the reply from AFC coordinator on UNII-5 or UNII-7 6GHz bands. Signed-off-by: Lorenzo Bianconi --- src/ap/hostapd.c | 39 +++++++++++++++++++++++++++++++++++++ src/ap/hostapd.h | 2 ++ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++----------------- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index d0ea86811..1b21bf43c 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4439,6 +4439,45 @@ struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211BE */ +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ +#ifdef CONFIG_AFC + int i; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) + return -EINVAL; + + if (!iface->afc.completed) + return -EINVAL; + + if (psd) { + for (i = 0; i < iface->afc.num_freq_range; i++) { + struct afc_freq_range_elem *f; + + f = &iface->afc.freq_range[i]; + if (iface->freq >= f->low_freq && + iface->freq <= f->high_freq) { + *power = 2 * f->max_psd; + return 0; + } + } + } else { + for (i = 0; i < iface->afc.num_chan_info; i++) { + struct afc_chan_info_elem *c; + + c = &iface->afc.chan_info_list[i]; + if (c->chan == iface->conf->channel) { + *power = 2 * c->power; + return 0; + } + } + } +#endif /* CONFIG_AFC */ + return -EINVAL; +} + + void hostap_afc_disable_channels(struct hostapd_iface *iface) { #ifdef CONFIG_AFC diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 4b1feb1a8..721f8b6bb 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -703,6 +703,8 @@ struct hostapd_iface { }; /* hostapd.c */ +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power); void hostap_afc_disable_channels(struct hostapd_iface *iface); int hostapd_afc_handle_request(struct hostapd_iface *iface); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 8b8c1f095..47982d0ec 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -7064,42 +7064,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) */ if (is_6ghz_op_class(iconf->op_class)) { enum max_tx_pwr_interpretation tx_pwr_intrpn; + int err, max_eirp_psd, max_eirp_power; /* Same Maximum Transmit Power for all 20 MHz bands */ tx_pwr_count = 0; tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD; /* Default Transmit Power Envelope for Global Operating Class */ - if (hapd->iconf->reg_def_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_def_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + err = hostap_afc_get_chan_max_eirp_power(iface, true, + &max_eirp_psd); + if (err < 0) { + if (hapd->iconf->reg_def_cli_eirp_psd != -1) + max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd; + else + max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, - REG_DEFAULT_CLIENT, tx_pwr); + REG_DEFAULT_CLIENT, max_eirp_psd); /* Indoor Access Point must include an additional TPE for * subordinate devices */ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) { - /* TODO: Extract PSD limits from channel data */ - if (hapd->iconf->reg_sub_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + if (err < 0) { + /* non-AFC connection */ + if (hapd->iconf->reg_sub_cli_eirp_psd != -1) + max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd; + else + max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, REG_SUBORDINATE_CLIENT, - tx_pwr); + max_eirp_psd); } - if (iconf->reg_def_cli_eirp != -1 && - he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) - eid = hostapd_add_tpe_info( - eid, tx_pwr_count, REGULATORY_CLIENT_EIRP, - REG_DEFAULT_CLIENT, - hapd->iconf->reg_def_cli_eirp); + if (hostap_afc_get_chan_max_eirp_power(iface, false, + &max_eirp_power)) { + max_eirp_power = iconf->reg_def_cli_eirp; + if (max_eirp_power == -1 || + !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return eid; + } - return eid; + return hostapd_add_tpe_info(eid, tx_pwr_count, + REGULATORY_CLIENT_EIRP, + REG_DEFAULT_CLIENT, + max_eirp_power); } #endif /* CONFIG_IEEE80211AX */