]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements token bucket filter for rate limiting.
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 2 Oct 2014 09:41:34 +0000 (11:41 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 2 Oct 2014 10:52:50 +0000 (12:52 +0200)
filter/filter.c
lib/Doc
lib/Modules
lib/birdlib.h
lib/tbf.c [new file with mode: 0644]
nest/rt-table.c
proto/bgp/packets.c
sysdep/linux/netlink.c
sysdep/unix/krt.c
sysdep/unix/log.c

index 88763302a2622a088373a9eae7e5a2dcea5b7587..63c2cd86b98d11e3e9b51091cfe87b10dbe15578 100644 (file)
@@ -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 6be6250d108ef4a504648a8d7967c599b72b3685..8f5138215cd2b221bdec16f3d90a4b4cf892ebbb 100644 (file)
--- 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
index c585c9a845ee287686f95ff959cb16a0b9221146..7131f0b2788d37c20acfe7dc8d000addfaf5741e 100644 (file)
@@ -19,6 +19,7 @@ resource.c
 resource.h
 slab.c
 socket.h
+tbf.c
 unaligned.h
 xmalloc.c
 printf.c
index 04fb7fedfe153e6b3fb2984932eb02b84c872244..c489c45f1a5eef6e507fb6ce366904e37162a2ae 100644 (file)
@@ -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 (file)
index 0000000..39e18e5
--- /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;
+}
index 4c889d0d1c033de3c6065880d2c6e33076f1a7ac..37dbb33dc0e36feda761e0d2b7b9ae601ad24b28 100644 (file)
@@ -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);
index 4464523d14cd22a39ce88877469f414b30a7c6b3..0b9de8c18a2103638f4f04f2fb56f03619116b99 100644 (file)
@@ -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] = {
index a0f851868e6b81e905df3c318a00617f28788c44..132403af831c1096cb1ad8aa5dcda81df7e61f3d 100644 (file)
@@ -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)
index 51950ec9291f9ebe83c4b004c94017f50d1a25ff..a2fb83d9252dc880996763f675c23b7696a1e1df 100644 (file)
@@ -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;
index 66a5581ce589da7072f4561f39c33e5292cc361a..ccf35bf34aca9d9ef08919af05a6cd304248c263 100644 (file)
@@ -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);
 }