Implements token bucket filter for rate limiting.
This commit is contained in:
parent
dcde7ae597
commit
1123e70740
10 changed files with 85 additions and 42 deletions
|
@ -493,7 +493,7 @@ f_rta_cow(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rate_limit rl_runtime_err;
|
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
|
||||||
|
|
||||||
#define runtime(x) do { \
|
#define runtime(x) do { \
|
||||||
log_rl(&rl_runtime_err, L_ERR "filters, line %d: %s", what->lineno, x); \
|
log_rl(&rl_runtime_err, L_ERR "filters, line %d: %s", what->lineno, x); \
|
||||||
|
@ -1492,7 +1492,7 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc
|
||||||
|
|
||||||
|
|
||||||
if (res.type != T_RETURN) {
|
if (res.type != T_RETURN) {
|
||||||
log( L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
|
log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
|
||||||
return F_ERROR;
|
return F_ERROR;
|
||||||
}
|
}
|
||||||
DBG( "done (%u)\n", res.val.i );
|
DBG( "done (%u)\n", res.val.i );
|
||||||
|
|
2
lib/Doc
2
lib/Doc
|
@ -1,7 +1,7 @@
|
||||||
H Library functions
|
H Library functions
|
||||||
S ip.c ipv4.c ipv6.c
|
S ip.c ipv4.c ipv6.c
|
||||||
S lists.c
|
S lists.c
|
||||||
S checksum.c bitops.c patmatch.c printf.c xmalloc.c
|
S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
|
||||||
D resource.sgml
|
D resource.sgml
|
||||||
S resource.c
|
S resource.c
|
||||||
S mempool.c
|
S mempool.c
|
||||||
|
|
|
@ -19,6 +19,7 @@ resource.c
|
||||||
resource.h
|
resource.h
|
||||||
slab.c
|
slab.c
|
||||||
socket.h
|
socket.h
|
||||||
|
tbf.c
|
||||||
unaligned.h
|
unaligned.h
|
||||||
xmalloc.c
|
xmalloc.c
|
||||||
printf.c
|
printf.c
|
||||||
|
|
|
@ -68,6 +68,38 @@ typedef s64 btime;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Rate limiting */
|
||||||
|
|
||||||
|
struct tbf {
|
||||||
|
bird_clock_t timestamp; /* Last update */
|
||||||
|
u16 count; /* Available tokens */
|
||||||
|
u16 burst; /* Max number of tokens */
|
||||||
|
u16 rate; /* Rate of replenishment */
|
||||||
|
u16 mark; /* Whether last op was limited */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Default TBF values for rate limiting log messages */
|
||||||
|
#define TBF_DEFAULT_LOG_LIMITS { .rate = 1, .burst = 5 }
|
||||||
|
|
||||||
|
void tbf_update(struct tbf *f);
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
tbf_limit(struct tbf *f)
|
||||||
|
{
|
||||||
|
tbf_update(f);
|
||||||
|
|
||||||
|
if (!f->count)
|
||||||
|
{
|
||||||
|
f->mark = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->count--;
|
||||||
|
f->mark = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Logging and dying */
|
/* Logging and dying */
|
||||||
|
|
||||||
typedef struct buffer {
|
typedef struct buffer {
|
||||||
|
@ -88,16 +120,10 @@ typedef struct buffer {
|
||||||
|
|
||||||
#define LOG_BUFFER_SIZE 1024
|
#define LOG_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
|
||||||
struct rate_limit {
|
|
||||||
bird_clock_t timestamp;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define log log_msg
|
#define log log_msg
|
||||||
void log_commit(int class, buffer *buf);
|
void log_commit(int class, buffer *buf);
|
||||||
void log_msg(char *msg, ...);
|
void log_msg(char *msg, ...);
|
||||||
void log_rl(struct rate_limit *rl, char *msg, ...);
|
void log_rl(struct tbf *rl, char *msg, ...);
|
||||||
void die(char *msg, ...) NORET;
|
void die(char *msg, ...) NORET;
|
||||||
void bug(char *msg, ...) NORET;
|
void bug(char *msg, ...) NORET;
|
||||||
|
|
||||||
|
|
29
lib/tbf.c
Normal file
29
lib/tbf.c
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* BIRD Library -- Token Bucket Filter
|
||||||
|
*
|
||||||
|
* (c) 2014 Ondrej Zajicek <santiago@crfreenet.org>
|
||||||
|
* (c) 2014 CZ.NIC z.s.p.o.
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nest/bird.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
tbf_update(struct tbf *f)
|
||||||
|
{
|
||||||
|
bird_clock_t delta = now - f->timestamp;
|
||||||
|
|
||||||
|
if (delta == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
f->timestamp = now;
|
||||||
|
|
||||||
|
if ((0 < delta) && (delta < f->burst))
|
||||||
|
{
|
||||||
|
u32 next = f->count + delta * f->rate;
|
||||||
|
f->count = MIN(next, f->burst);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
f->count = f->burst;
|
||||||
|
}
|
|
@ -645,7 +645,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
struct proto *p = ah->proto;
|
struct proto *p = ah->proto;
|
||||||
struct rtable *table = ah->table;
|
struct rtable *table = ah->table;
|
||||||
struct proto_stats *stats = ah->stats;
|
struct proto_stats *stats = ah->stats;
|
||||||
static struct rate_limit rl_pipe;
|
static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
|
||||||
rte *before_old = NULL;
|
rte *before_old = NULL;
|
||||||
rte *old_best = net->routes;
|
rte *old_best = net->routes;
|
||||||
rte *old = NULL;
|
rte *old = NULL;
|
||||||
|
@ -1367,7 +1367,7 @@ rt_init(void)
|
||||||
static int
|
static int
|
||||||
rt_prune_step(rtable *tab, int step, int *limit)
|
rt_prune_step(rtable *tab, int step, int *limit)
|
||||||
{
|
{
|
||||||
static struct rate_limit rl_flush;
|
static struct tbf rl_flush = TBF_DEFAULT_LOG_LIMITS;
|
||||||
struct fib_iterator *fit = &tab->prune_fit;
|
struct fib_iterator *fit = &tab->prune_fit;
|
||||||
|
|
||||||
DBG("Pruning route table %s\n", tab->name);
|
DBG("Pruning route table %s\n", tab->name);
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
|
|
||||||
#include "bgp.h"
|
#include "bgp.h"
|
||||||
|
|
||||||
static struct rate_limit rl_rcv_update, rl_snd_update;
|
static struct tbf rl_rcv_update = TBF_DEFAULT_LOG_LIMITS;
|
||||||
|
static struct tbf rl_snd_update = TBF_DEFAULT_LOG_LIMITS;
|
||||||
|
|
||||||
/* Table for state -> RFC 6608 FSM error subcodes */
|
/* Table for state -> RFC 6608 FSM error subcodes */
|
||||||
static byte fsm_err_subcode[BS_MAX] = {
|
static byte fsm_err_subcode[BS_MAX] = {
|
||||||
|
|
|
@ -151,7 +151,7 @@ nl_get_reply(struct nl_sock *nl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rate_limit rl_netlink_err;
|
static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nl_error(struct nlmsghdr *h)
|
nl_error(struct nlmsghdr *h)
|
||||||
|
|
|
@ -300,10 +300,10 @@ krt_trace_in(struct krt_proto *p, rte *e, char *msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg)
|
krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg)
|
||||||
{
|
{
|
||||||
if (p->p.debug & D_PACKETS)
|
if (p->p.debug & D_PACKETS)
|
||||||
log_rl(rl, L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg);
|
log_rl(f, L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -312,7 +312,7 @@ krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg)
|
||||||
|
|
||||||
#ifdef KRT_ALLOW_LEARN
|
#ifdef KRT_ALLOW_LEARN
|
||||||
|
|
||||||
static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored;
|
static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* krt_same_key() specifies what (aside from the net) is the key in
|
* krt_same_key() specifies what (aside from the net) is the key in
|
||||||
|
@ -378,20 +378,20 @@ krt_learn_scan(struct krt_proto *p, rte *e)
|
||||||
{
|
{
|
||||||
if (krt_uptodate(m, e))
|
if (krt_uptodate(m, e))
|
||||||
{
|
{
|
||||||
krt_trace_in_rl(&rl_alien_seen, p, e, "[alien] seen");
|
krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
|
||||||
rte_free(e);
|
rte_free(e);
|
||||||
m->u.krt.seen = 1;
|
m->u.krt.seen = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
krt_trace_in_rl(&rl_alien_updated, p, e, "[alien] updated");
|
krt_trace_in(p, e, "[alien] updated");
|
||||||
*mm = m->next;
|
*mm = m->next;
|
||||||
rte_free(m);
|
rte_free(m);
|
||||||
m = NULL;
|
m = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
|
krt_trace_in(p, e, "[alien] created");
|
||||||
if (!m)
|
if (!m)
|
||||||
{
|
{
|
||||||
e->next = n->routes;
|
e->next = n->routes;
|
||||||
|
@ -637,7 +637,7 @@ krt_got_route(struct krt_proto *p, rte *e)
|
||||||
krt_learn_scan(p, e);
|
krt_learn_scan(p, e);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
krt_trace_in_rl(&rl_alien_ignored, p, e, "[alien] ignored");
|
krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored");
|
||||||
rte_free(e);
|
rte_free(e);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -32,9 +32,6 @@ static FILE *dbgf;
|
||||||
static list *current_log_list;
|
static list *current_log_list;
|
||||||
static char *current_syslog_name; /* NULL -> syslog closed */
|
static char *current_syslog_name; /* NULL -> syslog closed */
|
||||||
|
|
||||||
static const bird_clock_t rate_limit_time = 5;
|
|
||||||
static const int rate_limit_count = 5;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_PTHREADS
|
#ifdef USE_PTHREADS
|
||||||
|
|
||||||
|
@ -154,7 +151,6 @@ vlog(int class, const char *msg, va_list args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* log - log a message
|
* log - log a message
|
||||||
* @msg: printf-like formatting string with message class information
|
* @msg: printf-like formatting string with message class information
|
||||||
|
@ -180,31 +176,21 @@ log_msg(char *msg, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
log_rl(struct rate_limit *rl, char *msg, ...)
|
log_rl(struct tbf *f, char *msg, ...)
|
||||||
{
|
{
|
||||||
|
int last_hit = f->mark;
|
||||||
int class = 1;
|
int class = 1;
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
bird_clock_t delta = now - rl->timestamp;
|
/* Rate limiting is a bit tricky here as it also logs '...' during the first hit */
|
||||||
if ((0 <= delta) && (delta < rate_limit_time))
|
if (tbf_limit(f) && last_hit)
|
||||||
{
|
|
||||||
rl->count++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rl->timestamp = now;
|
|
||||||
rl->count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rl->count > rate_limit_count)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
va_start(args, msg);
|
|
||||||
if (*msg >= 1 && *msg <= 8)
|
if (*msg >= 1 && *msg <= 8)
|
||||||
class = *msg++;
|
class = *msg++;
|
||||||
vlog(class, msg, args);
|
|
||||||
if (rl->count == rate_limit_count)
|
va_start(args, msg);
|
||||||
vlog(class, "...", args);
|
vlog(class, (f->mark ? "..." : msg), args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue