PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/opensips-1.8.0-beta-tls/modules/ratelimit/ratelimit.c

#
C | 567 lines | 412 code | 83 blank | 72 comment | 58 complexity | ec073b32c8bf5ef4a322b0d44166b8c1 MD5 | raw file
Possible License(s): AGPL-1.0
  1. /*
  2. * $Id: ratelimit.c 8109 2011-06-30 18:16:54Z bogdan_iancu $
  3. *
  4. * ratelimit module
  5. *
  6. * Copyright (C) 2006 Hendrik Scholz <hscholz@raisdorf.net>
  7. * Copyright (C) 2008 Ovidiu Sas <osas@voipembedded.com>
  8. *
  9. * This file is part of opensips, a free SIP server.
  10. *
  11. * opensips is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version
  15. *
  16. * opensips is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24. *
  25. * History:
  26. * ---------
  27. *
  28. * 2008-01-10 ported from SER project (osas)
  29. * 2008-01-16 ported enhancements from openims project (osas)
  30. */
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <sys/types.h>
  34. #include <regex.h>
  35. #include <math.h>
  36. #include "../../sr_module.h"
  37. #include "../../mem/mem.h"
  38. #include "../../mem/shm_mem.h"
  39. #include "../../dprint.h"
  40. #include "../../timer.h"
  41. #include "../../ut.h"
  42. #include "../../locking.h"
  43. #include "../../mod_fix.h"
  44. #include "../../data_lump.h"
  45. #include "../../data_lump_rpl.h"
  46. #include "../../socket_info.h"
  47. #include "../signaling/signaling.h"
  48. #include "ratelimit.h"
  49. /* === these change after startup */
  50. gen_lock_t * rl_lock;
  51. static double * rl_load_value; /* actual load, used by PIPE_ALGO_FEEDBACK */
  52. static double * pid_kp, * pid_ki, * pid_kd, * pid_setpoint; /* PID tuning params */
  53. static int * drop_rate; /* updated by PIPE_ALGO_FEEDBACK */
  54. static int *rl_feedback_limit;
  55. int * rl_network_load; /* network load */
  56. int * rl_network_count; /* flag for counting network algo users */
  57. /* these only change in the mod_init() process -- no locking needed */
  58. int rl_timer_interval = RL_TIMER_INTERVAL;
  59. static str db_url = {0,0};
  60. str db_prefix = str_init("rl_pipe_");
  61. /* === */
  62. #ifndef RL_DEBUG_LOCKS
  63. # define LOCK_GET lock_get
  64. # define LOCK_RELEASE lock_release
  65. #else
  66. # define LOCK_GET(l) do { \
  67. LM_INFO("%d: + get\n", __LINE__); \
  68. lock_get(l); \
  69. LM_INFO("%d: - get\n", __LINE__); \
  70. } while (0)
  71. # define LOCK_RELEASE(l) do { \
  72. LM_INFO("%d: + release\n", __LINE__); \
  73. lock_release(l); \
  74. LM_INFO("%d: - release\n", __LINE__); \
  75. } while (0)
  76. #endif
  77. /* module functions */
  78. static int mod_init(void);
  79. static int mod_child(int);
  80. /* fixup prototype */
  81. static int fixup_rl_check(void **param, int param_no);
  82. struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param);
  83. struct mi_root* mi_reset_pipe(struct mi_root* cmd_tree, void* param);
  84. struct mi_root* mi_set_pid(struct mi_root* cmd_tree, void* param);
  85. struct mi_root* mi_get_pid(struct mi_root* cmd_tree, void* param);
  86. static cmd_export_t cmds[] = {
  87. {"rl_check", (cmd_function)w_rl_check_2, 2,
  88. fixup_rl_check, 0, REQUEST_ROUTE|LOCAL_ROUTE},
  89. {"rl_check", (cmd_function)w_rl_check_3, 3,
  90. fixup_rl_check, 0, REQUEST_ROUTE|LOCAL_ROUTE},
  91. {"rl_dec_count", (cmd_function)w_rl_dec, 1,
  92. fixup_spve_null, 0, REQUEST_ROUTE|LOCAL_ROUTE},
  93. {"rl_reset_count", (cmd_function)w_rl_reset, 1,
  94. fixup_spve_null, 0, REQUEST_ROUTE|LOCAL_ROUTE},
  95. {0,0,0,0,0,0}
  96. };
  97. static param_export_t params[] = {
  98. { "timer_interval", INT_PARAM, &rl_timer_interval},
  99. { "expire_time", INT_PARAM, &rl_expire_time},
  100. { "hash_size", INT_PARAM, &rl_hash_size},
  101. { "default_algorithm", STR_PARAM, &rl_default_algo_s.s},
  102. { "cachedb_url", STR_PARAM, &db_url.s},
  103. { "db_prefix", STR_PARAM, &db_prefix.s},
  104. { 0, 0, 0}
  105. };
  106. #define RLH1 "Params: [pipe] ; Lists the parameters and variabiles in the " \
  107. "ratelimit module; If no pipe is specified, all existing pipes are listed."
  108. #define RLH2 "Params: pipe ; Resets the counter of a specified pipe."
  109. #define RLH3 "Params: ki kp kd ; Sets the PID Controller parameters for the " \
  110. "Feedback Algorithm."
  111. #define RLH4 "Params: none ; Gets the list of in use PID Controller parameters."
  112. static mi_export_t mi_cmds [] = {
  113. {"rl_list", RLH1, mi_stats, 0, 0, 0},
  114. {"rl_reset_pipe", RLH2, mi_reset_pipe, 0, 0, 0},
  115. {"rl_set_pid", RLH3, mi_set_pid, 0, 0, 0},
  116. {"rl_get_pid", RLH4, mi_get_pid, MI_NO_INPUT_FLAG, 0, 0},
  117. {0,0,0,0,0,0}
  118. };
  119. struct module_exports exports= {
  120. "ratelimit",
  121. MODULE_VERSION,
  122. DEFAULT_DLFLAGS, /* dlopen flags */
  123. cmds,
  124. params,
  125. 0, /* exported statistics */
  126. mi_cmds, /* exported MI functions */
  127. 0, /* exported pseudo-variables */
  128. 0, /* extra processes */
  129. mod_init, /* module initialization function */
  130. 0,
  131. mod_destroy, /* module exit function */
  132. mod_child /* per-child init function */
  133. };
  134. /* not using /proc/loadavg because it only works when our_timer_interval == theirs */
  135. int get_cpuload(void)
  136. {
  137. static
  138. long long o_user, o_nice, o_sys, o_idle, o_iow, o_irq, o_sirq, o_stl;
  139. long long n_user, n_nice, n_sys, n_idle, n_iow, n_irq, n_sirq, n_stl;
  140. static int first_time = 1;
  141. int scan_res;
  142. FILE * f = fopen("/proc/stat", "r");
  143. if (! f)
  144. return -1;
  145. scan_res = fscanf(f, "cpu %lld%lld%lld%lld%lld%lld%lld%lld",
  146. &n_user, &n_nice, &n_sys, &n_idle, &n_iow, &n_irq, &n_sirq, &n_stl);
  147. fclose(f);
  148. if (scan_res <= 0) {
  149. LM_ERR("/proc/stat didn't contain expected values");
  150. return -1;
  151. }
  152. if (first_time) {
  153. first_time = 0;
  154. *rl_load_value = 0;
  155. } else {
  156. long long d_total = (n_user - o_user) +
  157. (n_nice - o_nice) +
  158. (n_sys - o_sys) +
  159. (n_idle - o_idle) +
  160. (n_iow - o_iow) +
  161. (n_irq - o_irq) +
  162. (n_sirq - o_sirq) +
  163. (n_stl - o_stl);
  164. long long d_idle = (n_idle - o_idle);
  165. *rl_load_value = 1.0 - ((double)d_idle) / (double)d_total;
  166. }
  167. o_user = n_user;
  168. o_nice = n_nice;
  169. o_sys = n_sys;
  170. o_idle = n_idle;
  171. o_iow = n_iow;
  172. o_irq = n_irq;
  173. o_sirq = n_sirq;
  174. o_stl = n_stl;
  175. return 0;
  176. }
  177. static double int_err = 0.0;
  178. static double last_err = 0.0;
  179. void pid_setpoint_limit(int limit)
  180. {
  181. *pid_setpoint = 0.01 * (double)limit;
  182. }
  183. /* (*load_value) is expected to be in the 0.0 - 1.0 range
  184. * (expects rl_lock to be taken)
  185. */
  186. void do_update_load(void)
  187. {
  188. double err, dif_err, output;
  189. /* PID update */
  190. err = *pid_setpoint - *rl_load_value;
  191. dif_err = err - last_err;
  192. /*
  193. * TODO?: the 'if' is needed so low cpu loads for
  194. * long periods (which can't be compensated by
  195. * negative drop rates) don't confuse the controller
  196. *
  197. * NB: - "err < 0" means "desired_cpuload < actual_cpuload"
  198. * - int_err is integral(err) over time
  199. */
  200. if (int_err < 0 || err < 0)
  201. int_err += err;
  202. output = (*pid_kp) * err +
  203. (*pid_ki) * int_err +
  204. (*pid_kd) * dif_err;
  205. last_err = err;
  206. *drop_rate = (output > 0) ? output : 0;
  207. }
  208. #define RL_SHM_MALLOC(_p, _s) \
  209. do { \
  210. _p = shm_malloc((_s)); \
  211. if (!_p) { \
  212. LM_ERR("no more shm memory\n"); \
  213. return -1; \
  214. } \
  215. memset(_p, 0, (_s)); \
  216. } while (0)
  217. #define RL_SHM_FREE(_p) \
  218. do { \
  219. if (_p) { \
  220. shm_free(_p); \
  221. _p = 0; \
  222. } \
  223. } while (0)
  224. /* initialize ratelimit module */
  225. static int mod_init(void)
  226. {
  227. unsigned int n;
  228. LM_INFO("Ratelimit module - initializing ...\n");
  229. if (rl_timer_interval < 0) {
  230. LM_ERR("invalid timer interval\n");
  231. return -1;
  232. }
  233. if (rl_expire_time < 0) {
  234. LM_ERR("invalid expire time\n");
  235. return -1;
  236. }
  237. if (db_url.s) {
  238. db_url.len = strlen(db_url.s);
  239. db_prefix.len = strlen(db_prefix.s);
  240. LM_DBG("using CacheDB url: %s\n", db_url.s);
  241. }
  242. RL_SHM_MALLOC(rl_network_count, sizeof(int));
  243. RL_SHM_MALLOC(rl_network_load, sizeof(int));
  244. RL_SHM_MALLOC(rl_load_value, sizeof(double));
  245. RL_SHM_MALLOC(pid_kp, sizeof(double));
  246. RL_SHM_MALLOC(pid_ki, sizeof(double));
  247. RL_SHM_MALLOC(pid_kd, sizeof(double));
  248. RL_SHM_MALLOC(pid_setpoint, sizeof(double));
  249. RL_SHM_MALLOC(drop_rate, sizeof(int));
  250. RL_SHM_MALLOC(rl_feedback_limit, sizeof(int));
  251. /* init ki value for feedback algo */
  252. *pid_ki = -25.0;
  253. rl_lock = lock_alloc();
  254. if (!rl_lock) {
  255. LM_ERR("cannot alloc lock\n");
  256. return -1;
  257. }
  258. if (!lock_init(rl_lock)) {
  259. LM_ERR("failed to init lock\n");
  260. return -1;
  261. }
  262. /* register timer to reset counters */
  263. if (register_timer_process(rl_timer, NULL, rl_timer_interval,
  264. TIMER_PROC_INIT_FLAG) == NULL) {
  265. LM_ERR("could not register timer function\n");
  266. return -1;
  267. }
  268. /* if db_url is not used */
  269. for( n=0 ; n < 8 * sizeof(unsigned int) ; n++) {
  270. if (rl_hash_size==(1<<n))
  271. break;
  272. if (rl_hash_size<(1<<n)) {
  273. LM_WARN("hash_size is not a power "
  274. "of 2 as it should be -> rounding from %d to %d\n",
  275. rl_hash_size, 1<<(n-1));
  276. rl_hash_size = 1<<(n-1);
  277. }
  278. }
  279. if (init_rl_table(rl_hash_size) < 0) {
  280. LM_ERR("cannot allocate the table\n");
  281. return -1;
  282. }
  283. return 0;
  284. }
  285. static int mod_child(int rank)
  286. {
  287. /* init the cachedb */
  288. if (db_url.s && db_url.len)
  289. return init_cachedb(&db_url);
  290. LM_DBG("db_url not set - using standard behaviour\n");
  291. return 0;
  292. }
  293. void mod_destroy(void)
  294. {
  295. unsigned int i;
  296. if (rl_htable.maps) {
  297. for (i = 0; i < rl_htable.size; i++)
  298. map_destroy(rl_htable.maps[i], 0);
  299. shm_free(rl_htable.maps);
  300. rl_htable.maps = 0;
  301. rl_htable.size = 0;
  302. }
  303. if (rl_htable.locks) {
  304. lock_set_destroy(rl_htable.locks);
  305. lock_set_dealloc(rl_htable.locks);
  306. rl_htable.locks = 0;
  307. rl_htable.locks_no = 0;
  308. }
  309. if (rl_lock) {
  310. lock_destroy(rl_lock);
  311. lock_dealloc(rl_lock);
  312. }
  313. RL_SHM_FREE(rl_network_count);
  314. RL_SHM_FREE(rl_network_load);
  315. RL_SHM_FREE(rl_load_value);
  316. RL_SHM_FREE(pid_kp);
  317. RL_SHM_FREE(pid_ki);
  318. RL_SHM_FREE(pid_kd);
  319. RL_SHM_FREE(pid_setpoint);
  320. RL_SHM_FREE(drop_rate);
  321. RL_SHM_FREE(rl_feedback_limit);
  322. if (db_url.s && db_url.len)
  323. destroy_cachedb();
  324. }
  325. /* this is here to avoid using rand() ... which doesn't _always_ return
  326. * exactly what we want (see NOTES section in 'man 3 rand')
  327. */
  328. int hash[100] = {18, 50, 51, 39, 49, 68, 8, 78, 61, 75, 53, 32, 45, 77, 31,
  329. 12, 26, 10, 37, 99, 29, 0, 52, 82, 91, 22, 7, 42, 87, 43, 73, 86, 70,
  330. 69, 13, 60, 24, 25, 6, 93, 96, 97, 84, 47, 79, 64, 90, 81, 4, 15, 63,
  331. 44, 57, 40, 21, 28, 46, 94, 35, 58, 11, 30, 3, 20, 41, 74, 34, 88, 62,
  332. 54, 33, 92, 76, 85, 5, 72, 9, 83, 56, 17, 95, 55, 80, 98, 66, 14, 16,
  333. 38, 71, 23, 2, 67, 36, 65, 27, 1, 19, 59, 89, 48};
  334. /**
  335. * runs the pipe's algorithm
  336. * (expects rl_lock to be taken)
  337. * \return -1 if drop needed, 1 if allowed
  338. */
  339. int rl_pipe_check(rl_pipe_t *pipe)
  340. {
  341. switch (pipe->algo) {
  342. case PIPE_ALGO_NOP:
  343. LM_ERR("no algorithm defined for this pipe\n");
  344. return 1;
  345. case PIPE_ALGO_TAILDROP:
  346. return (pipe->counter <= pipe->limit * rl_timer_interval) ?
  347. 1 : -1;
  348. case PIPE_ALGO_RED:
  349. if (!pipe->load)
  350. return 1;
  351. return pipe->counter % pipe->load ? -1 : 1;
  352. case PIPE_ALGO_NETWORK:
  353. return pipe->load;
  354. case PIPE_ALGO_FEEDBACK:
  355. return (hash[pipe->counter % 100] < *drop_rate) ? -1 : 1;
  356. default:
  357. LM_ERR("ratelimit algorithm %d not implemented\n", pipe->algo);
  358. }
  359. return 1;
  360. }
  361. /*
  362. * MI functions
  363. *
  364. * mi_stats() dumps the current config/statistics
  365. */
  366. /* mi function implementations */
  367. struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param)
  368. {
  369. struct mi_root *rpl_tree;
  370. struct mi_node *node=NULL, *rpl=NULL;
  371. int len;
  372. char * p;
  373. node = cmd_tree->node.kids;
  374. rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  375. if (rpl_tree==0)
  376. return 0;
  377. rpl = &rpl_tree->node;
  378. if (rl_stats(rpl, &node->value)) {
  379. LM_ERR("cannoti mi print values\n");
  380. goto free;
  381. }
  382. LOCK_GET(rl_lock);
  383. p = int2str((unsigned long)(*drop_rate), &len);
  384. if (!(node = add_mi_node_child(rpl, MI_DUP_VALUE, "DROP_RATE", 9, p, len))) {
  385. LOCK_RELEASE(rl_lock);
  386. goto free;
  387. }
  388. LOCK_RELEASE(rl_lock);
  389. return rpl_tree;
  390. free:
  391. free_mi_tree(rpl_tree);
  392. return 0;
  393. }
  394. struct mi_root* mi_set_pid(struct mi_root* cmd_tree, void* param)
  395. {
  396. struct mi_node *node;
  397. char buf[5];
  398. int rl_ki, rl_kp, rl_kd;
  399. if (!(node = cmd_tree->node.kids))
  400. return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
  401. if ( !node->value.s || !node->value.len || node->value.len >= 5)
  402. goto bad_syntax;
  403. memcpy(buf, node->value.s, node->value.len);
  404. buf[node->value.len] = '\0';
  405. rl_ki = strtod(buf, NULL);
  406. node = node->next;
  407. if ( !node->value.s || !node->value.len || node->value.len >= 5)
  408. goto bad_syntax;
  409. memcpy(buf, node->value.s, node->value.len);
  410. buf[node->value.len] = '\0';
  411. rl_kp = strtod(buf, NULL);
  412. node = node->next;
  413. if ( !node->value.s || !node->value.len || node->value.len >= 5)
  414. goto bad_syntax;
  415. memcpy(buf, node->value.s, node->value.len);
  416. buf[node->value.len] = '\0';
  417. rl_kd = strtod(buf, NULL);
  418. LOCK_GET(rl_lock);
  419. *pid_ki = rl_ki;
  420. *pid_kp = rl_kp;
  421. *pid_kd = rl_kd;
  422. LOCK_RELEASE(rl_lock);
  423. return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  424. bad_syntax:
  425. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  426. }
  427. struct mi_root* mi_get_pid(struct mi_root* cmd_tree, void* param)
  428. {
  429. struct mi_root *rpl_tree;
  430. struct mi_node *node=NULL, *rpl=NULL;
  431. struct mi_attr* attr;
  432. rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  433. if (rpl_tree==0)
  434. return 0;
  435. rpl = &rpl_tree->node;
  436. node = add_mi_node_child(rpl, 0, "PID", 3, 0, 0);
  437. if(node == NULL)
  438. goto error;
  439. LOCK_GET(rl_lock);
  440. attr= addf_mi_attr(node, 0, "ki", 2, "%0.3f", *pid_ki);
  441. if(attr == NULL)
  442. goto error;
  443. attr= addf_mi_attr(node, 0, "kp", 2, "%0.3f", *pid_kp);
  444. if(attr == NULL)
  445. goto error;
  446. attr= addf_mi_attr(node, 0, "kd", 2, "%0.3f", *pid_kd);
  447. LOCK_RELEASE(rl_lock);
  448. if(attr == NULL)
  449. goto error;
  450. return rpl_tree;
  451. error:
  452. LOCK_RELEASE(rl_lock);
  453. LM_ERR("Unable to create reply\n");
  454. free_mi_tree(rpl_tree);
  455. return 0;
  456. }
  457. struct mi_root* mi_reset_pipe(struct mi_root* cmd_tree, void* param)
  458. {
  459. struct mi_node *node;
  460. if (!(node = cmd_tree->node.kids))
  461. return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
  462. if (w_rl_set_count(node->value, 0))
  463. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  464. return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  465. }
  466. /* fixup functions */
  467. static int fixup_rl_check(void **param, int param_no)
  468. {
  469. switch (param_no) {
  470. /* pipe name */
  471. case 1:
  472. return fixup_spve(param);
  473. /* limit */
  474. case 2:
  475. return fixup_igp(param);
  476. /* algorithm */
  477. case 3:
  478. return fixup_sgp(param);
  479. /* error */
  480. default:
  481. LM_ERR("[BUG] too many params (%d)\n", param_no);
  482. }
  483. return E_UNSPEC;
  484. }