int rl_get_counter_value(str *key) { unsigned int hash_idx; rl_pipe_t **pipe; int ret = -1; hash_idx = RL_GET_INDEX(*key); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_FIND_PIPE(hash_idx, *key); if (!pipe || !*pipe) { LM_DBG("cannot find any pipe named %.*s\n", key->len, key->s); goto release; } if (RL_USE_CDB(*pipe)) { if (rl_get_counter(key, *pipe) < 0) { LM_ERR("cannot get the counter's value\n"); goto release; } } ret = rl_get_all_counters(*pipe); release: RL_RELEASE_LOCK(hash_idx); return ret; }
int w_rl_set_count(str key, int val) { unsigned int hash_idx; int ret = -1; rl_pipe_t **pipe; hash_idx = RL_GET_INDEX(key); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_FIND_PIPE(hash_idx, key); if (!pipe || !*pipe) { LM_DBG("cannot find any pipe named %.*s\n", key.len, key.s); goto release; } if (RL_USE_CDB(*pipe)) { if (rl_change_counter(&key, *pipe, val) < 0) { LM_ERR("cannot decrease counter\n"); goto release; } } else if ((*pipe)->algo == PIPE_ALGO_HISTORY) { hist_set_count(*pipe, val); } else { if (val && (val + (*pipe)->counter >= 0)) { (*pipe)->counter += val; } else { (*pipe)->counter = 0; } } LM_DBG("new counter for key %.*s is %d\n", key.len, key.s, (*pipe)->counter); ret = 0; release: RL_RELEASE_LOCK(hash_idx); return ret; }
void rl_timer_repl(utime_t ticks, void *param) { static str module_name = str_init("ratelimit"); unsigned int i = 0; map_iterator_t it; rl_pipe_t **pipe; str *key; int nr = 0; int ret; if (bin_init(&module_name, RL_PIPE_COUNTER) < 0) { LM_ERR("cannot initiate bin buffer\n"); return; } /* iterate through each map */ for (i = 0; i < rl_htable.size; i++) { RL_GET_LOCK(i); /* iterate through all the entries */ if (map_first(rl_htable.maps[i], &it) < 0) { LM_ERR("map doesn't exist\n"); goto next_map; } for (; iterator_is_valid(&it);) { pipe = (rl_pipe_t **)iterator_val(&it); if (!pipe || !*pipe) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_pipe; } /* ignore cachedb replicated stuff */ if (RL_USE_CDB(*pipe)) goto next_pipe; key = iterator_key(&it); if (!key) { LM_ERR("cannot retrieve pipe key\n"); goto next_pipe; } if (bin_push_str(key) < 0) goto error; if (bin_push_int((*pipe)->algo) < 0) goto error; if (bin_push_int((*pipe)->limit) < 0) goto error; if ((ret = bin_push_int((*pipe)->counter)) < 0) goto error; nr++; if (ret > rl_buffer_th) { /* send the buffer */ if (nr) rl_replicate(); if (bin_init(&module_name, RL_PIPE_COUNTER) < 0) { LM_ERR("cannot initiate bin buffer\n"); RL_RELEASE_LOCK(i); return; } nr = 0; } next_pipe: if (iterator_next(&it) < 0) break; } next_map: RL_RELEASE_LOCK(i); } /* if there is anything else to send, do it now */ if (nr) rl_replicate(); return; error: LM_ERR("cannot add pipe info in buffer\n"); RL_RELEASE_LOCK(i); if (nr) rl_replicate(); }
/* timer housekeeping, invoked each timer interval to reset counters */ void rl_timer(unsigned int ticks, void *param) { unsigned int i = 0; map_iterator_t it, del; rl_pipe_t **pipe; str *key; void *value; unsigned long now = time(0); /* get CPU load */ if (get_cpuload() < 0) { LM_ERR("cannot update CPU load\n"); i = 1; } lock_get(rl_lock); /* if CPU was successfully loaded */ if (!i) do_update_load(); /* update network if needed */ if (*rl_network_count) *rl_network_load = get_total_bytes_waiting(PROTO_NONE); lock_release(rl_lock); /* iterate through each map */ for (i = 0; i < rl_htable.size; i++) { RL_GET_LOCK(i); /* iterate through all the entries */ if (map_first(rl_htable.maps[i], &it) < 0) { LM_ERR("map doesn't exist\n"); goto next_map; } for (; iterator_is_valid(&it);) { pipe = (rl_pipe_t **)iterator_val(&it); if (!pipe || !*pipe) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_pipe; } key = iterator_key(&it); if (!key) { LM_ERR("cannot retrieve pipe key\n"); goto next_pipe; } /* check to see if it is expired */ if ((*pipe)->last_used + rl_expire_time < now) { /* this pipe is engaged in a transaction */ del = it; if (iterator_next(&it) < 0) LM_DBG("cannot find next iterator\n"); if ((*pipe)->algo == PIPE_ALGO_NETWORK) { lock_get(rl_lock); (*rl_network_count)--; lock_release(rl_lock); } LM_DBG("Deleting ratelimit pipe key \"%.*s\"\n", key->len, key->s); value = iterator_delete(&del); /* free resources */ if (value) shm_free(value); continue; } else { /* leave the lock if a cachedb query should be done*/ if (RL_USE_CDB(*pipe)) { if (rl_get_counter(key, *pipe) < 0) { LM_ERR("cannot get pipe counter\n"); goto next_pipe; } } switch ((*pipe)->algo) { case PIPE_ALGO_NETWORK: /* handle network algo */ (*pipe)->load = (*rl_network_load > (*pipe)->limit) ? -1 : 1; break; case PIPE_ALGO_RED: if ((*pipe)->limit && rl_timer_interval) (*pipe)->load = (*pipe)->counter / ((*pipe)->limit * rl_timer_interval); break; default: break; } (*pipe)->last_counter = rl_get_all_counters(*pipe); if (RL_USE_CDB(*pipe)) { if (rl_change_counter(key, *pipe, 0) < 0) { LM_ERR("cannot reset counter\n"); } } else { (*pipe)->counter = 0; } } next_pipe: if (iterator_next(&it) < 0) break; } next_map: RL_RELEASE_LOCK(i); } }
int w_rl_check_3(struct sip_msg *_m, char *_n, char *_l, char *_a) { str name; int limit = 0, ret = 1, should_update = 0; str algorithm; unsigned int hash_idx; rl_pipe_t **pipe; rl_algo_t algo = -1; /* retrieve and check parameters */ if (!_n || !_l) { LM_ERR("invalid parameters\n"); goto end; } if (fixup_get_svalue(_m, (gparam_p)_n, &name) < 0) { LM_ERR("cannot retrieve identifier\n"); goto end; } if (fixup_get_ivalue(_m, (gparam_p)_l, &limit) < 0) { LM_ERR("cannot retrieve limit\n"); goto end; } algorithm.s = 0; if (!_a || fixup_get_svalue(_m, (gparam_p)_a, &algorithm) < 0 || (algo = get_rl_algo(algorithm)) < 0) { algo = PIPE_ALGO_NOP; } /* get limit for FEEDBACK algorithm */ if (algo == PIPE_ALGO_FEEDBACK) { lock_get(rl_lock); if (*rl_feedback_limit) { if (*rl_feedback_limit != limit) { LM_WARN("FEEDBACK limit should be the same for all pipes, but" " new limit %d differs - setting to %d\n", limit, *rl_feedback_limit); limit = *rl_feedback_limit; } } else { if (limit <= 0 || limit >= 100) { LM_ERR("invalid limit for FEEDBACK algorithm " "(must be between 0 and 100)\n"); lock_release(rl_lock); goto end; } *rl_feedback_limit = limit; pid_setpoint_limit(limit); } lock_release(rl_lock); } hash_idx = RL_GET_INDEX(name); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_GET_PIPE(hash_idx, name); if (!pipe) { LM_ERR("cannot get the index\n"); goto release; } if (!*pipe) { /* allocate new pipe */ *pipe = shm_malloc(sizeof(rl_pipe_t) + rl_dests_nr * sizeof(rl_repl_counter_t)); if (!*pipe) { LM_ERR("no more shm memory\n"); goto release; } memset(*pipe, 0, sizeof(rl_pipe_t) + rl_dests_nr * sizeof(rl_repl_counter_t)); (*pipe)->dsts = (rl_repl_counter_t *)((*pipe) + 1); LM_DBG("Pipe %.*s doens't exist, but was created %p\n", name.len, name.s, *pipe); if (algo == PIPE_ALGO_NETWORK) should_update = 1; (*pipe)->algo = (algo == PIPE_ALGO_NOP) ? rl_default_algo : algo; } else { LM_DBG("Pipe %.*s found: %p - last used %lu\n", name.len, name.s, *pipe, (*pipe)->last_used); if (algo != PIPE_ALGO_NOP && (*pipe)->algo != algo) { LM_WARN("algorithm %d different from the initial one %d for pipe " "%.*s", algo, (*pipe)->algo, name.len, name.s); } } /* set/update the limit */ (*pipe)->limit = limit; /* set the last used time */ (*pipe)->last_used = time(0); if (RL_USE_CDB(*pipe)) { /* release the counter for a while */ if (rl_change_counter(&name, *pipe, 1) < 0) { LM_ERR("cannot increase counter\n"); goto end; } } else { (*pipe)->counter++; } ret = rl_pipe_check(*pipe); LM_DBG("Pipe %.*s counter:%d load:%d limit:%d should %sbe blocked (%p)\n", name.len, name.s, (*pipe)->counter, (*pipe)->load, (*pipe)->limit, ret == 1? "NOT " : "", *pipe); release: RL_RELEASE_LOCK(hash_idx); if (should_update) { lock_get(rl_lock); (*rl_network_count)++; lock_release(rl_lock); } end: return ret; }
void rl_timer_repl(utime_t ticks, void *param) { unsigned int i = 0; map_iterator_t it; rl_pipe_t **pipe; str *key; int nr = 0; int ret; bin_packet_t packet; if (bin_init(&packet, &pipe_repl_cap, RL_PIPE_COUNTER, BIN_VERSION, 0) < 0) { LM_ERR("cannot initiate bin buffer\n"); return; } /* iterate through each map */ for (i = 0; i < rl_htable.size; i++) { RL_GET_LOCK(i); /* iterate through all the entries */ if (map_first(rl_htable.maps[i], &it) < 0) { LM_ERR("map doesn't exist\n"); goto next_map; } for (; iterator_is_valid(&it);) { pipe = (rl_pipe_t **) iterator_val(&it); if (!pipe || !*pipe) { LM_ERR("[BUG] bogus map[%d] state\n", i); goto next_pipe; } /* ignore cachedb replicated stuff */ if (RL_USE_CDB(*pipe)) goto next_pipe; key = iterator_key(&it); if (!key) { LM_ERR("cannot retrieve pipe key\n"); goto next_pipe; } if (bin_push_str(&packet, key) < 0) goto error; if (bin_push_int(&packet, (*pipe)->algo) < 0) goto error; if (bin_push_int(&packet, (*pipe)->limit) < 0) goto error; /* * for the SBT algorithm it is safe to replicate the current * counter, since it is always updating according to the window */ if ((ret = bin_push_int(&packet, ((*pipe)->algo == PIPE_ALGO_HISTORY ? (*pipe)->counter : (*pipe)->my_last_counter))) < 0) goto error; nr++; if (ret > rl_buffer_th) { /* send the buffer */ if (nr) rl_replicate(&packet); bin_reset_back_pointer(&packet); nr = 0; } next_pipe: if (iterator_next(&it) < 0) break; } next_map: RL_RELEASE_LOCK(i); } /* if there is anything else to send, do it now */ if (nr) rl_replicate(&packet); bin_free_packet(&packet); return; error: LM_ERR("cannot add pipe info in buffer\n"); RL_RELEASE_LOCK(i); if (nr) rl_replicate(&packet); bin_free_packet(&packet); }
int w_rl_check(struct sip_msg *_m, str *name, int *limit, str *algorithm) { int ret = 1, should_update = 0; unsigned int hash_idx; rl_pipe_t **pipe; rl_algo_t algo = -1; if (!algorithm || (algo = get_rl_algo(*algorithm)) == PIPE_ALGO_NOP) { algo = PIPE_ALGO_NOP; } /* get limit for FEEDBACK algorithm */ if (algo == PIPE_ALGO_FEEDBACK) { lock_get(rl_lock); if (*rl_feedback_limit) { if (*rl_feedback_limit != *limit) { LM_WARN("FEEDBACK limit should be the same for all pipes, but" " new limit %d differs - setting to %d\n", *limit, *rl_feedback_limit); *limit = *rl_feedback_limit; } } else { if (*limit <= 0 || *limit >= 100) { LM_ERR("invalid limit for FEEDBACK algorithm " "(must be between 0 and 100)\n"); lock_release(rl_lock); goto end; } *rl_feedback_limit = *limit; pid_setpoint_limit(*limit); } lock_release(rl_lock); } hash_idx = RL_GET_INDEX(*name); RL_GET_LOCK(hash_idx); /* try to get the value */ pipe = RL_GET_PIPE(hash_idx, *name); if (!pipe) { LM_ERR("cannot get the index\n"); goto release; } if (!*pipe) { /* allocate new pipe */ if (!(*pipe = rl_create_pipe(*limit, algo))) goto release; LM_DBG("Pipe %.*s doesn't exist, but was created %p\n", name->len, name->s, *pipe); if ((*pipe)->algo == PIPE_ALGO_NETWORK) should_update = 1; } else { LM_DBG("Pipe %.*s found: %p - last used %lu\n", name->len, name->s, *pipe, (*pipe)->last_used); if (algo != PIPE_ALGO_NOP && (*pipe)->algo != algo) { LM_WARN("algorithm %d different from the initial one %d for pipe " "%.*s", algo, (*pipe)->algo, name->len, name->s); } /* update the limit */ (*pipe)->limit = *limit; } /* set the last used time */ (*pipe)->last_used = time(0); if (RL_USE_CDB(*pipe)) { /* release the counter for a while */ if (rl_change_counter(name, *pipe, 1) < 0) { LM_ERR("cannot increase counter\n"); goto release; } } else { (*pipe)->counter++; } ret = rl_pipe_check(*pipe); LM_DBG("Pipe %.*s counter:%d load:%d limit:%d should %sbe blocked (%p)\n", name->len, name->s, (*pipe)->counter, (*pipe)->load, (*pipe)->limit, ret == 1 ? "NOT " : "", *pipe); release: RL_RELEASE_LOCK(hash_idx); if (should_update) { lock_get(rl_lock); (*rl_network_count)++; lock_release(rl_lock); } end: return ret; }