Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/app_radgw/rgw_servers.c b/extensions/app_radgw/rgw_servers.c
new file mode 100644
index 0000000..d524f20
--- /dev/null
+++ b/extensions/app_radgw/rgw_servers.c
@@ -0,0 +1,304 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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];
+	
+	LOG_D(" auth server:");
+	LOG_D("    disabled..... : %s", rgw_servers.auth_serv.disabled ? "TRUE":"false");
+	LOG_D("    IP disabled.. : %s", rgw_servers.auth_serv.ip_disabled ? "TRUE":"false");
+	LOG_D("    IPv6 disabled : %s", rgw_servers.auth_serv.ip6_disabled ? "TRUE":"false");
+	LOG_D("    port......... : %hu", ntohs(rgw_servers.auth_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.auth_serv.ip_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IP bind...... : %s", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.auth_serv.ip6_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IPv6 bind.... : %s", ipstr);
+
+	LOG_D(" acct server:");
+	LOG_D("    disabled..... : %s", rgw_servers.acct_serv.disabled ? "TRUE":"false");
+	LOG_D("    IP disabled.. : %s", rgw_servers.acct_serv.ip_disabled ? "TRUE":"false");
+	LOG_D("    IPv6 disabled : %s", rgw_servers.acct_serv.ip6_disabled ? "TRUE":"false");
+	LOG_D("    port......... : %hu", ntohs(rgw_servers.acct_serv.port));
+	inet_ntop(AF_INET, &rgw_servers.acct_serv.ip_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IP bind...... : %s", ipstr);
+	inet_ntop(AF_INET6, &rgw_servers.acct_serv.ip6_endpoint,ipstr,sizeof(ipstr));
+	LOG_D("    IPv6 bind.... : %s", 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;
+		char sa_buf[sSA_DUMP_STRLEN];
+		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 */
+		port = sSAport(&from);
+		if (!port) {
+			LOG_E("Invalid port (family: %d), discarding received %d bytes...", from.ss_family, len);
+			continue;
+		}
+		
+		fd_sa_sdump_numeric(sa_buf, (sSA*)&from);
+		LOG_D("RADIUS: RCV %dB from %s", len, sa_buf);
+		
+		/* Search the associated client definition, if any */
+		CHECK_FCT_DO( rgw_clients_search((struct sockaddr *) &from, &nas_info),
+			{
+				LOG_E("Discarding %d bytes received from unknown IP: %s", len, sa_buf);
+				continue;
+			} );
+				
+		
+		/* parse the message, loop if message is invalid */
+		CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), 
+			{
+				DiamId_t cliname = NULL;
+				size_t clisz;
+				CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, &clisz, NULL, NULL), );
+				LOG_E( "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;
+	char sa_buf[sSA_DUMP_STRLEN];
+	
+	/* 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) {
+		LOG_E( "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;
+	}
+	
+	fd_sa_sdump_numeric(sa_buf, (sSA*)&sto);
+	LOG_D("RADIUS: SND %zdB   to %s", buflen, sa_buf);
+	
+	/* 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;
+	}
+	
+}
+
+