blob: d524f20faa75608fa2c6522f34da9a3d96a13e46 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2013, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36/* Manage the RADIUS server(s): opening sockets, receiving messages, ... */
37
38#include "rgw.h"
39
40#define RADIUS_MAX_MSG_LEN 3000
41#define RADIUS_AUTH_PORT 1812
42#define RADIUS_ACCT_PORT 1813
43
44/* Declare the rgw_servers */
45struct rgw_servs rgw_servers;
46
47void rgw_servers_dump(void)
48{
49 char ipstr[INET6_ADDRSTRLEN];
50
51 LOG_D(" auth server:");
52 LOG_D(" disabled..... : %s", rgw_servers.auth_serv.disabled ? "TRUE":"false");
53 LOG_D(" IP disabled.. : %s", rgw_servers.auth_serv.ip_disabled ? "TRUE":"false");
54 LOG_D(" IPv6 disabled : %s", rgw_servers.auth_serv.ip6_disabled ? "TRUE":"false");
55 LOG_D(" port......... : %hu", ntohs(rgw_servers.auth_serv.port));
56 inet_ntop(AF_INET, &rgw_servers.auth_serv.ip_endpoint,ipstr,sizeof(ipstr));
57 LOG_D(" IP bind...... : %s", ipstr);
58 inet_ntop(AF_INET6, &rgw_servers.auth_serv.ip6_endpoint,ipstr,sizeof(ipstr));
59 LOG_D(" IPv6 bind.... : %s", ipstr);
60
61 LOG_D(" acct server:");
62 LOG_D(" disabled..... : %s", rgw_servers.acct_serv.disabled ? "TRUE":"false");
63 LOG_D(" IP disabled.. : %s", rgw_servers.acct_serv.ip_disabled ? "TRUE":"false");
64 LOG_D(" IPv6 disabled : %s", rgw_servers.acct_serv.ip6_disabled ? "TRUE":"false");
65 LOG_D(" port......... : %hu", ntohs(rgw_servers.acct_serv.port));
66 inet_ntop(AF_INET, &rgw_servers.acct_serv.ip_endpoint,ipstr,sizeof(ipstr));
67 LOG_D(" IP bind...... : %s", ipstr);
68 inet_ntop(AF_INET6, &rgw_servers.acct_serv.ip6_endpoint,ipstr,sizeof(ipstr));
69 LOG_D(" IPv6 bind.... : %s", ipstr);
70
71}
72
73static struct servers_data {
74 int type; /* auth or acct */
75 int family; /* AF_INET or AF_INET6 */
76 int sock; /* the socket number */
77 pthread_t th; /* the running server thread, or NULL */
78 char name[10];
79} SERVERS[4];
80
81int rgw_servers_init(void)
82{
83 memset(&rgw_servers, 0, sizeof(rgw_servers));
84 memset(&SERVERS[0], 0, sizeof(SERVERS));
85
86 rgw_servers.auth_serv.port = htons(RADIUS_AUTH_PORT);
87 rgw_servers.acct_serv.port = htons(RADIUS_ACCT_PORT);
88
89 return 0;
90}
91
92static void * server_thread(void * param)
93{
94 struct servers_data * me = (struct servers_data *)param;
95
96 TRACE_ENTRY("%p", param);
97
98 CHECK_PARAMS_DO(param, return NULL);
99
100 /* Set the thread name */
101 {
102 char buf[48];
103 snprintf(buf, sizeof(buf), "radgw/%s serv", me->name);
104 fd_log_threadname ( buf );
105 }
106
107 /* Now loop on this socket, parse and queue each message received, until thread is cancelled. */
108 while (1) {
109 struct sockaddr_storage from;
110 char sa_buf[sSA_DUMP_STRLEN];
111 socklen_t fromlen = sizeof(from);
112 int len;
113 struct rgw_client * nas_info = NULL;
114 uint16_t port = 0;
115 unsigned char buf[RADIUS_MAX_MSG_LEN];
116 struct rgw_radius_msg_meta *msg = NULL;
117
118 pthread_testcancel();
119
120 /* receive the next message */
121 CHECK_SYS_DO( len = recvfrom( me->sock, &buf[0], sizeof(buf), 0, (struct sockaddr *) &from, &fromlen), break );
122
123 /* Get the port */
124 port = sSAport(&from);
125 if (!port) {
126 LOG_E("Invalid port (family: %d), discarding received %d bytes...", from.ss_family, len);
127 continue;
128 }
129
130 fd_sa_sdump_numeric(sa_buf, (sSA*)&from);
131 LOG_D("RADIUS: RCV %dB from %s", len, sa_buf);
132
133 /* Search the associated client definition, if any */
134 CHECK_FCT_DO( rgw_clients_search((struct sockaddr *) &from, &nas_info),
135 {
136 LOG_E("Discarding %d bytes received from unknown IP: %s", len, sa_buf);
137 continue;
138 } );
139
140
141 /* parse the message, loop if message is invalid */
142 CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg),
143 {
144 DiamId_t cliname = NULL;
145 size_t clisz;
146 CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, &clisz, NULL, NULL), );
147 LOG_E( "Discarding invalid RADIUS message from '%s'", cliname);
148 rgw_clients_dispose(&nas_info);
149 continue;
150 } );
151
152 msg->serv_type = me->type;
153 msg->port = port;
154
155 rgw_msg_dump(msg, 1);
156
157 /* queue the message for a worker thread */
158 CHECK_FCT_DO( rgw_work_add(msg, nas_info), break );
159
160 /* Then wait for next incoming message */
161 }
162
163 TRACE_DEBUG(INFO, "Server thread terminated.");
164 return NULL;
165}
166
167/* Set the socket options for UDP sockets, before bind is called */
168static int _udp_setsockopt(int family, int sk)
169{
170 int ret = 0;
171 int opt;
172
173 /* In case of v6 address, force the v6only option, we use a different socket for v4 */
174 #ifdef IPV6_V6ONLY
175 if (family == AF_INET6) {
176 opt = 1;
177 ret = setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
178 if (ret != 0) {
179 ret = errno;
180 TRACE_DEBUG(INFO, "Unable to set the socket IPV6_V6ONLY option: %s", strerror(ret));
181 return ret;
182 }
183 }
184 #endif /* IPV6_V6ONLY */
185
186 return 0;
187}
188
189/* We reuse the same logic for all 4 possible servers (IP / IPv6, Auth / Acct ports) */
190#define UDPSERV( _type_, _portval_, _family_ ) { \
191 /* Check that this type / family are not disabled by configuration */ \
192 if ( (! rgw_servers. _type_ ## _serv.disabled) \
193 && ( ! rgw_servers. _type_ ## _serv.ip ## _family_ ## _disabled ) ) { \
194 struct sockaddr_in ## _family_ sin ## _family_ ; \
195 /* Create the socket */ \
196 CHECK_SYS( SERVERS[idx].sock = socket(AF_INET ## _family_, SOCK_DGRAM, 0) ); \
197 /* Set the parameters for bind into "sin" or "sin6" */ \
198 memset(& sin ## _family_, 0, sizeof(struct sockaddr_in ## _family_)); \
199 sin ## _family_ . sin ## _family_ ## _family = AF_INET ## _family_; \
200 sin ## _family_ . sin ## _family_ ## _port = rgw_servers. _type_ ## _serv . port; \
201 memcpy( &sin ## _family_ .sin ## _family_ ## _addr, \
202 &rgw_servers. _type_ ## _serv . ip ## _family_ ## _endpoint, \
203 sizeof(struct in ## _family_ ## _addr) ); \
204 /* This sockopt must be set before binding */ \
205 TRACE_DEBUG(ANNOYING, "Setting socket options..."); \
206 CHECK_FCT( _udp_setsockopt(AF_INET ## _family_, SERVERS[idx].sock) ); \
207 /* OK, now, bind */ \
208 TRACE_DEBUG(ANNOYING, "Binding " #_type_ " ip" #_family_ " server..."); \
209 CHECK_SYS( bind( SERVERS[idx].sock, \
210 (struct sockaddr *)&sin ## _family_, \
211 sizeof(struct sockaddr_in ## _family_) ) ); \
212 /* Save the server information in SERVERS structure */ \
213 SERVERS[idx].type = _portval_; \
214 SERVERS[idx].family = AF_INET ## _family_; \
215 snprintf(&SERVERS[idx].name[0], sizeof(SERVERS[idx].name), # _type_ "/ip" #_family_); \
216 /* Create the server thread */ \
217 CHECK_POSIX( pthread_create(&SERVERS[idx].th, NULL, server_thread, &SERVERS[idx]) ); \
218 idx++; \
219 } \
220}
221
222int rgw_servers_start(void)
223{
224 int idx = 0;
225
226 TRACE_ENTRY();
227
228 UDPSERV( auth, RGW_PLG_TYPE_AUTH, );
229 UDPSERV( auth, RGW_PLG_TYPE_AUTH, 6 );
230 UDPSERV( acct, RGW_PLG_TYPE_ACCT, );
231 UDPSERV( acct, RGW_PLG_TYPE_ACCT, 6 );
232
233 TRACE_DEBUG(FULL, "%d UDP servers started succesfully.", idx);
234 return 0;
235}
236
237/* Send a RADIUS message */
238int rgw_servers_send(int type, unsigned char *buf, size_t buflen, struct sockaddr *to, uint16_t to_port)
239{
240 int idx = 0;
241 int ret = 0;
242 struct sockaddr_storage sto;
243 char sa_buf[sSA_DUMP_STRLEN];
244
245 /* Find the appropriate socket to use (not sure if it is important) */
246 for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) {
247 if ( SERVERS[idx].sock && (type == SERVERS[idx].type) && (to->sa_family == SERVERS[idx].family) ) {
248 ret = 1;
249 break;
250 }
251 }
252
253 if (!ret) {
254 LOG_E( "Trying to send a message from a disabled server: %s / %s",
255 (type == RGW_PLG_TYPE_AUTH) ? "Auth" : "Acct",
256 (to->sa_family == AF_INET) ? "IP (v4)" : "IPv6");
257 return EINVAL;
258 }
259
260 /* Prepare the destination info */
261 memset(&sto, 0, sizeof(sto));
262 if (to->sa_family == AF_INET) {
263 memcpy(&sto, to, sizeof(struct sockaddr_in));
264 ((struct sockaddr_in *)&sto)->sin_port = to_port;
265 } else {
266 memcpy(&sto, to, sizeof(struct sockaddr_in6));
267 ((struct sockaddr_in6 *)&sto)->sin6_port = to_port;
268 }
269
270 fd_sa_sdump_numeric(sa_buf, (sSA*)&sto);
271 LOG_D("RADIUS: SND %zdB to %s", buflen, sa_buf);
272
273 /* Send */
274 ret = sendto(SERVERS[idx].sock, buf, buflen, 0, (struct sockaddr *)&sto, sSAlen(&sto));
275 if (ret < 0) {
276 ret = errno;
277 TRACE_DEBUG(INFO, "An error prevented sending of a RADIUS message: %s", strerror(ret));
278 return ret;
279 }
280 if (ret != buflen) {
281 TRACE_DEBUG(INFO, "Incomplete send: %d bytes / %zd", ret, buflen);
282 return EAGAIN;
283 }
284
285 /* Done :) */
286 return 0;
287}
288
289void rgw_servers_fini(void)
290{
291 int idx = 0;
292
293 for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) {
294 if (SERVERS[idx].sock == 0)
295 break;
296
297 CHECK_FCT_DO( fd_thr_term(&SERVERS[idx].th), /* continue */ );
298 close(SERVERS[idx].sock);
299 SERVERS[idx].sock = 0;
300 }
301
302}
303
304