Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

radius_acct.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * radius_acct.c - RADIUS accounting proxy thread
00059  *
00060  * Stipe Tolj <stolj@kannel.org>
00061  */
00062 
00063 #include <string.h>
00064 #include <fcntl.h>
00065 #include <errno.h>
00066 
00067 #include "gwlib/gwlib.h"
00068 #include "radius/radius_acct.h"
00069 #include "radius/radius_pdu.h"
00070 
00071 static Dict *radius_table = NULL;      /* maps client ip -> msisdn */
00072 static Dict *session_table = NULL;     /* maps session id -> client ip */
00073 static Dict *client_table = NULL;      /* maps client ip -> session id */
00074 
00075 /* we will initialize hash tables in the size of our NAS ports */
00076 #define RADIUS_NAS_PORTS    30
00077 
00078 static Mutex *radius_mutex = NULL;
00079 static int run_thread = 0;
00080 
00081 /*
00082  * Beware that the official UDP port for RADIUS accounting packets 
00083  * is 1813 (according to RFC2866). The previously used port 1646 has
00084  * been conflicting with an other protocol and "should" not be used.
00085  */
00086 static Octstr *our_host = NULL;
00087 static long our_port = 1813;
00088 static Octstr *remote_host = NULL;
00089 static long remote_port = 1813;
00090 
00091 /* the shared secrets for NAS and remote RADIUS communication */
00092 static Octstr *secret_nas = NULL;
00093 static Octstr *secret_radius = NULL;
00094 
00095 /* the global unified-prefix list */
00096 static Octstr *unified_prefix = NULL;
00097 
00098 /* timeout in msec for the remote RADIUS responses */
00099 static long remote_timeout = 40000;
00100 
00101 /*************************************************************************
00102  *
00103  */
00104 
00105 /*
00106  * Updates the internal RADIUS mapping tables. Returns 1 if the 
00107  * mapping has been processes and the PDU should be proxied to the
00108  * remote RADIUS server, otherwise if it is a duplicate returns 0.
00109  */
00110 static int update_tables(RADIUS_PDU *pdu)
00111 {
00112     Octstr *client_ip, *msisdn;
00113     Octstr *type, *session_id;
00114     int ret = 0;
00115     Octstr *rm_item;
00116 
00117     client_ip = msisdn = type = session_id = NULL;
00118 
00119     /* only add if we have a Accounting-Request PDU */
00120     if (pdu->type == 0x04) {
00121 
00122         /* check if we have a START or STOP event */
00123         type = dict_get(pdu->attr, octstr_imm("Acct-Status-Type"));
00124 
00125         /* get the sesion id */
00126         session_id = dict_get(pdu->attr, octstr_imm("Acct-Session-Id"));
00127 
00128         /* grep the needed data */
00129         client_ip = dict_get(pdu->attr, octstr_imm("Framed-IP-Address"));
00130         msisdn = dict_get(pdu->attr, octstr_imm("Calling-Station-Id"));
00131 
00132         /* we can't add mapping without both components */
00133         if (client_ip == NULL || msisdn == NULL) {
00134             warning(0, "RADIUS: NAS did either not send 'Framed-IP-Address' or/and "
00135                     "'Calling-Station-Id', dropping mapping but will forward.");
00136             /* anyway forward the packet to remote RADIUS server */
00137             return 1;
00138         }
00139 
00140         if (octstr_compare(type, octstr_imm("1")) == 0 && session_id && msisdn) {
00141             /* session START */
00142             if (dict_get(radius_table, client_ip) == NULL &&
00143                 dict_get(session_table, session_id) == NULL) {
00144                 Octstr *put_msisdn = octstr_duplicate(msisdn);
00145                 Octstr *put_client_ip = octstr_duplicate(client_ip);
00146                 Octstr *put_session_id = octstr_duplicate(session_id);
00147                 Octstr *old_session_id, *old_client_ip;
00148 
00149                 /* ok, this is a new session. If it contains an IP that is still
00150                  * in the session/client tables then remove the old session from the
00151                  * two tables session/client */
00152                 if ((old_session_id = dict_get(client_table, client_ip)) != NULL &&
00153                     (old_client_ip = dict_get(session_table, old_session_id)) != NULL &&
00154                     octstr_compare(old_session_id, session_id) != 0) {
00155                     rm_item = dict_remove(client_table, client_ip);
00156                     octstr_destroy(rm_item);
00157                     rm_item = dict_remove(session_table, old_session_id);
00158                     octstr_destroy(rm_item);
00159                     octstr_destroy(old_session_id);
00160                     octstr_destroy(old_client_ip);
00161                 }
00162 
00163                 /* insert both, new client IP and session to mapping tables */
00164                 dict_put(radius_table, client_ip, put_msisdn);
00165                 dict_put(session_table, session_id, put_client_ip);
00166                 dict_put(client_table, client_ip, put_session_id);
00167 
00168                 info(0, "RADIUS: Mapping `%s <-> %s' for session id <%s> added.",
00169                      octstr_get_cstr(client_ip), octstr_get_cstr(msisdn),
00170                      octstr_get_cstr(session_id));
00171                 ret = 1;
00172             } else {
00173                 warning(0, "RADIUS: Duplicate mapping `%s <-> %s' for session "
00174                         "id <%s> received, ignoring.",
00175                         octstr_get_cstr(client_ip), octstr_get_cstr(msisdn),
00176                         octstr_get_cstr(session_id));
00177             }
00178         } else if (octstr_compare(type, octstr_imm("2")) == 0) {
00179             /* session STOP */
00180             Octstr *comp_client_ip;
00181             if ((msisdn = dict_get(radius_table, client_ip)) != NULL &&
00182                 (comp_client_ip = dict_get(session_table, session_id)) != NULL &&
00183                 octstr_compare(client_ip, comp_client_ip) == 0) {
00184                 dict_remove(radius_table, client_ip);
00185                 rm_item = dict_remove(client_table, client_ip);
00186                 octstr_destroy(rm_item);
00187                 dict_remove(session_table, session_id);
00188                 info(0, "RADIUS: Mapping `%s <-> %s' for session id <%s> removed.",
00189                      octstr_get_cstr(client_ip), octstr_get_cstr(msisdn),
00190                      octstr_get_cstr(session_id));
00191                 octstr_destroy(msisdn);
00192                 octstr_destroy(comp_client_ip);
00193 
00194                 ret = 1;
00195             } else {
00196                 warning(0, "RADIUS: Could not find mapping for `%s' session "
00197                         "id <%s>, ignoring.",
00198                         octstr_get_cstr(client_ip), octstr_get_cstr(session_id));
00199             }
00200 
00201         } else {
00202             error(0, "RADIUS: unknown Acct-Status-Type `%s' received, ignoring.",
00203                   octstr_get_cstr(type));
00204         }
00205     }
00206 
00207     return ret;
00208 }
00209 
00210 
00211 /*************************************************************************
00212  * The main proxy thread.
00213  */
00214 
00215 static void proxy_thread(void *arg)
00216 {
00217     int ss, cs; /* server and client sockets */
00218     int fl; /* socket flags */
00219     Octstr *addr = NULL;
00220     int forward;
00221     Octstr *tmp;
00222 
00223     run_thread = 1;
00224     ss = cs = -1;
00225 
00226     /* create client binding, only if we have a remote server
00227      * and make the client socet non-blocking */
00228     if (remote_host != NULL) {
00229         cs = udp_client_socket();
00230         fl = fcntl(cs, F_GETFL);
00231         fcntl(cs, F_SETFL, fl | O_NONBLOCK);
00232         addr = udp_create_address(remote_host, remote_port);
00233     }
00234 
00235     /* create server binding */
00236     ss = udp_bind(our_port, octstr_get_cstr(our_host));
00237 
00238     /* make the server socket non-blocking */
00239     fl = fcntl(ss, F_GETFL);
00240     fcntl(ss, F_SETFL, fl | O_NONBLOCK);
00241 
00242     if (ss == -1)
00243         panic(0, "RADIUS: Couldn't set up server socket for port %ld.", our_port);
00244 
00245     while (run_thread) {
00246         RADIUS_PDU *pdu, *r;
00247         Octstr *data, *rdata;
00248         Octstr *from_nas, *from_radius;
00249 
00250         pdu = r = NULL;
00251         data = rdata = from_nas = from_radius = NULL;
00252         
00253         if (read_available(ss, 100000) < 1)
00254             continue;
00255 
00256         /* get request from NAS */
00257         if (udp_recvfrom(ss, &data, &from_nas) == -1) {
00258             if (errno == EAGAIN)
00259                 /* No datagram available, don't block. */
00260                 continue;
00261 
00262             error(0, "RADIUS: Couldn't receive request data from NAS");
00263             continue;
00264         }
00265 
00266         tmp = udp_get_ip(from_nas);
00267         info(0, "RADIUS: Got data from NAS <%s:%d>",
00268              octstr_get_cstr(tmp), udp_get_port(from_nas));
00269         octstr_destroy(tmp);
00270         octstr_dump(data, 0);
00271 
00272         /* unpacking the RADIUS PDU */
00273         if ((pdu = radius_pdu_unpack(data)) == NULL) {
00274             warning(0, "RADIUS: Couldn't unpack PDU from NAS, ignoring.");
00275             goto error;
00276         }
00277         info(0, "RADIUS: from NAS: PDU type: %s", pdu->type_name);
00278 
00279         /* authenticate the Accounting-Request packet */
00280         if (radius_authenticate_pdu(pdu, &data, secret_nas) == 0) {
00281             warning(0, "RADIUS: Authentication failed for PDU from NAS, ignoring.");
00282             goto error;
00283         }
00284 
00285         /* store to hash table if not present yet */
00286         mutex_lock(radius_mutex);
00287         forward = update_tables(pdu);
00288         mutex_unlock(radius_mutex);
00289 
00290         /* create response PDU for NAS */
00291         r = radius_pdu_create(0x05, pdu);
00292 
00293         /*
00294          * create response authenticator 
00295          * code+identifier(req)+length+authenticator(req)+(attributes)+secret 
00296          */
00297         r->u.Accounting_Response.identifier = pdu->u.Accounting_Request.identifier;
00298         r->u.Accounting_Response.authenticator =
00299             octstr_duplicate(pdu->u.Accounting_Request.authenticator);
00300 
00301         /* pack response for NAS */
00302         rdata = radius_pdu_pack(r);
00303 
00304         /* creates response autenticator in encoded PDU */
00305         radius_authenticate_pdu(r, &rdata, secret_nas);
00306 
00307         /* 
00308          * forward request to remote RADIUS server only if updated
00309          * and if we have a configured remote RADIUS server 
00310          */
00311         if ((remote_host != NULL) && forward) {
00312             if (udp_sendto(cs, data, addr) == -1) {
00313                 error(0, "RADIUS: Couldn't send to remote RADIUS <%s:%ld>.",
00314                       octstr_get_cstr(remote_host), remote_port);
00315             } else 
00316             if (read_available(cs, remote_timeout) < 1) {
00317                 error(0, "RADIUS: Timeout for response from remote RADIUS <%s:%ld>.",
00318                       octstr_get_cstr(remote_host), remote_port);
00319             } else 
00320             if (udp_recvfrom(cs, &data, &from_radius) == -1) {
00321                 error(0, "RADIUS: Couldn't receive from remote RADIUS <%s:%ld>.",
00322                       octstr_get_cstr(remote_host), remote_port);
00323             } else {
00324                 info(0, "RADIUS: Got data from remote RADIUS <%s:%d>.",
00325                      octstr_get_cstr(udp_get_ip(from_radius)), udp_get_port(from_radius));
00326                 octstr_dump(data, 0);
00327 
00328                 /* XXX unpack the response PDU and check if the response
00329                  * authenticator is valid */
00330             }
00331         }
00332 
00333         /* send response to NAS */
00334         if (udp_sendto(ss, rdata, from_nas) == -1)
00335             error(0, "RADIUS: Couldn't send response data to NAS <%s:%d>.",
00336                   octstr_get_cstr(udp_get_ip(from_nas)), udp_get_port(from_nas));
00337 
00338 error:
00339         radius_pdu_destroy(pdu);
00340         radius_pdu_destroy(r);
00341 
00342         octstr_destroy(rdata);
00343         octstr_destroy(data);
00344         octstr_destroy(from_nas);
00345 
00346         debug("radius.proxy", 0, "RADIUS: Mapping table contains %ld elements",
00347               dict_key_count(radius_table));
00348         debug("radius.proxy", 0, "RADIUS: Session table contains %ld elements",
00349               dict_key_count(session_table));
00350         debug("radius.proxy", 0, "RADIUS: Client table contains %ld elements",
00351               dict_key_count(client_table));
00352 
00353     }
00354 
00355     octstr_destroy(addr);
00356 }
00357 
00358 
00359 /*************************************************************************
00360  * Public functions: init, shutdown, mapping.
00361  */
00362 
00363 Octstr *radius_acct_get_msisdn(Octstr *client_ip)
00364 {
00365     Octstr *m, *r;
00366     char *uf;
00367 
00368     /* if no proxy thread is running, then pass NULL as result */
00369     if (radius_table == NULL || client_ip == NULL)
00370         return NULL;
00371 
00372     mutex_lock(radius_mutex);
00373     m = dict_get(radius_table, client_ip);
00374     mutex_unlock(radius_mutex);
00375     r = m ? octstr_duplicate(m) : NULL;
00376 
00377     /* apply number normalization */
00378     uf = unified_prefix ? octstr_get_cstr(unified_prefix) : NULL;
00379     normalize_number(uf, &r);
00380 
00381     return r;
00382 }
00383 
00384 void radius_acct_init(CfgGroup *grp)
00385 {
00386     long nas_ports = 0;
00387 
00388     /* get configured parameters */
00389     if ((our_host = cfg_get(grp, octstr_imm("our-host"))) == NULL) {
00390         our_host = octstr_create("0.0.0.0");
00391     }
00392     if ((remote_host = cfg_get(grp, octstr_imm("remote-host"))) != NULL) {
00393         cfg_get_integer(&remote_port, grp, octstr_imm("remote-port"));
00394         if ((secret_radius = cfg_get(grp, octstr_imm("secret-radius"))) == NULL) {
00395             panic(0, "RADIUS: No shared secret `secret-radius' for remote RADIUS in `radius-acct' provided.");
00396         }
00397     }
00398     cfg_get_integer(&our_port, grp, octstr_imm("our-port"));
00399     cfg_get_integer(&remote_timeout, grp, octstr_imm("remote-timeout"));
00400 
00401     if ((cfg_get_integer(&nas_ports, grp, octstr_imm("nas-ports"))) == -1) {
00402         nas_ports = RADIUS_NAS_PORTS;
00403     }
00404 
00405     if ((secret_nas = cfg_get(grp, octstr_imm("secret-nas"))) == NULL) {
00406         panic(0, "RADIUS: No shared secret `secret-nas' for NAS in `radius-acct' provided.");
00407     }
00408 
00409     unified_prefix = cfg_get(grp, octstr_imm("unified-prefix"));
00410 
00411     info(0, "RADIUS: local RADIUS accounting proxy at <%s:%ld>",
00412          octstr_get_cstr(our_host), our_port);
00413     if (remote_host == NULL) {
00414         info(0, "RADIUS: remote RADIUS accounting server is absent");
00415     } else {
00416         info(0, "RADIUS: remote RADIUS accounting server at <%s:%ld>",
00417              octstr_get_cstr(remote_host), remote_port);
00418     }
00419 
00420     info(0, "RADIUS: initializing internal hash tables with %ld buckets.", nas_ports);
00421 
00422     radius_mutex = mutex_create();
00423 
00424     /* init hash tables */
00425     radius_table = dict_create(nas_ports, (void (*)(void *))octstr_destroy);
00426     session_table = dict_create(nas_ports, (void (*)(void *))octstr_destroy);
00427     client_table = dict_create(nas_ports, (void (*)(void *))octstr_destroy);
00428 
00429     gwthread_create(proxy_thread, NULL);
00430 }
00431 
00432 void radius_acct_shutdown(void)
00433 {
00434     if (radius_mutex == NULL) /* haven't init'ed at all */
00435         return ;
00436 
00437     mutex_lock(radius_mutex);
00438     run_thread = 0;
00439     mutex_unlock(radius_mutex);
00440 
00441     gwthread_join_every(proxy_thread);
00442 
00443     dict_destroy(radius_table);
00444     dict_destroy(session_table);
00445     dict_destroy(client_table);
00446 
00447     mutex_destroy(radius_mutex);
00448 
00449     octstr_destroy(our_host);
00450     octstr_destroy(remote_host);
00451     octstr_destroy(secret_nas);
00452     octstr_destroy(secret_radius);
00453     octstr_destroy(unified_prefix);
00454 
00455     info(0, "RADIUS: accounting proxy stopped.");
00456 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.