Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
radius_acct.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2016 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * radius_acct.c - RADIUS accounting proxy thread
59  *
60  * Stipe Tolj <stolj@kannel.org>
61  */
62 
63 #include <string.h>
64 #include <fcntl.h>
65 #include <errno.h>
66 
67 #include "gwlib/gwlib.h"
68 #include "radius/radius_acct.h"
69 #include "radius/radius_pdu.h"
70 
71 static Dict *radius_table = NULL; /* maps client ip -> msisdn */
72 static Dict *session_table = NULL; /* maps session id -> client ip */
73 static Dict *client_table = NULL; /* maps client ip -> session id */
74 
75 /* we will initialize hash tables in the size of our NAS ports */
76 #define RADIUS_NAS_PORTS 30
77 
78 static Mutex *radius_mutex = NULL;
79 static int run_thread = 0;
80 
81 /*
82  * Beware that the official UDP port for RADIUS accounting packets
83  * is 1813 (according to RFC2866). The previously used port 1646 has
84  * been conflicting with an other protocol and "should" not be used.
85  */
86 static Octstr *our_host = NULL;
87 static long our_port = 1813;
88 static Octstr *remote_host = NULL;
89 static long remote_port = 1813;
90 
91 /* the shared secrets for NAS and remote RADIUS communication */
92 static Octstr *secret_nas = NULL;
93 static Octstr *secret_radius = NULL;
94 
95 /* the global unified-prefix list */
96 static Octstr *unified_prefix = NULL;
97 
98 /* timeout in msec for the remote RADIUS responses */
99 static long remote_timeout = 40000;
100 
101 /*************************************************************************
102  *
103  */
104 
105 /*
106  * Updates the internal RADIUS mapping tables. Returns 1 if the
107  * mapping has been processes and the PDU should be proxied to the
108  * remote RADIUS server, otherwise if it is a duplicate returns 0.
109  */
110 static int update_tables(RADIUS_PDU *pdu)
111 {
112  Octstr *client_ip, *msisdn;
113  Octstr *type, *session_id;
114  int ret = 0;
115  Octstr *rm_item;
116 
117  client_ip = msisdn = type = session_id = NULL;
118 
119  /* only add if we have a Accounting-Request PDU */
120  if (pdu->type == 0x04) {
121 
122  /* check if we have a START or STOP event */
123  type = dict_get(pdu->attr, octstr_imm("Acct-Status-Type"));
124 
125  /* get the sesion id */
126  session_id = dict_get(pdu->attr, octstr_imm("Acct-Session-Id"));
127 
128  /* grep the needed data */
129  client_ip = dict_get(pdu->attr, octstr_imm("Framed-IP-Address"));
130  msisdn = dict_get(pdu->attr, octstr_imm("Calling-Station-Id"));
131 
132  /* we can't add mapping without both components */
133  if (client_ip == NULL || msisdn == NULL) {
134  warning(0, "RADIUS: NAS did either not send 'Framed-IP-Address' or/and "
135  "'Calling-Station-Id', dropping mapping but will forward.");
136  /* anyway forward the packet to remote RADIUS server */
137  return 1;
138  }
139 
140  if (octstr_compare(type, octstr_imm("1")) == 0 && session_id && msisdn) {
141  /* session START */
142  if (dict_get(radius_table, client_ip) == NULL &&
143  dict_get(session_table, session_id) == NULL) {
144  Octstr *put_msisdn = octstr_duplicate(msisdn);
145  Octstr *put_client_ip = octstr_duplicate(client_ip);
146  Octstr *put_session_id = octstr_duplicate(session_id);
147  Octstr *old_session_id, *old_client_ip;
148 
149  /* ok, this is a new session. If it contains an IP that is still
150  * in the session/client tables then remove the old session from the
151  * two tables session/client */
152  if ((old_session_id = dict_get(client_table, client_ip)) != NULL &&
153  (old_client_ip = dict_get(session_table, old_session_id)) != NULL &&
154  octstr_compare(old_session_id, session_id) != 0) {
155  rm_item = dict_remove(client_table, client_ip);
156  octstr_destroy(rm_item);
157  rm_item = dict_remove(session_table, old_session_id);
158  octstr_destroy(rm_item);
159  octstr_destroy(old_session_id);
160  octstr_destroy(old_client_ip);
161  }
162 
163  /* insert both, new client IP and session to mapping tables */
164  dict_put(radius_table, client_ip, put_msisdn);
165  dict_put(session_table, session_id, put_client_ip);
166  dict_put(client_table, client_ip, put_session_id);
167 
168  info(0, "RADIUS: Mapping `%s <-> %s' for session id <%s> added.",
169  octstr_get_cstr(client_ip), octstr_get_cstr(msisdn),
170  octstr_get_cstr(session_id));
171  ret = 1;
172  } else {
173  warning(0, "RADIUS: Duplicate mapping `%s <-> %s' for session "
174  "id <%s> received, ignoring.",
175  octstr_get_cstr(client_ip), octstr_get_cstr(msisdn),
176  octstr_get_cstr(session_id));
177  }
178  } else if (octstr_compare(type, octstr_imm("2")) == 0) {
179  /* session STOP */
180  Octstr *comp_client_ip;
181  if ((msisdn = dict_get(radius_table, client_ip)) != NULL &&
182  (comp_client_ip = dict_get(session_table, session_id)) != NULL &&
183  octstr_compare(client_ip, comp_client_ip) == 0) {
184  dict_remove(radius_table, client_ip);
185  rm_item = dict_remove(client_table, client_ip);
186  octstr_destroy(rm_item);
187  dict_remove(session_table, session_id);
188  info(0, "RADIUS: Mapping `%s <-> %s' for session id <%s> removed.",
189  octstr_get_cstr(client_ip), octstr_get_cstr(msisdn),
190  octstr_get_cstr(session_id));
191  octstr_destroy(msisdn);
192  octstr_destroy(comp_client_ip);
193 
194  ret = 1;
195  } else {
196  warning(0, "RADIUS: Could not find mapping for `%s' session "
197  "id <%s>, ignoring.",
198  octstr_get_cstr(client_ip), octstr_get_cstr(session_id));
199  }
200 
201  } else {
202  error(0, "RADIUS: unknown Acct-Status-Type `%s' received, ignoring.",
203  octstr_get_cstr(type));
204  }
205  }
206 
207  return ret;
208 }
209 
210 
211 /*************************************************************************
212  * The main proxy thread.
213  */
214 
215 static void proxy_thread(void *arg)
216 {
217  int ss, cs; /* server and client sockets */
218  int fl; /* socket flags */
219  Octstr *addr = NULL;
220  int forward;
221  Octstr *tmp;
222 
223  run_thread = 1;
224  ss = cs = -1;
225 
226  /* create client binding, only if we have a remote server
227  * and make the client socet non-blocking */
228  if (remote_host != NULL) {
229  cs = udp_client_socket();
230  fl = fcntl(cs, F_GETFL);
231  fcntl(cs, F_SETFL, fl | O_NONBLOCK);
232  addr = udp_create_address(remote_host, remote_port);
233  }
234 
235  /* create server binding */
236  ss = udp_bind(our_port, octstr_get_cstr(our_host));
237 
238  /* make the server socket non-blocking */
239  fl = fcntl(ss, F_GETFL);
240  fcntl(ss, F_SETFL, fl | O_NONBLOCK);
241 
242  if (ss == -1)
243  panic(0, "RADIUS: Couldn't set up server socket for port %ld.", our_port);
244 
245  while (run_thread) {
246  RADIUS_PDU *pdu, *r;
247  Octstr *data, *rdata;
248  Octstr *from_nas, *from_radius;
249 
250  pdu = r = NULL;
251  data = rdata = from_nas = from_radius = NULL;
252 
253  if (read_available(ss, 100000) < 1)
254  continue;
255 
256  /* get request from NAS */
257  if (udp_recvfrom(ss, &data, &from_nas) == -1) {
258  if (errno == EAGAIN)
259  /* No datagram available, don't block. */
260  continue;
261 
262  error(0, "RADIUS: Couldn't receive request data from NAS");
263  continue;
264  }
265 
266  tmp = udp_get_ip(from_nas);
267  info(0, "RADIUS: Got data from NAS <%s:%d>",
268  octstr_get_cstr(tmp), udp_get_port(from_nas));
269  octstr_destroy(tmp);
270  octstr_dump(data, 0);
271 
272  /* unpacking the RADIUS PDU */
273  if ((pdu = radius_pdu_unpack(data)) == NULL) {
274  warning(0, "RADIUS: Couldn't unpack PDU from NAS, ignoring.");
275  goto error;
276  }
277  info(0, "RADIUS: from NAS: PDU type: %s", pdu->type_name);
278 
279  /* authenticate the Accounting-Request packet */
280  if (radius_authenticate_pdu(pdu, &data, secret_nas) == 0) {
281  warning(0, "RADIUS: Authentication failed for PDU from NAS, ignoring.");
282  goto error;
283  }
284 
285  /* store to hash table if not present yet */
286  mutex_lock(radius_mutex);
287  forward = update_tables(pdu);
288  mutex_unlock(radius_mutex);
289 
290  /* create response PDU for NAS */
291  r = radius_pdu_create(0x05, pdu);
292 
293  /*
294  * create response authenticator
295  * code+identifier(req)+length+authenticator(req)+(attributes)+secret
296  */
297  r->u.Accounting_Response.identifier = pdu->u.Accounting_Request.identifier;
298  r->u.Accounting_Response.authenticator =
299  octstr_duplicate(pdu->u.Accounting_Request.authenticator);
300 
301  /* pack response for NAS */
302  rdata = radius_pdu_pack(r);
303 
304  /* creates response autenticator in encoded PDU */
305  radius_authenticate_pdu(r, &rdata, secret_nas);
306 
307  /*
308  * forward request to remote RADIUS server only if updated
309  * and if we have a configured remote RADIUS server
310  */
311  if ((remote_host != NULL) && forward) {
312  if (udp_sendto(cs, data, addr) == -1) {
313  error(0, "RADIUS: Couldn't send to remote RADIUS <%s:%ld>.",
314  octstr_get_cstr(remote_host), remote_port);
315  } else
316  if (read_available(cs, remote_timeout) < 1) {
317  error(0, "RADIUS: Timeout for response from remote RADIUS <%s:%ld>.",
318  octstr_get_cstr(remote_host), remote_port);
319  } else
320  if (udp_recvfrom(cs, &data, &from_radius) == -1) {
321  error(0, "RADIUS: Couldn't receive from remote RADIUS <%s:%ld>.",
322  octstr_get_cstr(remote_host), remote_port);
323  } else {
324  info(0, "RADIUS: Got data from remote RADIUS <%s:%d>.",
325  octstr_get_cstr(udp_get_ip(from_radius)), udp_get_port(from_radius));
326  octstr_dump(data, 0);
327 
328  /* XXX unpack the response PDU and check if the response
329  * authenticator is valid */
330  }
331  }
332 
333  /* send response to NAS */
334  if (udp_sendto(ss, rdata, from_nas) == -1)
335  error(0, "RADIUS: Couldn't send response data to NAS <%s:%d>.",
336  octstr_get_cstr(udp_get_ip(from_nas)), udp_get_port(from_nas));
337 
338 error:
339  radius_pdu_destroy(pdu);
341 
342  octstr_destroy(rdata);
343  octstr_destroy(data);
344  octstr_destroy(from_nas);
345 
346  debug("radius.proxy", 0, "RADIUS: Mapping table contains %ld elements",
347  dict_key_count(radius_table));
348  debug("radius.proxy", 0, "RADIUS: Session table contains %ld elements",
349  dict_key_count(session_table));
350  debug("radius.proxy", 0, "RADIUS: Client table contains %ld elements",
351  dict_key_count(client_table));
352 
353  }
354 
355  octstr_destroy(addr);
356 }
357 
358 
359 /*************************************************************************
360  * Public functions: init, shutdown, mapping.
361  */
362 
364 {
365  Octstr *m, *r;
366  char *uf;
367 
368  /* if no proxy thread is running, then pass NULL as result */
369  if (radius_table == NULL || client_ip == NULL)
370  return NULL;
371 
372  mutex_lock(radius_mutex);
373  m = dict_get(radius_table, client_ip);
374  mutex_unlock(radius_mutex);
375  r = m ? octstr_duplicate(m) : NULL;
376 
377  /* apply number normalization */
378  uf = unified_prefix ? octstr_get_cstr(unified_prefix) : NULL;
379  normalize_number(uf, &r);
380 
381  return r;
382 }
383 
385 {
386  long nas_ports = 0;
387 
388  /* get configured parameters */
389  if ((our_host = cfg_get(grp, octstr_imm("our-host"))) == NULL) {
390  our_host = octstr_create("0.0.0.0");
391  }
392  if ((remote_host = cfg_get(grp, octstr_imm("remote-host"))) != NULL) {
393  cfg_get_integer(&remote_port, grp, octstr_imm("remote-port"));
394  if ((secret_radius = cfg_get(grp, octstr_imm("secret-radius"))) == NULL) {
395  panic(0, "RADIUS: No shared secret `secret-radius' for remote RADIUS in `radius-acct' provided.");
396  }
397  }
398  cfg_get_integer(&our_port, grp, octstr_imm("our-port"));
399  cfg_get_integer(&remote_timeout, grp, octstr_imm("remote-timeout"));
400 
401  if ((cfg_get_integer(&nas_ports, grp, octstr_imm("nas-ports"))) == -1) {
402  nas_ports = RADIUS_NAS_PORTS;
403  }
404 
405  if ((secret_nas = cfg_get(grp, octstr_imm("secret-nas"))) == NULL) {
406  panic(0, "RADIUS: No shared secret `secret-nas' for NAS in `radius-acct' provided.");
407  }
408 
409  unified_prefix = cfg_get(grp, octstr_imm("unified-prefix"));
410 
411  info(0, "RADIUS: local RADIUS accounting proxy at <%s:%ld>",
412  octstr_get_cstr(our_host), our_port);
413  if (remote_host == NULL) {
414  info(0, "RADIUS: remote RADIUS accounting server is absent");
415  } else {
416  info(0, "RADIUS: remote RADIUS accounting server at <%s:%ld>",
417  octstr_get_cstr(remote_host), remote_port);
418  }
419 
420  info(0, "RADIUS: initializing internal hash tables with %ld buckets.", nas_ports);
421 
422  radius_mutex = mutex_create();
423 
424  /* init hash tables */
425  radius_table = dict_create(nas_ports, (void (*)(void *))octstr_destroy);
426  session_table = dict_create(nas_ports, (void (*)(void *))octstr_destroy);
427  client_table = dict_create(nas_ports, (void (*)(void *))octstr_destroy);
428 
430 }
431 
433 {
434  if (radius_mutex == NULL) /* haven't init'ed at all */
435  return ;
436 
437  mutex_lock(radius_mutex);
438  run_thread = 0;
439  mutex_unlock(radius_mutex);
440 
442 
443  dict_destroy(radius_table);
444  dict_destroy(session_table);
445  dict_destroy(client_table);
446 
447  mutex_destroy(radius_mutex);
448 
449  octstr_destroy(our_host);
450  octstr_destroy(remote_host);
451  octstr_destroy(secret_nas);
452  octstr_destroy(secret_radius);
453  octstr_destroy(unified_prefix);
454 
455  info(0, "RADIUS: accounting proxy stopped.");
456 }
Dict * dict_create(long size_hint, void(*destroy_value)(void *))
Definition: dict.c:192
void error(int err, const char *fmt,...)
Definition: log.c:612
void info(int err, const char *fmt,...)
Definition: log.c:636
static long our_port
Definition: radius_acct.c:87
Octstr * radius_acct_get_msisdn(Octstr *client_ip)
Definition: radius_acct.c:363
static Mutex * radius_mutex
Definition: radius_acct.c:78
void dict_put(Dict *dict, Octstr *key, void *value)
Definition: dict.c:240
#define mutex_unlock(m)
Definition: thread.h:136
RADIUS_PDU * radius_pdu_unpack(Octstr *data_without_len)
Definition: radius_pdu.c:360
static Octstr * unified_prefix
Definition: radius_acct.c:96
union RADIUS_PDU::@72 u
int udp_get_port(Octstr *addr)
Definition: socket.c:547
Dict * attr
Definition: radius_pdu.h:93
#define mutex_create()
Definition: thread.h:96
int type
Definition: smsc_cimd2.c:215
static int update_tables(RADIUS_PDU *pdu)
Definition: radius_acct.c:110
#define cfg_get(grp, varname)
Definition: cfg.h:86
static void proxy_thread(void *arg)
Definition: radius_acct.c:215
static Octstr * secret_radius
Definition: radius_acct.c:93
void radius_acct_init(CfgGroup *grp)
Definition: radius_acct.c:384
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
void gwthread_join_every(gwthread_func_t *func)
void radius_pdu_destroy(RADIUS_PDU *pdu)
Definition: radius_pdu.c:156
static Octstr * our_host
Definition: radius_acct.c:86
int radius_authenticate_pdu(RADIUS_PDU *pdu, Octstr **data, Octstr *secret)
Definition: radius_pdu.c:416
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:281
void * dict_remove(Dict *dict, Octstr *key)
Definition: dict.c:307
int udp_bind(int port, const char *source_addr)
Definition: socket.c:478
int udp_client_socket(void)
Definition: socket.c:464
void * dict_get(Dict *dict, Octstr *key)
Definition: dict.c:286
#define RADIUS_NAS_PORTS
Definition: radius_acct.c:76
Definition: dict.c:116
#define octstr_duplicate(ostr)
Definition: octstr.h:187
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
long dict_key_count(Dict *dict)
Definition: dict.c:335
static Dict * session_table
Definition: radius_acct.c:72
void warning(int err, const char *fmt,...)
Definition: log.c:624
static int run_thread
Definition: radius_acct.c:79
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
void radius_acct_shutdown(void)
Definition: radius_acct.c:432
void mutex_destroy(Mutex *mutex)
Definition: thread.c:97
void dict_destroy(Dict *dict)
Definition: dict.c:215
RADIUS_PDU * radius_pdu_create(int type, RADIUS_PDU *req)
Definition: radius_pdu.c:123
Definition: octstr.c:118
static Dict * radius_table
Definition: radius_acct.c:71
int read_available(int fd, long wait_usec)
Definition: socket.c:406
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:690
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:739
#define panic
Definition: log.h:87
Definition: cfg.c:73
int normalize_number(char *dial_prefixes, Octstr **number)
Definition: utils.c:882
static Octstr * secret_nas
Definition: radius_acct.c:92
static long remote_timeout
Definition: radius_acct.c:99
Definition: thread.h:76
static long remote_port
Definition: radius_acct.c:89
int udp_sendto(int s, Octstr *datagram, Octstr *addr)
Definition: socket.c:567
Octstr * radius_pdu_pack(RADIUS_PDU *pdu)
Definition: radius_pdu.c:237
const char * type_name
Definition: radius_pdu.h:92
#define mutex_lock(m)
Definition: thread.h:130
static Octstr * remote_host
Definition: radius_acct.c:88
static Dict * client_table
Definition: radius_acct.c:73
int udp_recvfrom(int s, Octstr **datagram, Octstr **addr)
Definition: socket.c:582
Octstr * udp_create_address(Octstr *host_or_ip, int port)
Definition: socket.c:517
Octstr * udp_get_ip(Octstr *addr)
Definition: socket.c:557
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:869
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.