summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2014-10-02 11:41:34 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2014-10-02 12:52:50 +0200
commit1123e707400984108f48ac7c1be559f7ed8d9306 (patch)
treef303a7df3d685d3c7886fbf30cb43a4288341fde
parentdcde7ae597ccb7d81648b9ecab7c0f61c88e60f2 (diff)
Implements token bucket filter for rate limiting.
-rw-r--r--filter/filter.c4
-rw-r--r--lib/Doc2
-rw-r--r--lib/Modules1
-rw-r--r--lib/birdlib.h40
-rw-r--r--lib/tbf.c29
-rw-r--r--nest/rt-table.c4
-rw-r--r--proto/bgp/packets.c3
-rw-r--r--sysdep/linux/netlink.c2
-rw-r--r--sysdep/unix/krt.c14
-rw-r--r--sysdep/unix/log.c28
10 files changed, 85 insertions, 42 deletions
diff --git a/filter/filter.c b/filter/filter.c
index 88763302..63c2cd86 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -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 { \
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) {
- 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;
}
DBG( "done (%u)\n", res.val.i );
diff --git a/lib/Doc b/lib/Doc
index 6be6250d..8f513821 100644
--- a/lib/Doc
+++ b/lib/Doc
@@ -1,7 +1,7 @@
H Library functions
S ip.c ipv4.c ipv6.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
S resource.c
S mempool.c
diff --git a/lib/Modules b/lib/Modules
index c585c9a8..7131f0b2 100644
--- a/lib/Modules
+++ b/lib/Modules
@@ -19,6 +19,7 @@ resource.c
resource.h
slab.c
socket.h
+tbf.c
unaligned.h
xmalloc.c
printf.c
diff --git a/lib/birdlib.h b/lib/birdlib.h
index 04fb7fed..c489c45f 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -68,6 +68,38 @@ typedef s64 btime;
#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 */
typedef struct buffer {
@@ -88,16 +120,10 @@ typedef struct buffer {
#define LOG_BUFFER_SIZE 1024
-
-struct rate_limit {
- bird_clock_t timestamp;
- int count;
-};
-
#define log log_msg
void log_commit(int class, buffer *buf);
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 bug(char *msg, ...) NORET;
diff --git a/lib/tbf.c b/lib/tbf.c
new file mode 100644
index 00000000..39e18e57
--- /dev/null
+++ b/lib/tbf.c
@@ -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;
+}
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 4c889d0d..37dbb33d 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -645,7 +645,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
struct proto *p = ah->proto;
struct rtable *table = ah->table;
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 *old_best = net->routes;
rte *old = NULL;
@@ -1367,7 +1367,7 @@ rt_init(void)
static int
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;
DBG("Pruning route table %s\n", tab->name);
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 4464523d..0b9de8c1 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -22,7 +22,8 @@
#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 */
static byte fsm_err_subcode[BS_MAX] = {
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index a0f85186..132403af 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -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
nl_error(struct nlmsghdr *h)
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 51950ec9..a2fb83d9 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -300,10 +300,10 @@ krt_trace_in(struct krt_proto *p, rte *e, char *msg)
}
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)
- 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
-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
@@ -378,20 +378,20 @@ krt_learn_scan(struct krt_proto *p, rte *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);
m->u.krt.seen = 1;
}
else
{
- krt_trace_in_rl(&rl_alien_updated, p, e, "[alien] updated");
+ krt_trace_in(p, e, "[alien] updated");
*mm = m->next;
rte_free(m);
m = NULL;
}
}
else
- krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
+ krt_trace_in(p, e, "[alien] created");
if (!m)
{
e->next = n->routes;
@@ -637,7 +637,7 @@ krt_got_route(struct krt_proto *p, rte *e)
krt_learn_scan(p, e);
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);
}
return;
diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c
index 66a5581c..ccf35bf3 100644
--- a/sysdep/unix/log.c
+++ b/sysdep/unix/log.c
@@ -32,9 +32,6 @@ static FILE *dbgf;
static list *current_log_list;
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
@@ -154,7 +151,6 @@ vlog(int class, const char *msg, va_list args)
}
-
/**
* log - log a message
* @msg: printf-like formatting string with message class information
@@ -180,31 +176,21 @@ log_msg(char *msg, ...)
}
void
-log_rl(struct rate_limit *rl, char *msg, ...)
+log_rl(struct tbf *f, char *msg, ...)
{
+ int last_hit = f->mark;
int class = 1;
va_list args;
- bird_clock_t delta = now - rl->timestamp;
- if ((0 <= delta) && (delta < rate_limit_time))
- {
- rl->count++;
- }
- else
- {
- rl->timestamp = now;
- rl->count = 1;
- }
-
- if (rl->count > rate_limit_count)
+ /* Rate limiting is a bit tricky here as it also logs '...' during the first hit */
+ if (tbf_limit(f) && last_hit)
return;
- va_start(args, msg);
if (*msg >= 1 && *msg <= 8)
class = *msg++;
- vlog(class, msg, args);
- if (rl->count == rate_limit_count)
- vlog(class, "...", args);
+
+ va_start(args, msg);
+ vlog(class, (f->mark ? "..." : msg), args);
va_end(args);
}