blob: 25cb1469097e513cc4fa986d1da6dd8182efe43c [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 incoming RADIUS messages. */
37
38#include "rgw.h"
39
40/* How many threads to handle messages in parallel ? */
41#define NB_WORKERS 2
42
43static pthread_t workers[NB_WORKERS];
44static struct fifo * work_stack = NULL;
45
46/* Data that is stacked */
47struct work_item {
48 struct rgw_radius_msg_meta * msg;
49 struct rgw_client * cli;
50};
51
52/* Data stored in freeDiameter while pending Diameter answer */
53struct pending_answer {
54 struct rgw_radius_msg_meta * rad; /* the RADIUS message that was received and translated */
55 struct rgw_client * cli; /* the client it was received from */
56};
57
58/* Callback when a Diameter answer is received */
59static void receive_diam_answer(void * paback, struct msg **ans);
60
61/* Worker thread, processing incoming RADIUS messages (after parsing) */
62static void * work_th(void * arg)
63{
64 TRACE_ENTRY("%p", arg);
65
66 /* Set the thread name */
67 {
68 char buf[48];
69 snprintf(buf, sizeof(buf), "radgw/worker #%ld", (long)arg);
70 fd_log_threadname ( buf );
71 }
72
73 while (1) { /* The thread will be cancelled */
74
75 struct rgw_radius_msg_meta * msg;
76 struct rgw_client * cli;
77 struct msg * diam_msg;
78 int pb, a;
79 struct pending_answer * pa;
80
81 /* Get the next incoming RADIUS message */
82 {
83 struct work_item * wi = NULL;
84
85 CHECK_FCT_DO( fd_fifo_get(work_stack, &wi), break );
86
87 msg = wi->msg;
88 cli = wi->cli;
89 free(wi);
90 }
91
92 TRACE_DEBUG(ANNOYING, "Processing next RADIUS message: %p received on client: %p", msg, cli);
93
94 /* process the data */
95
96 /* Check authenticator, if any */
97 CHECK_FCT_DO( rgw_clients_auth_check(msg, cli, NULL),
98 {
99 /* An error occurred, discard message */
100 rgw_msg_free(&msg);
101 rgw_clients_dispose(&cli);
102 continue;
103 } );
104
105 /* Check duplicate */
106 CHECK_FCT_DO( rgw_clients_check_dup(&msg, cli),
107 {
108 /* An error occurred, discard message */
109 rgw_msg_free(&msg);
110 rgw_clients_dispose(&cli);
111 continue;
112 } );
113 if (msg == NULL) {
114 rgw_clients_dispose(&cli);
115 continue; /* the message was a duplicate */
116 }
117
118 diam_msg = NULL;
119 /* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */
120
121 /* Check that IP is coherent with the identity in the message, and create an empty message with only Origin information */
122 CHECK_FCT_DO( rgw_clients_create_origin(msg, cli, &diam_msg),
123 {
124 /* An error occurred, discard message */
125 if (diam_msg) {
126 CHECK_FCT_DO( fd_msg_free(diam_msg), );
127 }
128 rgw_msg_free(&msg);
129 rgw_clients_dispose(&cli);
130 continue;
131 } );
132
133 /* Pass the message to the list of registered plugins */
134 CHECK_FCT_DO( rgw_plg_loop_req(&msg, &diam_msg, cli),
135 {
136 /* An error occurred, discard message */
137 if (diam_msg) {
138 CHECK_FCT_DO( fd_msg_free(diam_msg), );
139 diam_msg = NULL;
140 }
141 rgw_msg_free(&msg);
142 rgw_clients_dispose(&cli);
143 continue;
144 } );
145 if (msg == NULL) { /* Error or RADIUS answer locally generated */
146 rgw_clients_dispose(&cli);
147 if (diam_msg) {
148 CHECK_FCT_DO( fd_msg_free(diam_msg), );
149 diam_msg = NULL;
150 }
151 continue; /* the message was handled already */
152 }
153
154 pb = 0;
155
156 /* Check the created Diameter message -- it will be invalid if no callback has handled the RADIUS message */
157 if ((diam_msg == NULL) || ( fd_msg_parse_rules(diam_msg, fd_g_config->cnf_dict, NULL) ) ) {
158 fd_log_debug("[radgw] No or invalid Diameter message was generated after processing the RADIUS command %hhd (%s)."
159 " It may indicate a gateway configuration problem, or implementation issue in a plugin.",
160 msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
161 /* We should also dump the conflicting rule here to help debug? */
162 pb++;
163 }
164
165 /* Check if the full content of the RADIUS message was handled */
166 for (a = 0; a < msg->radius.attr_used; a++) {
167 struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[a]);
168 pb++;
169 fd_log_debug("[radgw] No plugin available to handle attribute %hhd (%s) in command %hhd (%s)! Translation aborted.",
170 attr->type, rgw_msg_attrtype_str(attr->type),
171 msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code));
172 }
173
174 if (pb) {
175 /* Something went wrong during the conversion */
176 if (diam_msg) {
177 CHECK_FCT_DO( fd_msg_free(diam_msg), );
178 diam_msg = NULL;
179 }
180
181 rgw_msg_free(&msg);
182 rgw_clients_dispose(&cli);
183
184 TRACE_DEBUG(INFO, "%d problem(s) occurred while translating a RADIUS message, data discarded.", pb);
185 continue;
186 }
187
188 /* Send the Diameter message and register for receiving the answer */
189 CHECK_MALLOC_DO( pa = malloc(sizeof(struct pending_answer)), break );
190 memset(pa, 0, sizeof(*pa));
191 pa->rad = msg;
192 pa->cli = cli;
193
194 CHECK_FCT_DO( fd_msg_send( &diam_msg, receive_diam_answer, pa),
195 {
196 /* If an error occurs, log and destroy the data */
197 fd_log_debug("An error occurred while sending Diameter message, please turn Debug on for detail.");
198
199 if (diam_msg) {
200 CHECK_FCT_DO( fd_msg_free(diam_msg), );
201 diam_msg = NULL;
202 }
203
204 rgw_msg_free(&msg);
205 rgw_clients_dispose(&cli);
206
207 free(pa);
208
209 continue;
210 } );
211
212 /* Done! */
213 }
214
215 TRACE_DEBUG(INFO, "Thread terminated!");
216 return NULL;
217}
218
219static void receive_diam_answer(void * paback, struct msg **ans)
220{
221 struct pending_answer * pa = (struct pending_answer *)paback;
222 struct radius_msg * rad_ans;
223 struct avp *avp;
224 struct avp_hdr *ahdr;
225 int pb = 0;
226
227 TRACE_ENTRY("%p %p", pa, ans);
228 CHECK_PARAMS_DO( pa && ans, return );
229
230 /* Create an empty RADIUS answer message */
231 CHECK_MALLOC_DO( rad_ans = radius_msg_new(0, pa->rad->radius.hdr->identifier), goto out );
232
233 /* Pass the Diameter answer to the same extensions as the request */
234 CHECK_FCT_DO( rgw_plg_loop_ans(pa->rad, ans, &rad_ans, pa->cli), goto out );
235
236 if (*ans != NULL) {
237
238 /* Now check what AVPs remain in the diameter answer. If AVPs with the 'M' flag are here, we have a problem... */
239 CHECK_FCT_DO( fd_msg_browse(*ans, MSG_BRW_FIRST_CHILD, &avp, NULL), { avp = NULL; pb++; } );
240 while (avp) {
241 CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), { pb++; continue; } );
242 if (ahdr->avp_flags & AVP_FLAG_MANDATORY) {
243 if (ahdr->avp_flags & AVP_FLAG_VENDOR) {
244 TRACE_DEBUG(FULL, "Remaining Mandatory Vendor AVP, code %d", ahdr->avp_code);
245 pb++;
246 } else {
247 switch (ahdr->avp_code) {
248 /* A few AVPs can be safely ignored here: */
249 case DIAM_ATTR_SESSION_ID:
250 case DIAM_ATTR_ROUTE_RECORD:
251 case DIAM_ATTR_PROXY_INFO:
252
253
254 /* just ignore */
255 break;
256
257 default:
258 LOG_D("Remaining Mandatory AVP, code %d", ahdr->avp_code);
259 pb++;
260 }
261 }
262 }
263 CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { pb++; break; } );
264 }
265
266 if (pb) {
267 TRACE_DEBUG(INFO, "[radgw] WARNING: %d mandatory AVP in the Diameter answer have not been translated to RADIUS! Please use debug.rgwx for more information.", pb);
268 }
269 }
270
271
272out:
273 /* Now try and send the RADIUS answer */
274 if (rad_ans) {
275 CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, pa->rad, pa->cli), );
276 } else {
277 /* Remove the request from the duplicate cache */
278 CHECK_FCT_DO( rgw_client_finish_nosend(pa->rad, pa->cli), );
279 }
280
281 /* Clear the Diameter message */
282 if (*ans) {
283 CHECK_FCT_DO( fd_msg_free(*ans), );
284 *ans = NULL;
285 }
286
287 /* Clear the RADIUS request */
288 if (pa->rad) {
289 rgw_msg_free(&pa->rad);
290 }
291
292 /* Release reference on the client */
293 rgw_clients_dispose(&pa->cli);
294
295 /* Clear the answer data */
296 free(pa);
297
298 /* Finished */
299 return;
300}
301
302int rgw_work_start(void)
303{
304 long i;
305 TRACE_ENTRY();
306
307 memset(workers, 0, sizeof(workers));
308
309 CHECK_FCT( fd_fifo_new ( &work_stack, 30 ) );
310
311 /* Create the worker thread(s) */
312 for (i = 0; i < NB_WORKERS; i++) {
313 CHECK_POSIX( pthread_create(&workers[i], NULL, work_th, (void *)i) );
314 }
315
316 return 0;
317}
318
319int rgw_work_add(struct rgw_radius_msg_meta * msg, struct rgw_client * client)
320{
321 struct work_item * new;
322
323 CHECK_MALLOC( new = malloc(sizeof(struct work_item)) );
324 memset(new, 0, sizeof(struct work_item));
325
326 new->msg = msg;
327 new->cli = client;
328
329 CHECK_FCT( fd_fifo_post(work_stack, &new) );
330
331 return 0;
332}
333
334void rgw_work_fini(void)
335{
336 int i;
337 TRACE_ENTRY();
338
339 for (i = 0; i < NB_WORKERS; i++) {
340 fd_thr_term(&workers[i]);
341 }
342
343 TODO("Empty the stack, what to do about the RADIUS messages?");
344
345 return;
346}