From 252c7e4d0b7b45c89f69b3c4763b0c013aa5830d Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 2 Oct 2014 11:05:55 +0200 Subject: Refresh kernel protocol when interface disappears. When an interface goes down, (Linux) kernel removes routes pointing to that ifacem but does not send withdraws for them. We rescan the kernel table to ensure synchronization. Thanks to Alexander Demenshin for the bugreport. --- sysdep/unix/krt.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'sysdep/unix/krt.c') diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index bff3001f..51950ec9 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -36,7 +36,7 @@ * only once for all the instances. * * The code uses OS-dependent parts for kernel updates and scans. These parts are - * in more specific sysdep directories (e.g. sysdep/linux) in functions krt_sys_* + * in more specific sysdep directories (e.g. sysdep/linux) in functions krt_sys_* * and kif_sys_* (and some others like krt_replace_rte()) and krt-sys.h header file. * This is also used for platform specific protocol options and route attributes. * @@ -117,7 +117,7 @@ kif_request_scan(void) static inline int prefer_addr(struct ifa *a, struct ifa *b) -{ +{ int sa = a->scope > SCOPE_LINK; int sb = b->scope > SCOPE_LINK; @@ -737,7 +737,7 @@ krt_prune(struct krt_proto *p) if (! krt_export_rte(p, &new, &tmpa)) { /* Route rejected, should not happen (KRF_INSTALLED) but to be sure .. */ - verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; + verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; } else { @@ -910,7 +910,7 @@ krt_scan_timer_stop(struct krt_proto *p) } static void -krt_scan_timer_kick(struct krt_proto *p UNUSED) +krt_scan_timer_kick(struct krt_proto *p) { tm_start(p->scan_timer, 0); } @@ -962,8 +962,8 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * if (e->attrs->src->proto == P) return -1; - if (!KRT_CF->devroutes && - (e->attrs->dest == RTD_DEVICE) && + if (!KRT_CF->devroutes && + (e->attrs->dest == RTD_DEVICE) && (e->attrs->source != RTS_STATIC_DEVICE)) return -1; @@ -974,8 +974,8 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * } static void -krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, - rte *new, rte *old, struct ea_list *eattrs) +krt_rt_notify(struct proto *P, struct rtable *table UNUSED, net *net, + rte *new, rte *old, struct ea_list *eattrs) { struct krt_proto *p = (struct krt_proto *) P; @@ -991,6 +991,36 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, krt_replace_rte(p, net, new, old, eattrs); } +static void +krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED) +{ + struct krt_proto *p = (struct krt_proto *) P; + + /* + * When interface went down, we should remove routes to it. In the ideal world, + * OS kernel would send us route removal notifications in such cases, but we + * cannot rely on it as it is often not true. E.g. Linux kernel removes related + * routes when an interface went down, but it does not notify userspace about + * that. To be sure, we just schedule a scan to ensure synchronization. + */ + + if ((flags & IF_CHANGE_DOWN) && KRT_CF->learn) + krt_scan_timer_kick(p); +} + +static int +krt_reload_routes(struct proto *P) +{ + struct krt_proto *p = (struct krt_proto *) P; + + /* Although we keep learned routes in krt_table, we rather schedule a scan */ + + if (KRT_CF->learn) + krt_scan_timer_kick(p); + + return 1; +} + static void krt_feed_done(struct proto *P) { @@ -1022,7 +1052,9 @@ krt_init(struct proto_config *c) p->p.accept_ra_types = RA_OPTIMAL; p->p.import_control = krt_import_control; - p->p.rt_notify = krt_notify; + p->p.rt_notify = krt_rt_notify; + p->p.if_notify = krt_if_notify; + p->p.reload_routes = krt_reload_routes; p->p.feed_done = krt_feed_done; p->p.make_tmp_attrs = krt_make_tmp_attrs; p->p.store_tmp_attrs = krt_store_tmp_attrs; -- cgit v1.2.3 From 1123e707400984108f48ac7c1be559f7ed8d9306 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 2 Oct 2014 11:41:34 +0200 Subject: Implements token bucket filter for rate limiting. --- filter/filter.c | 4 ++-- lib/Doc | 2 +- lib/Modules | 1 + lib/birdlib.h | 40 +++++++++++++++++++++++++++++++++------- lib/tbf.c | 29 +++++++++++++++++++++++++++++ nest/rt-table.c | 4 ++-- proto/bgp/packets.c | 3 ++- sysdep/linux/netlink.c | 2 +- sysdep/unix/krt.c | 14 +++++++------- sysdep/unix/log.c | 28 +++++++--------------------- 10 files changed, 85 insertions(+), 42 deletions(-) create mode 100644 lib/tbf.c (limited to 'sysdep/unix/krt.c') 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 + * (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); } -- cgit v1.2.3