obsolete/ratelimit/ratelimit.c

00001 /*
00002  * $Id$
00003  *
00004  * ratelimit module
00005  *
00006  * Copyright (C) 2006 Hendrik Scholz <hscholz@raisdorf.net>
00007  *
00008  * This file is part of ser, a free SIP server.
00009  *
00010  * ser is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version
00014  *
00015  * For a license to use the ser software under conditions
00016  * other than those described here, or to purchase support for this
00017  * software, please contact iptel.org by e-mail at the following addresses:
00018  *    info@iptel.org
00019  *
00020  * ser is distributed in the hope that it will be useful,
00021  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023  * GNU General Public License for more details.
00024  *
00025  * You should have received a copy of the GNU General Public License
00026  * along with this program; if not, write to the Free Software
00027  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00028  */
00029 
00030 #include <stdio.h>
00031 
00032 #include "../../mem/shm_mem.h"
00033 #include "../../sr_module.h"
00034 #include "../../dprint.h"
00035 #include "../../timer.h"
00036 #include "../../ut.h"
00037 
00038 MODULE_VERSION
00039 
00040 /* use RED queuing algorithm? */
00041 #define RL_WITH_RED     1
00042 /* timer interval length in seconds, tunable via modparam */
00043 #define RL_TIMER_INTERVAL 10
00044 
00045 #ifndef rpc_lf
00046 #define rpc_lf(rpc, c)  rpc->add(c, "s","")
00047 #endif
00048 
00049 /* globally visible parameters tunable via modparam and RPC interface */
00050 int *invite_limit, *register_limit, *subscribe_limit = NULL;
00051 
00052 /* storage for initial modparam values */
00053 int invite_limit_mp = 0;
00054 int register_limit_mp = 0;
00055 int subscribe_limit_mp = 0;
00056 int timer_interval = RL_TIMER_INTERVAL; /* in seconds */
00057 
00058 /* internal counters */
00059 int *invite_counter = NULL;
00060 int *register_counter = NULL;
00061 int *subscribe_counter = NULL;
00062 
00063 #if defined(RL_WITH_RED)
00064 /* load levels for Random Early Detection algorithm */
00065 int *invite_load = NULL;
00066 int *register_load = NULL;
00067 int *subscribe_load = NULL;
00068 #endif
00069 
00071 static int mod_init(void);
00072 static int child_init(int);
00073 static int rl_check(struct sip_msg*, char *, char *);
00074 #if defined (RL_WITH_RED)
00075 static int rl_limit_check(int, int, int);
00076 #else
00077 static int rl_limit_check(int, int);
00078 #endif
00079 static void timer(unsigned int, void *);
00080 static void destroy(void);
00081 
00082 static rpc_export_t rpc_methods[];
00083 
00084 static cmd_export_t cmds[]={
00085         {"rl_check", rl_check, 0, 0, REQUEST_ROUTE},
00086         {0,0,0,0,0}
00087 };
00088 
00089 static param_export_t params[]={
00090         {"invite_limit",        PARAM_INT, &invite_limit_mp},
00091         {"register_limit",      PARAM_INT, &register_limit_mp},
00092         {"subscribe_limit",     PARAM_INT, &subscribe_limit_mp},
00093         {"timer_interval",      PARAM_INT, &timer_interval},
00094         {0,0,0}
00095 };
00096 
00098 struct module_exports exports= {
00099         "ratelimit",
00100         cmds,
00101         rpc_methods,
00102         params, 
00103         mod_init,   /* module initialization function */
00104         (response_function) 0,
00105         (destroy_function) destroy,
00106         0,
00107         child_init  /* per-child init function */
00108 };
00109 
00110 /* initialize ratelimit module */
00111 static int mod_init(void)
00112 {
00113         DBG("RATELIMIT: initializing ...\n");
00114 
00115         /* register timer to reset counters */
00116         if (register_timer(timer, 0, timer_interval) < 0) {
00117                 LOG(L_ERR, "RATELIMIT:ERROR: could not register timer function\n");
00118                 return -1;
00119         }
00120 
00121         invite_counter = shm_malloc(sizeof(int));
00122         register_counter = shm_malloc(sizeof(int));
00123         subscribe_counter = shm_malloc(sizeof(int));
00124         if (!invite_counter || !register_counter || !subscribe_counter) {
00125                 LOG(L_ERR, "RATELIMIT:ERROR: no memory for counters\n");
00126                 return -1;
00127         }
00128         *invite_counter = 0;
00129         *register_counter = 0;
00130         *subscribe_counter = 0;
00131 
00132         invite_limit = shm_malloc(sizeof(int));
00133         register_limit = shm_malloc(sizeof(int));
00134         subscribe_limit = shm_malloc(sizeof(int));
00135         if (!invite_limit || !register_limit || !subscribe_limit) {
00136                 LOG(L_ERR, "RATELIMIT:ERROR: no memory for limit settings\n");
00137                 return -1;
00138         }
00139         /* obtain limits from modparam */
00140         *invite_limit = invite_limit_mp;
00141         *register_limit = register_limit_mp;
00142         *subscribe_limit = subscribe_limit_mp;
00143 
00144 
00145 #if defined (RL_WITH_RED)
00146         /* these are only needed when using RED */
00147         invite_load = shm_malloc(sizeof(int));
00148         register_load = shm_malloc(sizeof(int));
00149         subscribe_load = shm_malloc(sizeof(int));
00150         if (!invite_load || !register_load || !subscribe_load) {
00151                 LOG(L_ERR, "RATELIMIT:ERROR: no memory for load levels\n");
00152                 return -1;
00153         }
00154         *invite_load = -1; /* -1 = first run identifier */
00155         *register_load = -1;
00156         *subscribe_load = -1;
00157 #endif
00158 
00159         return 0;
00160 }
00161 
00162 /* generic SER module functions */
00163 static int child_init(int rank)
00164 {
00165         DBG("RATELIMIT:init_child #%d / pid <%d>\n", rank, getpid());
00166         return 0;
00167 }
00168 static void destroy(void)
00169 {
00170         DBG("RATELIMIT: destroy module ...\n");
00171 }
00172 
00173 /* ratelimit check 
00174  *
00175  * return values:
00176  *  -1: over limit (aka too many messages of that request type)
00177  *   0: internal error (not a request)
00178  *   1: within limit (let message through)
00179  */
00180 
00181 static int rl_check(struct sip_msg* msg, char *_foo, char *_bar) {
00182 
00183         DBG("RATELIMIT:rl_check:invoked\n");
00184 
00185         if (msg->first_line.type != SIP_REQUEST) {
00186                 DBG("RATELIMIT:rl_check:not a request\n");
00187                 return 0;
00188         }
00189 
00190         if (msg->first_line.u.request.method_value == METHOD_INVITE) {
00191                 if (*invite_limit == 0) 
00192                         return 1;
00193                 *invite_counter = *invite_counter + 1;
00194 #if defined(RL_WITH_RED)
00195                 return rl_limit_check(*invite_counter, *invite_limit, *invite_load);
00196 #else
00197                 return rl_limit_check(*invite_counter, *invite_limit);
00198 #endif
00199         } else if (msg->first_line.u.request.method_value == METHOD_REGISTER) {
00200                 if (*register_limit == 0) 
00201                         return 1;
00202                 *register_counter = *register_counter + 1;
00203 #if defined(RL_WITH_RED)
00204                 return rl_limit_check(*register_counter, *register_limit,
00205                         *register_load);
00206 #else
00207                 return rl_limit_check(*register_counter, *register_limit);
00208 #endif
00209         } else if (msg->first_line.u.request.method_value == METHOD_SUBSCRIBE) {
00210                 if (*subscribe_limit == 0) 
00211                         return 1;
00212                 *subscribe_counter = *subscribe_counter + 1;
00213 #if defined(RL_WITH_RED)
00214                 return rl_limit_check(*subscribe_counter, *subscribe_limit,
00215                         *subscribe_load);
00216 #else
00217                 return rl_limit_check(*subscribe_counter, *subscribe_limit);
00218 #endif
00219         } else {
00220                 return 0;
00221         }
00222         return -1;
00223 }
00224 
00225 
00226 /* timer housekeeping, invoked each timer interval to reset counters */
00227 static void timer(unsigned int ticks, void *param) {
00228     DBG("RATELIMIT:timer:invoked\n");
00229 
00230 #if defined(RL_WITH_RED)
00231         /* calculate load levels for RED */
00232         if (*invite_limit > 0) {
00233                 if (*invite_counter < *invite_limit)
00234                         *invite_load = 0;
00235                 else 
00236                         *invite_load = (int) (*invite_counter / *invite_limit);
00237         }
00238         if (*register_limit > 0) {
00239                 if (*register_counter < *register_limit)
00240                         *register_load = 0;
00241                 else 
00242                         *register_load = (int) (*register_counter / *register_limit);
00243         }
00244         if (*subscribe_limit > 0) {
00245                 if (*subscribe_counter < *subscribe_limit)
00246                         *subscribe_load = 0;
00247                 else 
00248                         *subscribe_load = (int) (*subscribe_counter / *subscribe_limit);
00249         }
00250 #endif
00251 
00252         /* clear counters */
00253         if (*invite_limit > 0) *invite_counter = 0;
00254         if (*register_limit > 0) *register_counter = 0;
00255         if (*subscribe_limit > 0) *subscribe_counter = 0;
00256 }
00257 
00258 #if defined(RL_WITH_RED)
00259 
00260 /* rl_limit_check() RED implementation
00261  *
00262  * RED (Random Early Detection) is a queue management algorithm that tries
00263  * to counter the usual tail drop issues as seen in the trivial implementation
00264  * below.
00265  *
00266  * We monitor the load level and start dropping messages early on to achieve
00267  * an even utilization throughout the timer interval.
00268  *
00269  * The algorithm has no load indication during the first interval so we revert
00270  * to 'tail drop' for a simple start.
00271  *
00272  */
00273 
00274 static int rl_limit_check(int cnt, int limit, int load) {
00275 
00276         DBG("RATELIMIT:rl_limit_check: invoked\n");
00277 
00278         /* first run? */
00279         if (load == -1) return (cnt > limit) ? -1 : 1;
00280 
00281         /* low load, no drops */
00282         if (load <= 1) return 1;
00283 
00284         /* RED implementation, every load'th packet is let through */
00285         return (!(cnt % load)) ? 1 : -1;
00286 }
00287 
00288 #else
00289 
00290 /*
00291  * rl_limit_check() trivial implementation (aka tail drop)
00292  *
00293  * check if counter is above limit and in that case return -1.
00294  * Returns 1 if still within limit.
00295  *
00296  * Caveats:
00297  *
00298  *  If the timer interval is too large this 'algorithm' might cause
00299  *  end device synchronization and bad traffic patterns, i.e. full traffic/load
00300  *  in the beginning of the timer interval and no traffic at all once
00301  *  the limit has been reached for the remaining interval.
00302  *
00303  */
00304 
00305 static int rl_limit_check(int cnt, int limit) {
00306         DBG("RATELIMIT:rl_limit_check: invoked\n");
00307         return (cnt > limit) ? -1 : 1;
00308 }
00309 
00310 #endif /* queue management algorithm selection */
00311 
00312 /*
00313  * RPC functions
00314  *
00315  * rpc_stats() dumps the current config/statistics
00316  * rpc_{invite|register|subscribe}() set the limits
00317  * rpc_timer() sets the timer interval length
00318  *
00319  */
00320 
00321 /* rpc function documentation */
00322 static const char *rpc_stats_doc[2] = {
00323         "Print ratelimit statistics", 
00324         0
00325 };
00326 
00327 static const char *rpc_invite_doc[2] = {
00328         "Set INVITEs per timer interval limit",
00329         0
00330 };
00331 static const char *rpc_register_doc[2] = {
00332         "Set REGISTERs per timer interval limit",
00333         0
00334 };
00335 static const char *rpc_subscribe_doc[2] = {
00336         "Set SUBSCRIBEs per timer interval limit",
00337         0
00338 };
00339 static const char *rpc_timer_doc[2] = {
00340         "Set the ratelimit timer_interval length",
00341         0
00342 };
00343 
00344 /* rpc function implementations */
00345 static void rpc_stats(rpc_t *rpc, void *c) {
00346 
00347 #if defined(RL_WITH_RED)
00348         if (rpc->printf(c, "   INVITE: %d/%d (drop rate: %d)", *invite_counter,
00349                 *invite_limit, *invite_load) < 0) return;
00350         rpc_lf(rpc, c);
00351         if (rpc->printf(c, " REGISTER: %d/%d (drop rate: %d)", *register_counter,
00352                 *register_limit, *register_load) < 0) return;
00353         rpc_lf(rpc, c);
00354         if (rpc->printf(c, "SUBSCRIBE: %d/%d (drop rate: %d)", *subscribe_counter,
00355                 *subscribe_limit, *subscribe_load) < 0) return;
00356         rpc_lf(rpc, c);
00357 #else
00358         if (rpc->printf(c, "   INVITE: %d/%d", *invite_counter,
00359                 *invite_limit) < 0) return;
00360         rpc_lf(rpc, c);
00361         if (rpc->printf(c, " REGISTER: %d/%d", *register_counter,
00362                 *register_limit) < 0) return;
00363         rpc_lf(rpc, c);
00364         if (rpc->printf(c, "SUBSCRIBE: %d/%d", *subscribe_counter,
00365                 *subscribe_limit) < 0) return;
00366         rpc_lf(rpc, c);
00367 #endif
00368 
00369 }
00370 static void rpc_invite(rpc_t *rpc, void *c) {
00371 
00372         int limit;
00373         if (rpc->scan(c, "d", &limit) < 1) {
00374                 rpc->fault(c, 400, "Limit expected");
00375                 return;
00376         }
00377         if (limit < 0) {
00378                 rpc->fault(c, 400, "limit must be >= 0 (0 = unlimited)");
00379                 return;
00380         }
00381         DBG("RATELIMIT:setting INVITE limit to %d messages\n", limit);
00382         *invite_limit = limit;
00383 }
00384 static void rpc_register(rpc_t *rpc, void *c) {
00385 
00386         int limit;
00387         if (rpc->scan(c, "d", &limit) < 1) {
00388                 rpc->fault(c, 400, "Limit expected");
00389                 return;
00390         }
00391         if (limit < 0) {
00392                 rpc->fault(c, 400, "limit must be >= 0 (0 = unlimited)");
00393                 return;
00394         }
00395         DBG("RATELIMIT:setting REGISTER limit to %d messages\n", limit);
00396         *register_limit = limit;
00397 }
00398 static void rpc_subscribe(rpc_t *rpc, void *c) {
00399 
00400         int limit;
00401         if (rpc->scan(c, "d", &limit) < 1) {
00402                 rpc->fault(c, 400, "Limit expected");
00403                 return;
00404         }
00405         if (limit < 0) {
00406                 rpc->fault(c, 400, "limit must be >= 0 (0 = unlimited)");
00407                 return;
00408         }
00409         DBG("RATELIMIT:setting SUBSCRIBE limit to %d messages\n", limit);
00410         *subscribe_limit = limit;
00411 }
00412 static void rpc_timer(rpc_t *rpc, void *c) {
00413         rpc->fault(c, 400, "Not yet implemented");
00414 }
00415 
00416 static rpc_export_t rpc_methods[] = {
00417         {"rl.stats",                    rpc_stats,              rpc_stats_doc,          0},
00418         {"rl.invite_limit",             rpc_invite,             rpc_invite_doc,         0},
00419         {"rl.register_limit",   rpc_register,   rpc_register_doc,       0},
00420         {"rl.subscribe_limit",  rpc_subscribe,  rpc_subscribe_doc,      0},
00421         {"rl.timer_interval",   rpc_timer,              rpc_timer_doc,  0},
00422         {0, 0, 0, 0}
00423 };