Beispiel #1
0
/**
 * runs the pipe's algorithm
 * (expects rl_lock to be taken)
 * \return	-1 if drop needed, 1 if allowed
 */
int rl_pipe_check(rl_pipe_t *pipe)
{
	unsigned counter = rl_get_all_counters(pipe);

	switch (pipe->algo) {
		case PIPE_ALGO_NOP:
			LM_ERR("no algorithm defined for this pipe\n");
			return 1;
		case PIPE_ALGO_TAILDROP:
			return (counter <= pipe->limit *
				(rl_limit_per_interval ? 1 : rl_timer_interval)) ? 1 : -1;
		case PIPE_ALGO_RED:
			if (!pipe->load)
				return 1;
			return counter % pipe->load ? -1 : 1;
		case PIPE_ALGO_NETWORK:
			return pipe->load;
		case PIPE_ALGO_FEEDBACK:
			return (hash[counter % 100] < *drop_rate) ? -1 : 1;
		case PIPE_ALGO_HISTORY:
			return hist_check(pipe, 1);
		default:
			LM_ERR("ratelimit algorithm %d not implemented\n", pipe->algo);
	}
	return 1;
}
Beispiel #2
0
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;
}
Beispiel #3
0
static int rl_map_print(void *param, str key, void *value)
{
	struct mi_attr* attr;
	char* p;
	int len;
	struct rl_param_t * rl_param = (struct rl_param_t *)param;
	struct mi_node * rpl;
	rl_pipe_t *pipe = (rl_pipe_t *)value;
	struct mi_node * node;
	str *alg;

	if (!pipe) {
		LM_ERR("invalid pipe value\n");
		return -1;
	}

	if (!rl_param || !rl_param->node || !rl_param->root) {
		LM_ERR("no reply node\n");
		return -1;
	}
	rpl = rl_param->node;

	if (!key.len || !key.s) {
		LM_ERR("no key found\n");
		return -1;
	}

	/* skip if no algo */
	if (pipe->algo == PIPE_ALGO_NOP)
		return 0;

	if (!(node = add_mi_node_child(rpl, 0, "PIPE", 4, 0, 0)))
		return -1;

	if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "id", 2, key.s, key.len)))
		return -1;

	if (!(alg = get_rl_algo_name(pipe->algo))) {
		LM_ERR("[BUG] unknown algorithm %d\n", pipe->algo);
		return -1;
	}

	if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "algorithm", 9,
					alg->s, alg->len)))
		return -1;


	p = int2str((unsigned long)(pipe->limit), &len);
	if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "limit", 5, p, len)))
		return -1;

	p = int2str((unsigned long)rl_get_all_counters(pipe), &len);
	if (!(attr = add_mi_attr(node, MI_DUP_VALUE, "counter", 7, p, len)))
		return -1;

	if ((++rl_param->counter % 50) == 0) {
		LM_DBG("flush mi tree - number %d\n", rl_param->counter);
		flush_mi_tree(rl_param->root);
	}

	return 0;
}
Beispiel #4
0
/* 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);
	}
}
Beispiel #5
0
/**
 * the algorithm keeps a circular window of requests in a fixed size buffer
 *
 * @param pipe   containing the window
 * @param update whether or not to inc call number
 * @return number of calls in the window
 */
static inline int hist_check(rl_pipe_t *pipe, int update)
{
	#define U2MILI(__usec__) (__usec__/1000)
	#define S2MILI(__sec__)  (__sec__ *1000)
	int i;
	int first_good_index;
	int rl_win_ms = rl_window_size * 1000;


	unsigned long long now_total, start_total;

	struct timeval tv;

	/* first get values from our beloved replicated friends
	 * current pipe counter will be calculated after this
	 * iteration; no need for the old one */
	pipe->counter = 0;
	pipe->counter = rl_get_all_counters(pipe);

	gettimeofday(&tv, NULL);
	if (pipe->rwin.start_time.tv_sec == 0) {
		/* the lucky one to come first here */
		pipe->rwin.start_time = tv;
		pipe->rwin.start_index = 0;

		/* we know it starts from 0 because we did memset when created*/
		pipe->rwin.window[pipe->rwin.start_index] += update;
	} else {
		start_total = S2MILI(pipe->rwin.start_time.tv_sec)
							+ U2MILI(pipe->rwin.start_time.tv_usec);

		now_total = S2MILI(tv.tv_sec) + U2MILI(tv.tv_usec);

		/* didn't do any update to the window for "2*window_size" secs
		 * we can't use any elements from the vector
		 * the window is invalidated; very unlikely to happen*/
		if (now_total - start_total >= 2*rl_win_ms) {
			memset(pipe->rwin.window, 0,
					pipe->rwin.window_size * sizeof(long int));

			pipe->rwin.start_index = 0;
			pipe->rwin.start_time = tv;
			pipe->rwin.window[pipe->rwin.start_index] += update;
		} else if (now_total - start_total >= rl_win_ms) {
			/* current time in interval [window_size; 2*window_size)
			 * all the elements in [start_time; (ctime-window_size+1) are
			 * invalidated(set to 0)
			 * */
			/* the first window index not to be set to 0
			 * number of slots from the start_index*/
			first_good_index = ((((now_total - rl_win_ms) - start_total)
							/rl_slot_period + 1) + pipe->rwin.start_index) %
							pipe->rwin.window_size;

			/* the new start time will be the start time of the first slot */
			start_total = (now_total - rl_win_ms) -
					(now_total - rl_win_ms)%rl_slot_period+ rl_slot_period;

			pipe->rwin.start_time.tv_sec  = start_total/1000;
			pipe->rwin.start_time.tv_usec = (start_total%1000)*1000;


			for (i=pipe->rwin.start_index; i != first_good_index;
										i=(i+1)%pipe->rwin.window_size)
				pipe->rwin.window[i] = 0;

			pipe->rwin.start_index = first_good_index;

			/* count current call; it will be the last element in the window */
			pipe->rwin.window[((pipe->rwin.start_index)
					+ (pipe->rwin.window_size-1)) % pipe->rwin.window_size] += update;

		} else { /* now_total - start_total < rl_win_ms  */
			/* no need to modify the window, the value is inside it;
			 * we just need to increment the number of calls for
			 * the current slot*/
			pipe->rwin.window[(now_total-start_total)/rl_slot_period] += update;
		}
	}

	/* count the total number of calls in the window */
	for (i=0; i < pipe->rwin.window_size; i++)
		pipe->counter += pipe->rwin.window[i];

	return pipe->counter > pipe->limit ? -1 : 1;

	#undef U2MILI
	#undef S2MILI
}