hostapd: improve multi CoA support

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2022-12-18 15:25:27 +01:00
parent e973110de7
commit 4967fcd2be
2 changed files with 286 additions and 63 deletions

View File

@@ -705,7 +705,9 @@ hostapd_set_bss_options() {
[ -n "$wpa_strict_rekey" ] && append bss_conf "wpa_strict_rekey=$wpa_strict_rekey" "$N"
}
[ -n "$nasid" ] && append bss_conf "nas_identifier=$nasid" "$N"
set_default nasid "${macaddr//\:}"
append bss_conf "nas_identifier=$nasid" "$N"
[ -n "$acct_server" ] && {
append bss_conf "acct_server_addr=$acct_server" "$N"
append bss_conf "acct_server_port=$acct_port" "$N"
@@ -923,7 +925,6 @@ hostapd_set_bss_options() {
append bss_conf "ft_psk_generate_local=$ft_psk_generate_local" "$N"
append bss_conf "ft_over_ds=$ft_over_ds" "$N"
append bss_conf "reassociation_deadline=$reassociation_deadline" "$N"
[ -n "$nasid" ] || append bss_conf "nas_identifier=${macaddr//\:}" "$N"
if [ "$skip_kh_setup" -eq "0" ]; then
json_get_vars r0_key_lifetime r1_key_holder pmk_r1_push

View File

@@ -1,76 +1,298 @@
Index: hostapd-2021-02-20-59e9794c/src/ap/hostapd.c
===================================================================
--- hostapd-2021-02-20-59e9794c.orig/src/ap/hostapd.c
+++ hostapd-2021-02-20-59e9794c/src/ap/hostapd.c
@@ -862,7 +862,6 @@ static int hostapd_das_nas_mismatch(stru
return 0;
}
-
static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
struct radius_das_attrs *attr,
int *multi)
@@ -1050,6 +1049,24 @@ static int hostapd_das_disconnect_pmksa(
}
--- 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"
+static struct hostapd_data * ap_get_hapd(struct hostapd_data *hapd, struct radius_das_attrs *attr)
+{
+ size_t i;
+ int multi;
-struct radius_das_data {
+static struct dl_list das_ports = DL_LIST_HEAD_INIT(das_ports);
+
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ if (!hapd->iface->bss[i]->iface->bss[i]->radius_das)
+ continue;
+ if (hapd->conf->radius_das_port !=hapd->iface->bss[i]->iface->bss[i]->conf->radius_das_port)
+ continue;
+ if (hostapd_das_find_sta(hapd, attr, &multi))
+ return hapd->iface->bss[i];
+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;
+ }
+ return hapd;
+
+ 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 enum radius_das_res
hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
static int radius_das_open_socket(int port)
{
@@ -1057,6 +1074,10 @@ hostapd_das_disconnect(void *ctx, struct
struct sta_info *sta;
int multi;
+ hapd = ap_get_hapd(hapd, attr);
+ if (!hapd)
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+
if (hostapd_das_nas_mismatch(hapd, attr))
return RADIUS_DAS_NAS_MISMATCH;
@@ -1096,6 +1117,10 @@ hostapd_das_coa(void *ctx, struct radius
struct sta_info *sta;
int multi;
+ hapd = ap_get_hapd(hapd, attr);
+ if (!hapd)
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+
if (hostapd_das_nas_mismatch(hapd, attr))
return RADIUS_DAS_NAS_MISMATCH;
Index: hostapd-2021-02-20-59e9794c/src/radius/radius_das.c
===================================================================
--- hostapd-2021-02-20-59e9794c.orig/src/radius/radius_das.c
+++ hostapd-2021-02-20-59e9794c/src/radius/radius_das.c
@@ -568,10 +568,9 @@ radius_das_init(struct radius_das_conf *
das->sock = radius_das_open_socket(conf->port);
if (das->sock < 0) {
- wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS - reusing existing port "
"DAS");
- radius_das_deinit(das);
- return NULL;
+ return das;
@@ -533,6 +572,49 @@ static int radius_das_open_socket(int po
}
if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+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);
}