From 4967fcd2be5dd983e57060ab4575534d6c1c59c6 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 18 Dec 2022 15:25:27 +0100 Subject: [PATCH] hostapd: improve multi CoA support Signed-off-by: John Crispin --- feeds/wifi-ax/hostapd/files/hostapd.sh | 5 +- .../hostapd/patches/900-coa_multi.patch | 344 ++++++++++++++---- 2 files changed, 286 insertions(+), 63 deletions(-) diff --git a/feeds/wifi-ax/hostapd/files/hostapd.sh b/feeds/wifi-ax/hostapd/files/hostapd.sh index 31afd77df..267836932 100644 --- a/feeds/wifi-ax/hostapd/files/hostapd.sh +++ b/feeds/wifi-ax/hostapd/files/hostapd.sh @@ -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 diff --git a/feeds/wifi-ax/hostapd/patches/900-coa_multi.patch b/feeds/wifi-ax/hostapd/patches/900-coa_multi.patch index 2b0131de0..7516b7349 100644 --- a/feeds/wifi-ax/hostapd/patches/900-coa_multi.patch +++ b/feeds/wifi-ax/hostapd/patches/900-coa_multi.patch @@ -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; +@@ -533,6 +572,49 @@ static int radius_das_open_socket(int po + } -+ hapd = ap_get_hapd(hapd, attr); -+ if (!hapd) -+ return RADIUS_DAS_SESSION_NOT_FOUND; + ++static struct radius_das_port * ++radius_das_open_port(int port) ++{ ++ struct radius_das_port *p; + - 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; ++ dl_list_for_each(p, &das_ports, struct radius_das_port, list) { ++ if (p->port == port) ++ return p; ++ } + - if (hostapd_das_nas_mismatch(hapd, attr)) - return RADIUS_DAS_NAS_MISMATCH; ++ 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); -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 * + 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) { -- 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->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; -+ return das; + radius_das_deinit(das); + return NULL; } - if (eloop_register_read_sock(das->sock, radius_das_receive, das, 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); + }