--- 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 @@ -1423,6 +1423,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); }