aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2022-12-16 13:32:48 +0100
committerFelix Fietkau <nbd@nbd.name>2022-12-27 19:10:04 +0100
commit090ad0334369cc8c0197cd6bbb66da1eba601559 (patch)
tree045c570687f9f2690c1fa50e66555ac96b84f235
parent0a83889e89b7213ea8f6eac21de50ffa3ae5a606 (diff)
downloadupstream-090ad0334369cc8c0197cd6bbb66da1eba601559.tar.gz
upstream-090ad0334369cc8c0197cd6bbb66da1eba601559.tar.bz2
upstream-090ad0334369cc8c0197cd6bbb66da1eba601559.zip
hostapd: allow sharing the incoming DAS port across multiple interfaces
Use the NAS identifier to find the right receiver context on incoming messages Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--package/network/services/hostapd/patches/761-shared_das_port.patch298
1 files changed, 298 insertions, 0 deletions
diff --git a/package/network/services/hostapd/patches/761-shared_das_port.patch b/package/network/services/hostapd/patches/761-shared_das_port.patch
new file mode 100644
index 0000000000..7516b7349e
--- /dev/null
+++ b/package/network/services/hostapd/patches/761-shared_das_port.patch
@@ -0,0 +1,298 @@
+--- a/src/radius/radius_das.h
++++ b/src/radius/radius_das.h
+@@ -44,6 +44,7 @@ struct radius_das_attrs {
+ struct radius_das_conf {
+ int port;
+ const u8 *shared_secret;
++ const u8 *nas_identifier;
+ size_t shared_secret_len;
+ const struct hostapd_ip_addr *client_addr;
+ unsigned int time_window;
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -1367,6 +1367,7 @@ static int hostapd_setup_bss(struct host
+ struct radius_das_conf das_conf;
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
++ das_conf.nas_identifier = conf->nas_identifier;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+--- a/src/radius/radius_das.c
++++ b/src/radius/radius_das.c
+@@ -12,13 +12,26 @@
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+ #include "utils/ip_addr.h"
++#include "utils/list.h"
+ #include "radius.h"
+ #include "radius_das.h"
+
+
+-struct radius_das_data {
++static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
++
++struct radius_das_port {
++ struct dl_list list;
++ struct dl_list das_data;
++
++ int port;
+ int sock;
++};
++
++struct radius_das_data {
++ struct dl_list list;
++ struct radius_das_port *port;
+ u8 *shared_secret;
++ u8 *nas_identifier;
+ size_t shared_secret_len;
+ struct hostapd_ip_addr client_addr;
+ unsigned int time_window;
+@@ -378,56 +391,17 @@ fail:
+ }
+
+
+-static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++static void
++radius_das_receive_msg(struct radius_das_data *das, struct radius_msg *msg,
++ struct sockaddr *from, socklen_t fromlen,
++ char *abuf, int from_port)
+ {
+- struct radius_das_data *das = eloop_ctx;
+- u8 buf[1500];
+- union {
+- struct sockaddr_storage ss;
+- struct sockaddr_in sin;
+-#ifdef CONFIG_IPV6
+- struct sockaddr_in6 sin6;
+-#endif /* CONFIG_IPV6 */
+- } from;
+- char abuf[50];
+- int from_port = 0;
+- socklen_t fromlen;
+- int len;
+- struct radius_msg *msg, *reply = NULL;
++ struct radius_msg *reply = NULL;
+ struct radius_hdr *hdr;
+ struct wpabuf *rbuf;
++ struct os_time now;
+ u32 val;
+ int res;
+- struct os_time now;
+-
+- fromlen = sizeof(from);
+- len = recvfrom(sock, buf, sizeof(buf), 0,
+- (struct sockaddr *) &from.ss, &fromlen);
+- if (len < 0) {
+- wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+- return;
+- }
+-
+- os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+- from_port = ntohs(from.sin.sin_port);
+-
+- wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+- len, abuf, from_port);
+- if (das->client_addr.u.v4.s_addr &&
+- das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+- wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+- return;
+- }
+-
+- msg = radius_msg_parse(buf, len);
+- if (msg == NULL) {
+- wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+- "from %s:%d failed", abuf, from_port);
+- return;
+- }
+-
+- if (wpa_debug_level <= MSG_MSGDUMP)
+- radius_msg_dump(msg);
+
+ if (radius_msg_verify_das_req(msg, das->shared_secret,
+ das->shared_secret_len,
+@@ -494,9 +468,8 @@ static void radius_das_receive(int sock,
+ radius_msg_dump(reply);
+
+ rbuf = radius_msg_get_buf(reply);
+- res = sendto(das->sock, wpabuf_head(rbuf),
+- wpabuf_len(rbuf), 0,
+- (struct sockaddr *) &from.ss, fromlen);
++ res = sendto(das->port->sock, wpabuf_head(rbuf),
++ wpabuf_len(rbuf), 0, from, fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+ abuf, from_port, strerror(errno));
+@@ -508,6 +481,72 @@ fail:
+ radius_msg_free(reply);
+ }
+
++static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++ struct radius_das_port *p = eloop_ctx;
++ struct radius_das_data *das;
++ u8 buf[1500];
++ union {
++ struct sockaddr_storage ss;
++ struct sockaddr_in sin;
++#ifdef CONFIG_IPV6
++ struct sockaddr_in6 sin6;
++#endif /* CONFIG_IPV6 */
++ } from;
++ struct radius_msg *msg;
++ size_t nasid_len = 0;
++ u8 *nasid_buf = NULL;
++ char abuf[50];
++ int from_port = 0;
++ socklen_t fromlen;
++ int found = 0;
++ int len;
++
++ fromlen = sizeof(from);
++ len = recvfrom(sock, buf, sizeof(buf), 0,
++ (struct sockaddr *) &from.ss, &fromlen);
++ if (len < 0) {
++ wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
++ return;
++ }
++
++ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
++ from_port = ntohs(from.sin.sin_port);
++
++ msg = radius_msg_parse(buf, len);
++ if (msg == NULL) {
++ wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
++ "from %s:%d failed", abuf, from_port);
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
++ len, abuf, from_port);
++
++ if (wpa_debug_level <= MSG_MSGDUMP)
++ radius_msg_dump(msg);
++
++ radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
++ &nasid_buf, &nasid_len, NULL);
++ dl_list_for_each(das, &p->das_data, struct radius_das_data, list) {
++ if (das->client_addr.u.v4.s_addr &&
++ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr)
++ continue;
++
++ if (das->nas_identifier && nasid_buf &&
++ (nasid_len != os_strlen(das->nas_identifier) ||
++ os_memcmp(das->nas_identifier, nasid_buf, nasid_len) != 0))
++ continue;
++
++ found = 1;
++ radius_das_receive_msg(das, msg, (struct sockaddr *)&from.ss,
++ fromlen, abuf, from_port);
++ }
++
++ if (!found)
++ wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
++}
++
+
+ static int radius_das_open_socket(int port)
+ {
+@@ -533,6 +572,49 @@ static int radius_das_open_socket(int po
+ }
+
+
++static struct radius_das_port *
++radius_das_open_port(int port)
++{
++ struct radius_das_port *p;
++
++ dl_list_for_each(p, &das_ports, struct radius_das_port, list) {
++ if (p->port == port)
++ return p;
++ }
++
++ p = os_zalloc(sizeof(*p));
++ if (p == NULL)
++ return NULL;
++
++ dl_list_init(&p->das_data);
++ p->port = port;
++ p->sock = radius_das_open_socket(port);
++ if (p->sock < 0)
++ goto free_port;
++
++ if (eloop_register_read_sock(p->sock, radius_das_receive, p, NULL))
++ goto close_port;
++
++ dl_list_add(&das_ports, &p->list);
++
++ return p;
++
++close_port:
++ close(p->sock);
++free_port:
++ os_free(p);
++
++ return NULL;
++}
++
++static void radius_das_close_port(struct radius_das_port *p)
++{
++ dl_list_del(&p->list);
++ eloop_unregister_read_sock(p->sock);
++ close(p->sock);
++ free(p);
++}
++
+ struct radius_das_data *
+ radius_das_init(struct radius_das_conf *conf)
+ {
+@@ -553,6 +635,8 @@ radius_das_init(struct radius_das_conf *
+ das->ctx = conf->ctx;
+ das->disconnect = conf->disconnect;
+ das->coa = conf->coa;
++ if (conf->nas_identifier)
++ das->nas_identifier = os_strdup(conf->nas_identifier);
+
+ os_memcpy(&das->client_addr, conf->client_addr,
+ sizeof(das->client_addr));
+@@ -565,19 +649,15 @@ radius_das_init(struct radius_das_conf *
+ }
+ das->shared_secret_len = conf->shared_secret_len;
+
+- das->sock = radius_das_open_socket(conf->port);
+- if (das->sock < 0) {
++ das->port = radius_das_open_port(conf->port);
++ if (!das->port) {
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ "DAS");
+ radius_das_deinit(das);
+ return NULL;
+ }
+
+- if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+- {
+- radius_das_deinit(das);
+- return NULL;
+- }
++ dl_list_add(&das->port->das_data, &das->list);
+
+ return das;
+ }
+@@ -588,11 +668,14 @@ void radius_das_deinit(struct radius_das
+ if (das == NULL)
+ return;
+
+- if (das->sock >= 0) {
+- eloop_unregister_read_sock(das->sock);
+- close(das->sock);
++ if (das->port) {
++ dl_list_del(&das->list);
++
++ if (dl_list_empty(&das->port->das_data))
++ radius_das_close_port(das->port);
+ }
+
++ os_free(das->nas_identifier);
+ os_free(das->shared_secret);
+ os_free(das);
+ }