From 87f33c26b9c42fe8ff81499c7633a91c30571c2c Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 25 Feb 2024 17:49:40 +0200 Subject: [PATCH] RADIUS: Simplify IPv4/IPv6 socket handling in client There is only one connection in use in parallel to a RADIUS authentication server (and similarly to a RADIUS accounting server). As such, there is not really any need to maintain separate open IPv4 and IPv6 sockets. Instead, open the socket for the appropriate IP version only when actually connecting to a specific server. Signed-off-by: Jouni Malinen --- src/radius/radius_client.c | 262 +++++++++---------------------------- 1 file changed, 65 insertions(+), 197 deletions(-) diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 17d054657..68e6d23d3 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -168,26 +168,6 @@ struct radius_client_data { */ struct hostapd_radius_servers *conf; - /** - * auth_serv_sock - IPv4 socket for RADIUS authentication messages - */ - int auth_serv_sock; - - /** - * acct_serv_sock - IPv4 socket for RADIUS accounting messages - */ - int acct_serv_sock; - - /** - * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages - */ - int auth_serv_sock6; - - /** - * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages - */ - int acct_serv_sock6; - /** * auth_sock - Currently used socket for RADIUS authentication server */ @@ -715,6 +695,42 @@ static void radius_client_list_add(struct radius_client_data *radius, } +static int radius_client_disable_pmtu_discovery(int s) +{ + int r = -1; +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* Turn off Path MTU discovery on IPv4/UDP sockets. */ + int action = IP_PMTUDISC_DONT; + r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, + sizeof(action)); + if (r == -1) + wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", + strerror(errno)); +#endif + return r; +} + + +static void radius_close_auth_socket(struct radius_client_data *radius) +{ + if (radius->auth_sock >= 0) { + eloop_unregister_read_sock(radius->auth_sock); + close(radius->auth_sock); + radius->auth_sock = -1; + } +} + + +static void radius_close_acct_socket(struct radius_client_data *radius) +{ + if (radius->acct_sock >= 0) { + eloop_unregister_read_sock(radius->acct_sock); + close(radius->acct_sock); + radius->acct_sock = -1; + } +} + + /** * radius_client_send - Send a RADIUS request * @radius: RADIUS client context from radius_client_init() @@ -1076,14 +1092,9 @@ radius_change_server(struct radius_client_data *radius, struct sockaddr *addr, *cl_addr; socklen_t addrlen, claddrlen; char abuf[50]; - int sock = auth ? radius->auth_serv_sock : radius->acct_serv_sock; - int sock6 = auth ? radius->auth_serv_sock6 : radius->acct_serv_sock6; int sel_sock; struct radius_msg_list *entry; struct hostapd_radius_servers *conf = radius->conf; - struct sockaddr_in disconnect_addr = { - .sin_family = AF_UNSPEC, - }; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1142,7 +1153,9 @@ radius_change_server(struct radius_client_data *radius, serv.sin_port = htons(nserv->port); addr = (struct sockaddr *) &serv; addrlen = sizeof(serv); - sel_sock = sock; + sel_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sel_sock >= 0) + radius_client_disable_pmtu_discovery(sel_sock); break; #ifdef CONFIG_IPV6 case AF_INET6: @@ -1153,7 +1166,7 @@ radius_change_server(struct radius_client_data *radius, serv6.sin6_port = htons(nserv->port); addr = (struct sockaddr *) &serv6; addrlen = sizeof(serv6); - sel_sock = sock6; + sel_sock = socket(PF_INET6, SOCK_DGRAM, 0); break; #endif /* CONFIG_IPV6 */ default: @@ -1162,16 +1175,11 @@ radius_change_server(struct radius_client_data *radius, if (sel_sock < 0) { wpa_printf(MSG_INFO, - "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d", - nserv->addr.af, sock, sock6, auth); + "RADIUS: Failed to open server socket (af=%d auth=%d)", + nserv->addr.af, auth); return -1; } - /* Force a reconnect by disconnecting the socket first */ - if (connect(sel_sock, (struct sockaddr *) &disconnect_addr, - sizeof(disconnect_addr)) < 0) - wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno)); - #ifdef __linux__ if (conf->force_client_dev && conf->force_client_dev[0]) { if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE, @@ -1212,19 +1220,22 @@ radius_change_server(struct radius_client_data *radius, break; #endif /* CONFIG_IPV6 */ default: + close(sel_sock); return -1; } if (bind(sel_sock, cl_addr, claddrlen) < 0) { wpa_printf(MSG_INFO, "bind[radius]: %s", strerror(errno)); - return -1; + close(sel_sock); + return -2; } } if (connect(sel_sock, addr, addrlen) < 0) { wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); - return -1; + close(sel_sock); + return -2; } #ifndef CONFIG_NATIVE_WINDOWS @@ -1254,10 +1265,17 @@ radius_change_server(struct radius_client_data *radius, } #endif /* CONFIG_NATIVE_WINDOWS */ - if (auth) + if (auth) { + radius_close_auth_socket(radius); radius->auth_sock = sel_sock; - else + } else { + radius_close_acct_socket(radius); radius->acct_sock = sel_sock; + } + + eloop_register_read_sock(sel_sock, radius_client_receive, radius, + auth ? (void *) RADIUS_AUTH : + (void *) RADIUS_ACCT); return 0; } @@ -1300,165 +1318,17 @@ static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) } -static int radius_client_disable_pmtu_discovery(int s) -{ - int r = -1; -#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) - /* Turn off Path MTU discovery on IPv4/UDP sockets. */ - int action = IP_PMTUDISC_DONT; - r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, - sizeof(action)); - if (r == -1) - wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", - strerror(errno)); -#endif - return r; -} - - -static void radius_close_auth_sockets(struct radius_client_data *radius) -{ - radius->auth_sock = -1; - - if (radius->auth_serv_sock >= 0) { - eloop_unregister_read_sock(radius->auth_serv_sock); - close(radius->auth_serv_sock); - radius->auth_serv_sock = -1; - } -#ifdef CONFIG_IPV6 - if (radius->auth_serv_sock6 >= 0) { - eloop_unregister_read_sock(radius->auth_serv_sock6); - close(radius->auth_serv_sock6); - radius->auth_serv_sock6 = -1; - } -#endif /* CONFIG_IPV6 */ -} - - -static void radius_close_acct_sockets(struct radius_client_data *radius) -{ - radius->acct_sock = -1; - - if (radius->acct_serv_sock >= 0) { - eloop_unregister_read_sock(radius->acct_serv_sock); - close(radius->acct_serv_sock); - radius->acct_serv_sock = -1; - } -#ifdef CONFIG_IPV6 - if (radius->acct_serv_sock6 >= 0) { - eloop_unregister_read_sock(radius->acct_serv_sock6); - close(radius->acct_serv_sock6); - radius->acct_serv_sock6 = -1; - } -#endif /* CONFIG_IPV6 */ -} - - static int radius_client_init_auth(struct radius_client_data *radius) { - struct hostapd_radius_servers *conf = radius->conf; - int ok = 0; - - radius_close_auth_sockets(radius); - - radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (radius->auth_serv_sock < 0) - wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", - strerror(errno)); - else { - radius_client_disable_pmtu_discovery(radius->auth_serv_sock); - ok++; - } - -#ifdef CONFIG_IPV6 - radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); - if (radius->auth_serv_sock6 < 0) - wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", - strerror(errno)); - else - ok++; -#endif /* CONFIG_IPV6 */ - - if (ok == 0) - return -1; - - radius_change_server(radius, conf->auth_server, NULL, 1); - - if (radius->auth_serv_sock >= 0 && - eloop_register_read_sock(radius->auth_serv_sock, - radius_client_receive, radius, - (void *) RADIUS_AUTH)) { - wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); - radius_close_auth_sockets(radius); - return -1; - } - -#ifdef CONFIG_IPV6 - if (radius->auth_serv_sock6 >= 0 && - eloop_register_read_sock(radius->auth_serv_sock6, - radius_client_receive, radius, - (void *) RADIUS_AUTH)) { - wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); - radius_close_auth_sockets(radius); - return -1; - } -#endif /* CONFIG_IPV6 */ - - return 0; + radius_close_auth_socket(radius); + return radius_change_server(radius, radius->conf->auth_server, NULL, 1); } static int radius_client_init_acct(struct radius_client_data *radius) { - struct hostapd_radius_servers *conf = radius->conf; - int ok = 0; - - radius_close_acct_sockets(radius); - - radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (radius->acct_serv_sock < 0) - wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", - strerror(errno)); - else { - radius_client_disable_pmtu_discovery(radius->acct_serv_sock); - ok++; - } - -#ifdef CONFIG_IPV6 - radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); - if (radius->acct_serv_sock6 < 0) - wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", - strerror(errno)); - else - ok++; -#endif /* CONFIG_IPV6 */ - - if (ok == 0) - return -1; - - radius_change_server(radius, conf->acct_server, NULL, 0); - - if (radius->acct_serv_sock >= 0 && - eloop_register_read_sock(radius->acct_serv_sock, - radius_client_receive, radius, - (void *) RADIUS_ACCT)) { - wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); - radius_close_acct_sockets(radius); - return -1; - } - -#ifdef CONFIG_IPV6 - if (radius->acct_serv_sock6 >= 0 && - eloop_register_read_sock(radius->acct_serv_sock6, - radius_client_receive, radius, - (void *) RADIUS_ACCT)) { - wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); - radius_close_acct_sockets(radius); - return -1; - } -#endif /* CONFIG_IPV6 */ - - return 0; + radius_close_acct_socket(radius); + return radius_change_server(radius, radius->conf->acct_server, NULL, 0); } @@ -1483,16 +1353,14 @@ radius_client_init(void *ctx, struct hostapd_radius_servers *conf) radius->ctx = ctx; radius->conf = conf; - radius->auth_serv_sock = radius->acct_serv_sock = - radius->auth_serv_sock6 = radius->acct_serv_sock6 = - radius->auth_sock = radius->acct_sock = -1; + radius->auth_sock = radius->acct_sock = -1; - if (conf->auth_server && radius_client_init_auth(radius)) { + if (conf->auth_server && radius_client_init_auth(radius) == -1) { radius_client_deinit(radius); return NULL; } - if (conf->acct_server && radius_client_init_acct(radius)) { + if (conf->acct_server && radius_client_init_acct(radius) == -1) { radius_client_deinit(radius); return NULL; } @@ -1515,8 +1383,8 @@ void radius_client_deinit(struct radius_client_data *radius) if (!radius) return; - radius_close_auth_sockets(radius); - radius_close_acct_sockets(radius); + radius_close_auth_socket(radius); + radius_close_acct_socket(radius); eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);