| --- 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); |
| } |