Mercurial > hg > freeDiameter
view extensions/app_radgw/rgw_servers.c @ 553:153cd2600fcf
Fix another issue
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Wed, 15 Sep 2010 14:52:24 +0900 |
parents | c2a095f04e43 |
children | 4ffbc9f1e922 |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@nict.go.jp> * * * * Copyright (c) 2010, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * * permitted provided that the following conditions are met: * * * * * Redistributions of source code must retain the above * * copyright notice, this list of conditions and the * * following disclaimer. * * * * * Redistributions in binary form must reproduce the above * * copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other * * materials provided with the distribution. * * * * * Neither the name of the WIDE Project or NICT nor the * * names of its contributors may be used to endorse or * * promote products derived from this software without * * specific prior written permission of WIDE Project and * * NICT. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *********************************************************************************************************/ /* Manage the RADIUS server(s): opening sockets, receiving messages, ... */ #include "rgw.h" #define RADIUS_MAX_MSG_LEN 3000 #define RADIUS_AUTH_PORT 1812 #define RADIUS_ACCT_PORT 1813 /* Declare the rgw_servers */ struct rgw_servs rgw_servers; void rgw_servers_dump(void) { char ipstr[INET6_ADDRSTRLEN]; if ( ! TRACE_BOOL(FULL) ) return; fd_log_debug(" auth server:\n"); fd_log_debug(" disabled..... : %s\n", rgw_servers.auth_serv.disabled ? "TRUE":"false"); fd_log_debug(" IP disabled.. : %s\n", rgw_servers.auth_serv.ip_disabled ? "TRUE":"false"); fd_log_debug(" IPv6 disabled : %s\n", rgw_servers.auth_serv.ip6_disabled ? "TRUE":"false"); fd_log_debug(" port......... : %hu\n", ntohs(rgw_servers.auth_serv.port)); inet_ntop(AF_INET, &rgw_servers.auth_serv.ip_endpoint,ipstr,sizeof(ipstr)); fd_log_debug(" IP bind...... : %s\n", ipstr); inet_ntop(AF_INET6, &rgw_servers.auth_serv.ip6_endpoint,ipstr,sizeof(ipstr)); fd_log_debug(" IPv6 bind.... : %s\n", ipstr); fd_log_debug(" acct server:\n"); fd_log_debug(" disabled..... : %s\n", rgw_servers.acct_serv.disabled ? "TRUE":"false"); fd_log_debug(" IP disabled.. : %s\n", rgw_servers.acct_serv.ip_disabled ? "TRUE":"false"); fd_log_debug(" IPv6 disabled : %s\n", rgw_servers.acct_serv.ip6_disabled ? "TRUE":"false"); fd_log_debug(" port......... : %hu\n", ntohs(rgw_servers.acct_serv.port)); inet_ntop(AF_INET, &rgw_servers.acct_serv.ip_endpoint,ipstr,sizeof(ipstr)); fd_log_debug(" IP bind...... : %s\n", ipstr); inet_ntop(AF_INET6, &rgw_servers.acct_serv.ip6_endpoint,ipstr,sizeof(ipstr)); fd_log_debug(" IPv6 bind.... : %s\n", ipstr); } static struct servers_data { int type; /* auth or acct */ int family; /* AF_INET or AF_INET6 */ int sock; /* the socket number */ pthread_t th; /* the running server thread, or NULL */ char name[10]; } SERVERS[4]; int rgw_servers_init(void) { memset(&rgw_servers, 0, sizeof(rgw_servers)); memset(&SERVERS[0], 0, sizeof(SERVERS)); rgw_servers.auth_serv.port = htons(RADIUS_AUTH_PORT); rgw_servers.acct_serv.port = htons(RADIUS_ACCT_PORT); return 0; } static void * server_thread(void * param) { struct servers_data * me = (struct servers_data *)param; TRACE_ENTRY("%p", param); CHECK_PARAMS_DO(param, return NULL); /* Set the thread name */ { char buf[48]; snprintf(buf, sizeof(buf), "radgw/%s serv", me->name); fd_log_threadname ( buf ); } /* Now loop on this socket, parse and queue each message received, until thread is cancelled. */ while (1) { struct sockaddr_storage from; socklen_t fromlen = sizeof(from); int len; struct rgw_client * nas_info = NULL; uint16_t port = 0; unsigned char buf[RADIUS_MAX_MSG_LEN]; struct rgw_radius_msg_meta *msg = NULL; pthread_testcancel(); /* receive the next message */ CHECK_SYS_DO( len = recvfrom( me->sock, &buf[0], sizeof(buf), 0, (struct sockaddr *) &from, &fromlen), break ); /* Get the port */ if (from.ss_family == AF_INET) port = ((struct sockaddr_in *)&from)->sin_port; if (from.ss_family == AF_INET6) port = ((struct sockaddr_in6 *)&from)->sin6_port; if (!port) { TRACE_DEBUG(INFO, "Invalid port (family: %d), discarding received %d bytes...", from.ss_family, len); continue; } TRACE_DEBUG(FULL, "Received %d bytes", len); TRACE_DEBUG_sSA(FULL, " from ", &from, NI_NUMERICHOST | NI_NUMERICSERV, "" ); /* Search the associated client definition, if any */ CHECK_FCT_DO( rgw_clients_search((struct sockaddr *) &from, &nas_info), { TRACE_DEBUG(INFO, "Discarding %d bytes received from unknown IP:", len); TRACE_DEBUG_sSA(INFO, " ", &from, NI_NUMERICHOST | NI_NUMERICSERV, "" ); continue; } ); /* parse the message, loop if message is invalid */ CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), { char * cliname = NULL; CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, NULL), ); TRACE_DEBUG(INFO, "Discarding invalid RADIUS message from '%s'", cliname); rgw_clients_dispose(&nas_info); continue; } ); msg->serv_type = me->type; msg->port = port; rgw_msg_dump(msg, 1); /* queue the message for a worker thread */ CHECK_FCT_DO( rgw_work_add(msg, nas_info), break ); /* Then wait for next incoming message */ } TRACE_DEBUG(INFO, "Server thread terminated."); return NULL; } /* Set the socket options for UDP sockets, before bind is called */ static int _udp_setsockopt(int family, int sk) { int ret = 0; int opt; /* In case of v6 address, force the v6only option, we use a different socket for v4 */ #ifdef IPV6_V6ONLY if (family == AF_INET6) { opt = 1; ret = setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); if (ret != 0) { ret = errno; TRACE_DEBUG(INFO, "Unable to set the socket IPV6_V6ONLY option: %s", strerror(ret)); return ret; } } #endif /* IPV6_V6ONLY */ return 0; } /* We reuse the same logic for all 4 possible servers (IP / IPv6, Auth / Acct ports) */ #define UDPSERV( _type_, _portval_, _family_ ) { \ /* Check that this type / family are not disabled by configuration */ \ if ( (! rgw_servers. _type_ ## _serv.disabled) \ && ( ! rgw_servers. _type_ ## _serv.ip ## _family_ ## _disabled ) ) { \ struct sockaddr_in ## _family_ sin ## _family_ ; \ /* Create the socket */ \ CHECK_SYS( SERVERS[idx].sock = socket(AF_INET ## _family_, SOCK_DGRAM, 0) ); \ /* Set the parameters for bind into "sin" or "sin6" */ \ memset(& sin ## _family_, 0, sizeof(struct sockaddr_in ## _family_)); \ sin ## _family_ . sin ## _family_ ## _family = AF_INET ## _family_; \ sin ## _family_ . sin ## _family_ ## _port = rgw_servers. _type_ ## _serv . port; \ memcpy( &sin ## _family_ .sin ## _family_ ## _addr, \ &rgw_servers. _type_ ## _serv . ip ## _family_ ## _endpoint, \ sizeof(struct in ## _family_ ## _addr) ); \ /* This sockopt must be set before binding */ \ TRACE_DEBUG(ANNOYING, "Setting socket options..."); \ CHECK_FCT( _udp_setsockopt(AF_INET ## _family_, SERVERS[idx].sock) ); \ /* OK, now, bind */ \ TRACE_DEBUG(ANNOYING, "Binding " #_type_ " ip" #_family_ " server..."); \ CHECK_SYS( bind( SERVERS[idx].sock, \ (struct sockaddr *)&sin ## _family_, \ sizeof(struct sockaddr_in ## _family_) ) ); \ /* Save the server information in SERVERS structure */ \ SERVERS[idx].type = _portval_; \ SERVERS[idx].family = AF_INET ## _family_; \ snprintf(&SERVERS[idx].name[0], sizeof(SERVERS[idx].name), # _type_ "/ip" #_family_); \ /* Create the server thread */ \ CHECK_POSIX( pthread_create(&SERVERS[idx].th, NULL, server_thread, &SERVERS[idx]) ); \ idx++; \ } \ } int rgw_servers_start(void) { int idx = 0; TRACE_ENTRY(); UDPSERV( auth, RGW_PLG_TYPE_AUTH, ); UDPSERV( auth, RGW_PLG_TYPE_AUTH, 6 ); UDPSERV( acct, RGW_PLG_TYPE_ACCT, ); UDPSERV( acct, RGW_PLG_TYPE_ACCT, 6 ); TRACE_DEBUG(FULL, "%d UDP servers started succesfully.", idx); return 0; } /* Send a RADIUS message */ int rgw_servers_send(int type, unsigned char *buf, size_t buflen, struct sockaddr *to, uint16_t to_port) { int idx = 0; int ret = 0; struct sockaddr_storage sto; /* Find the appropriate socket to use (not sure if it is important) */ for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) { if ( SERVERS[idx].sock && (type == SERVERS[idx].type) && (to->sa_family == SERVERS[idx].family) ) { ret = 1; break; } } if (!ret) { TRACE_DEBUG(INFO, "Trying to send a message from a disabled server: %s / %s", (type == RGW_PLG_TYPE_AUTH) ? "Auth" : "Acct", (to->sa_family == AF_INET) ? "IP (v4)" : "IPv6"); return EINVAL; } /* Prepare the destination info */ memset(&sto, 0, sizeof(sto)); if (to->sa_family == AF_INET) { memcpy(&sto, to, sizeof(struct sockaddr_in)); ((struct sockaddr_in *)&sto)->sin_port = to_port; } else { memcpy(&sto, to, sizeof(struct sockaddr_in6)); ((struct sockaddr_in6 *)&sto)->sin6_port = to_port; } TRACE_DEBUG(FULL, "Sending %d bytes", buflen); TRACE_DEBUG_sSA(FULL, " to ", &sto, NI_NUMERICHOST | NI_NUMERICSERV, "" ); /* Send */ ret = sendto(SERVERS[idx].sock, buf, buflen, 0, (struct sockaddr *)&sto, sSAlen(&sto)); if (ret < 0) { ret = errno; TRACE_DEBUG(INFO, "An error prevented sending of a RADIUS message: %s", strerror(ret)); return ret; } if (ret != buflen) { TRACE_DEBUG(INFO, "Incomplete send: %d bytes / %zd", ret, buflen); return EAGAIN; } /* Done :) */ return 0; } void rgw_servers_fini(void) { int idx = 0; for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) { if (SERVERS[idx].sock == 0) break; CHECK_FCT_DO( fd_thr_term(&SERVERS[idx].th), /* continue */ ); close(SERVERS[idx].sock); SERVERS[idx].sock = 0; } }