306 lines
7.7 KiB
Diff
306 lines
7.7 KiB
Diff
From: Felix Fietkau <nbd@nbd.name>
|
|
Date: Fri, 16 Dec 2022 13:32:48 +0100
|
|
Subject: [PATCH] hostapd: allow sharing the incoming DAS port across multiple
|
|
interfaces
|
|
|
|
Use the NAS identifier to find the right receiver context on incoming messages
|
|
|
|
--- a/src/ap/hostapd.c
|
|
+++ b/src/ap/hostapd.c
|
|
@@ -1510,6 +1510,7 @@ int hostapd_setup_bss(struct hostapd_dat
|
|
|
|
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);
|
|
}
|
|
--- 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;
|