0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Limit containment

This commit is contained in:
Maria Matejka 2021-11-06 20:34:16 +01:00
parent 46739f007a
commit 3a8197a9dc
5 changed files with 269 additions and 202 deletions

49
nest/limit.h Normal file
View File

@ -0,0 +1,49 @@
/*
* BIRD Internet Routing Daemon -- Limits
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_LIMIT_H_
#define _BIRD_LIMIT_H_
struct limit {
u32 max;
u32 count;
int (*action)(struct limit *, void *data);
};
static inline int limit_do_action(struct limit *l, void *data)
{
return l->action ? l->action(l, data) : 1;
}
static inline int limit_push(struct limit *l, void *data)
{
if ((l->count >= l->max) && limit_do_action(l, data))
return 1;
l->count++;
return 0;
}
static inline void limit_pop(struct limit *l)
{
--l->count;
}
static inline void limit_reset(struct limit *l)
{
l->count = 0;
}
static inline void limit_update(struct limit *l, void *data, u32 max)
{
if (l->count > (l->max = max))
limit_do_action(l, data);
}
#endif

View File

@ -52,9 +52,9 @@ static void channel_request_reload(struct channel *c);
static void proto_shutdown_loop(timer *);
static void proto_rethink_goal(struct proto *p);
static char *proto_state_name(struct proto *p);
static void channel_verify_limits(struct channel *c);
static inline void channel_reset_limit(struct channel_limit *l);
static void channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf);
static void channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf);
static void channel_reset_limit(struct channel *c, struct limit *l, int dir);
static inline int proto_is_done(struct proto *p)
{ return (p->proto_state == PS_DOWN) && (p->active_channels == 0); }
@ -168,9 +168,10 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
c->rx_limit = cf->rx_limit;
c->in_limit = cf->in_limit;
c->out_limit = cf->out_limit;
channel_init_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
channel_init_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
channel_init_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit);
c->net_type = cf->net_type;
c->ra_mode = cf->ra_mode;
@ -280,14 +281,12 @@ channel_feed_loop(void *ptr)
}
/* Reset export limit if the feed ended with acceptable number of exported routes */
struct channel_limit *l = &c->out_limit;
if (c->refeeding &&
(l->state == PLS_BLOCKED) &&
(c->refeed_count <= l->limit) &&
(c->export_stats.routes <= l->limit))
(c->limit_active & (1 << PLD_OUT)) &&
(c->refeed_count <= c->out_limit.max))
{
log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit);
channel_reset_limit(&c->out_limit);
log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, c->out_limit.max);
channel_reset_limit(c, &c->out_limit, PLD_OUT);
/* Continue in feed - it will process routing table again from beginning */
c->refeed_count = 0;
@ -461,7 +460,8 @@ channel_stop_export(struct channel *c)
rt_feed_channel_abort(c);
c->export_state = ES_DOWN;
c->export_stats.routes = 0;
channel_reset_limit(c, &c->out_limit, PLD_OUT);
bmap_reset(&c->export_map, 1024);
bmap_reset(&c->export_reject_map, 1024);
}
@ -556,9 +556,9 @@ channel_do_start(struct channel *c)
memset(&c->export_stats, 0, sizeof(struct export_stats));
memset(&c->import_stats, 0, sizeof(struct import_stats));
channel_reset_limit(&c->rx_limit);
channel_reset_limit(&c->in_limit);
channel_reset_limit(&c->out_limit);
channel_reset_limit(c, &c->rx_limit, PLD_RX);
channel_reset_limit(c, &c->in_limit, PLD_IN);
channel_reset_limit(c, &c->out_limit, PLD_OUT);
CALL(c->channel->start, c);
}
@ -604,8 +604,8 @@ channel_do_down(struct channel *c)
rt_unlock_table(c->table);
c->proto->active_channels--;
if ((c->import_stats.routes + c->import_stats.filtered) != 0)
log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
if (c->in_limit.count || c->rx_limit.count)
bug("%s: Channel %s is down but still has some routes", c->proto->name, c->name);
// bmap_free(&c->export_map);
memset(&c->import_stats, 0, sizeof(struct import_stats));
@ -748,8 +748,8 @@ channel_request_reload(struct channel *c)
* Should this be done before reload_routes() hook?
* Perhaps, but routes are updated asynchronously.
*/
channel_reset_limit(&c->rx_limit);
channel_reset_limit(&c->in_limit);
channel_reset_limit(c, &c->rx_limit, PLD_RX);
channel_reset_limit(c, &c->in_limit, PLD_IN);
}
const struct channel_class channel_basic = {
@ -852,9 +852,10 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
/* Reconfigure channel fields */
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
c->rx_limit = cf->rx_limit;
c->in_limit = cf->in_limit;
c->out_limit = cf->out_limit;
channel_update_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
channel_update_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
channel_update_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit);
// c->ra_mode = cf->ra_mode;
c->merge_limit = cf->merge_limit;
@ -863,8 +864,6 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
c->in_keep_filtered = cf->in_keep_filtered;
c->rpki_reload = cf->rpki_reload;
channel_verify_limits(c);
/* Execute channel-specific reconfigure hook */
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed))
return 0;
@ -1785,88 +1784,104 @@ proto_set_message(struct proto *p, char *msg, int len)
}
static const char *
channel_limit_name(struct channel_limit *l)
{
const char *actions[] = {
[PLA_WARN] = "warn",
[PLA_BLOCK] = "block",
[PLA_RESTART] = "restart",
[PLA_DISABLE] = "disable",
};
static const char * channel_limit_name[] = {
[PLA_WARN] = "warn",
[PLA_BLOCK] = "block",
[PLA_RESTART] = "restart",
[PLA_DISABLE] = "disable",
};
return actions[l->action];
}
/**
* channel_notify_limit: notify about limit hit and take appropriate action
* @c: channel
* @l: limit being hit
* @dir: limit direction (PLD_*)
* @rt_count: the number of routes
*
* The function is called by the route processing core when limit @l
* is breached. It activates the limit and tooks appropriate action
* according to @l->action.
*/
void
channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count)
static void
channel_log_limit(struct channel *c, struct limit *l, int dir)
{
const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
struct proto *p = c->proto;
if (l->state == PLS_BLOCKED)
return;
/* For warning action, we want the log message every time we hit the limit */
if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
p->name, dir_name[dir], l->limit, channel_limit_name(l));
switch (l->action)
{
case PLA_WARN:
l->state = PLS_ACTIVE;
break;
case PLA_BLOCK:
l->state = PLS_BLOCKED;
break;
case PLA_RESTART:
case PLA_DISABLE:
l->state = PLS_BLOCKED;
if (p->proto_state == PS_UP)
proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
break;
}
log(L_WARN "Channel %s.%s hits route %s limit (%d), action: %s",
c->proto->name, c->name, dir_name[dir], l->max, channel_limit_name[c->limit_actions[dir]]);
}
static void
channel_verify_limits(struct channel *c)
channel_activate_limit(struct channel *c, struct limit *l, int dir)
{
struct channel_limit *l;
u32 all_routes = c->import_stats.routes + c->import_stats.filtered;
if (c->limit_active & (1 << dir))
return;
l = &c->rx_limit;
if (l->action && (all_routes > l->limit))
channel_notify_limit(c, l, PLD_RX, all_routes);
l = &c->in_limit;
if (l->action && (c->import_stats.routes > l->limit))
channel_notify_limit(c, l, PLD_IN, c->import_stats.routes);
l = &c->out_limit;
if (l->action && (c->export_stats.routes > l->limit))
channel_notify_limit(c, l, PLD_OUT, c->export_stats.routes);
c->limit_active |= (1 << dir);
channel_log_limit(c, l, dir);
}
static inline void
channel_reset_limit(struct channel_limit *l)
static int
channel_limit_warn(struct limit *l, void *data)
{
if (l->action)
l->state = PLS_INITIAL;
struct channel_limit_data *cld = data;
struct channel *c = cld->c;
int dir = cld->dir;
channel_log_limit(c, l, dir);
return 0;
}
static int
channel_limit_block(struct limit *l, void *data)
{
struct channel_limit_data *cld = data;
struct channel *c = cld->c;
int dir = cld->dir;
channel_activate_limit(c, l, dir);
return 1;
}
static const byte chl_dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
static int
channel_limit_down(struct limit *l, void *data)
{
struct channel_limit_data *cld = data;
struct channel *c = cld->c;
struct proto *p = c->proto;
int dir = cld->dir;
channel_activate_limit(c, l, dir);
if (p->proto_state == PS_UP)
proto_schedule_down(p, c->limit_actions[dir] == PLA_RESTART, chl_dir_down[dir]);
return 1;
}
static int (*channel_limit_action[])(struct limit *, void *) = {
[PLA_NONE] = NULL,
[PLA_WARN] = channel_limit_warn,
[PLA_BLOCK] = channel_limit_block,
[PLA_RESTART] = channel_limit_down,
[PLA_DISABLE] = channel_limit_down,
};
static void
channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf)
{
l->action = channel_limit_action[cf->action];
c->limit_actions[dir] = cf->action;
struct channel_limit_data cld = { .c = c, .dir = dir };
limit_update(l, &cld, cf->action ? cf->limit : ~((u32) 0));
}
static void
channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf)
{
channel_reset_limit(c, l, dir);
channel_update_limit(c, l, dir, cf);
}
static void
channel_reset_limit(struct channel *c, struct limit *l, int dir)
{
limit_reset(l);
c->limit_active &= ~(1 << dir);
}
static inline void
@ -2017,12 +2032,16 @@ channel_show_stats(struct channel *c)
struct import_stats *is = &c->import_stats;
struct export_stats *es = &c->export_stats;
u32 rx_routes = c->rx_limit.count;
u32 in_routes = c->in_limit.count;
u32 out_routes = c->out_limit.count;
if (c->in_keep_filtered)
cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
is->routes, is->filtered, es->routes, is->pref);
in_routes, (rx_routes - in_routes), out_routes, is->pref);
else
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
is->routes, es->routes, is->pref);
in_routes, out_routes, is->pref);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
@ -2040,13 +2059,13 @@ channel_show_stats(struct channel *c)
}
void
channel_show_limit(struct channel_limit *l, const char *dsc)
channel_show_limit(struct limit *l, const char *dsc, int active, int action)
{
if (!l->action)
return;
cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
cli_msg(-1006, " Action: %s", channel_limit_name(l));
cli_msg(-1006, " %-16s%d%s", dsc, l->max, active ? " [HIT]" : "");
cli_msg(-1006, " Action: %s", channel_limit_name[action]);
}
void
@ -2064,9 +2083,9 @@ channel_show_info(struct channel *c)
c->gr_lock ? " pending" : "",
c->gr_wait ? " waiting" : "");
channel_show_limit(&c->rx_limit, "Receive limit:");
channel_show_limit(&c->in_limit, "Import limit:");
channel_show_limit(&c->out_limit, "Export limit:");
channel_show_limit(&c->rx_limit, "Receive limit:", c->limit_active & (1 << PLD_RX), c->limit_actions[PLD_RX]);
channel_show_limit(&c->in_limit, "Import limit:", c->limit_active & (1 << PLD_IN), c->limit_actions[PLD_IN]);
channel_show_limit(&c->out_limit, "Export limit:", c->limit_active & (1 << PLD_OUT), c->limit_actions[PLD_OUT]);
if (c->channel_state != CS_DOWN)
channel_show_stats(c);

View File

@ -13,6 +13,7 @@
#include "lib/resource.h"
#include "lib/event.h"
#include "nest/route.h"
#include "nest/limit.h"
#include "conf/conf.h"
struct iface;
@ -134,8 +135,6 @@ struct proto_config {
/* Protocol statistics */
struct import_stats {
/* Import - from protocol to core */
u32 routes; /* Number of routes successfully imported to the (adjacent) routing table */
u32 filtered; /* Number of routes rejected in import filter but kept in the routing table */
u32 pref; /* Number of routes selected as best in the (adjacent) routing table */
u32 updates_received; /* Number of route updates received */
u32 updates_invalid; /* Number of route updates rejected as invalid */
@ -150,7 +149,6 @@ struct import_stats {
struct export_stats {
/* Export - from core to protocol */
u32 routes; /* Number of routes successfully exported to the protocol */
u32 updates_received; /* Number of route updates received */
u32 updates_rejected; /* Number of route updates rejected by protocol */
u32 updates_filtered; /* Number of route updates rejected by filters */
@ -277,7 +275,7 @@ void channel_graceful_restart_unlock(struct channel *c);
#define DEFAULT_GR_WAIT 240
void channel_show_limit(struct channel_limit *l, const char *dsc);
void channel_show_limit(struct limit *l, const char *dsc, int active, int action);
void channel_show_info(struct channel *c);
void channel_cmd_debug(struct channel *c, uint mask);
@ -432,18 +430,29 @@ extern struct proto_config *cf_dev_proto;
#define PLA_RESTART 4 /* Force protocol restart */
#define PLA_DISABLE 5 /* Shutdown and disable protocol */
#define PLS_INITIAL 0 /* Initial limit state after protocol start */
#define PLS_ACTIVE 1 /* Limit was hit */
#define PLS_BLOCKED 2 /* Limit is active and blocking new routes */
struct channel_limit {
u32 limit; /* Maximum number of prefixes */
u8 action; /* Action to take (PLA_*) */
u8 state; /* State of limit (PLS_*) */
};
void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count);
struct channel_limit_data {
struct channel *c;
int dir;
};
#define CLP__RX(_c) (&(_c)->rx_limit)
#define CLP__IN(_c) (&(_c)->in_limit)
#define CLP__OUT(_c) (&(_c)->out_limit)
#if 0
#define CHANNEL_LIMIT_LOG(_c, _dir, _op) log(L_TRACE "%s.%s: %s limit %s %u", (_c)->proto->name, (_c)->name, #_dir, _op, (CLP__##_dir(_c))->count)
#else
#define CHANNEL_LIMIT_LOG(_c, _dir, _op)
#endif
#define CHANNEL_LIMIT_PUSH(_c, _dir) ({ CHANNEL_LIMIT_LOG(_c, _dir, "push from"); struct channel_limit_data cld = { .c = (_c), .dir = PLD_##_dir }; limit_push(CLP__##_dir(_c), &cld); })
#define CHANNEL_LIMIT_POP(_c, _dir) ({ limit_pop(CLP__##_dir(_c)); CHANNEL_LIMIT_LOG(_c, _dir, "pop to"); })
/*
* Channels
@ -486,6 +495,7 @@ struct channel_config {
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
const struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep_filtered is active) */
struct channel_limit in_limit; /* Limit for importing routes from protocol */
@ -513,9 +523,13 @@ struct channel {
const struct filter *out_filter; /* Output filter */
struct bmap export_map; /* Keeps track which routes were really exported */
struct bmap export_reject_map; /* Keeps track which routes were rejected by export filter */
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct channel_limit in_limit; /* Input limit */
struct channel_limit out_limit; /* Output limit */
struct limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct limit in_limit; /* Input limit */
struct limit out_limit; /* Output limit */
u8 limit_actions[PLD_MAX]; /* Limit actions enum */
u8 limit_active; /* Flags for active limits */
struct event *feed_event; /* Event responsible for feeding */
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */

View File

@ -473,20 +473,16 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref
if (refeed && new)
c->refeed_count++;
/* Apply export limit */
struct channel_limit *l = &c->out_limit;
if (l->action && !old && new)
{
if (stats->routes >= l->limit)
channel_notify_limit(c, l, PLD_OUT, stats->routes);
if (l->state == PLS_BLOCKED)
if (!old && new)
if (CHANNEL_LIMIT_PUSH(c, OUT))
{
stats->updates_rejected++;
rte_trace_out(D_FILTERS, c, new, "rejected [limit]");
return;
}
}
if (!new && old)
CHANNEL_LIMIT_POP(c, OUT);
/* Apply export table */
struct rte_storage *old_exported = NULL;
@ -505,16 +501,10 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, rte *old, int ref
stats->withdraws_accepted++;
if (old)
{
bmap_clear(&c->export_map, old->id);
stats->routes--;
}
if (new)
{
bmap_set(&c->export_map, new->id);
stats->routes++;
}
if (p->debug & D_ROUTES)
{
@ -973,58 +963,53 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
int new_ok = rte_is_ok(new);
int old_ok = rte_is_ok(old);
struct channel_limit *l = &c->rx_limit;
if (l->action && !old && new && !c->in_table)
if (!c->in_table)
{
if (!old && new)
if (CHANNEL_LIMIT_PUSH(c, RX))
{
/* In receive limit the situation is simple, old is NULL so
we just free new and exit like nothing happened */
stats->updates_ignored++;
rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
return;
}
if (old && !new)
CHANNEL_LIMIT_POP(c, RX);
}
if (!old_ok && new_ok)
if (CHANNEL_LIMIT_PUSH(c, IN))
{
u32 all_routes = stats->routes + stats->filtered;
/* In import limit the situation is more complicated. We
shouldn't just drop the route, we should handle it like
it was filtered. We also have to continue the route
processing if old or new is non-NULL, but we should exit
if both are NULL as this case is probably assumed to be
already handled. */
if (all_routes >= l->limit)
channel_notify_limit(c, l, PLD_RX, all_routes);
stats->updates_ignored++;
rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
if (l->state == PLS_BLOCKED)
{
/* In receive limit the situation is simple, old is NULL so
we just free new and exit like nothing happened */
if (c->in_keep_filtered)
new->flags |= REF_FILTERED;
else
new = NULL;
stats->updates_ignored++;
rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
return;
}
/* Note that old && !new could be possible when
c->in_keep_filtered changed in the recent past. */
if (!old && !new)
return;
new_ok = 0;
goto skip_stats1;
}
l = &c->in_limit;
if (l->action && !old_ok && new_ok)
{
if (stats->routes >= l->limit)
channel_notify_limit(c, l, PLD_IN, stats->routes);
if (l->state == PLS_BLOCKED)
{
/* In import limit the situation is more complicated. We
shouldn't just drop the route, we should handle it like
it was filtered. We also have to continue the route
processing if old or new is non-NULL, but we should exit
if both are NULL as this case is probably assumed to be
already handled. */
stats->updates_ignored++;
rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
if (c->in_keep_filtered)
new->flags |= REF_FILTERED;
else
new = NULL;
/* Note that old && !new could be possible when
c->in_keep_filtered changed in the recent past. */
if (!old && !new)
return;
new_ok = 0;
goto skip_stats1;
}
}
if (old_ok && !new_ok)
CHANNEL_LIMIT_POP(c, IN);
if (new_ok)
stats->updates_accepted++;
@ -1039,11 +1024,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
skip_stats1:;
struct rte_storage *new_stored = new ? rte_store(new, net, table) : NULL;
if (new)
rte_is_filtered(new) ? stats->filtered++ : stats->routes++;
if (old)
rte_is_filtered(old) ? stats->filtered-- : stats->routes--;
if (table->config->sorted)
{
/* If routes are sorted, just insert new route to appropriate position */
@ -2332,6 +2312,9 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
goto drop_update;
}
if (!new)
CHANNEL_LIMIT_POP(c, RX);
/* Move iterator if needed */
if (*pos == c->reload_next_rte)
c->reload_next_rte = (*pos)->next;
@ -2342,7 +2325,18 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
rte_free(del, tab);
tab->rt_count--;
}
else if (!new)
else if (new)
{
if (CHANNEL_LIMIT_PUSH(c, RX))
{
/* Required by rte_trace_in() */
new->net = n;
rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
goto drop_update;
}
}
else
goto drop_withdraw;
if (!new)
@ -2353,22 +2347,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
return 1;
}
struct channel_limit *l = &c->rx_limit;
if (l->action && !*pos)
{
if (tab->rt_count >= l->limit)
channel_notify_limit(c, l, PLD_RX, tab->rt_count);
if (l->state == PLS_BLOCKED)
{
/* Required by rte_trace_in() */
new->net = n;
rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
goto drop_update;
}
}
/* Insert the new rte */
struct rte_storage *e = rte_store(new, net, tab);
e->rte.sender = c;

View File

@ -217,6 +217,9 @@ pipe_show_stats(struct pipe_proto *p)
struct import_stats *s2i = &p->sec->import_stats;
struct export_stats *s2e = &p->sec->export_stats;
u32 pri_routes = p->pri->in_limit.count;
u32 sec_routes = p->sec->in_limit.count;
/*
* Pipe stats (as anything related to pipes) are a bit tricky. There
* are two sets of stats - s1 for ahook to the primary routing and
@ -239,7 +242,7 @@ pipe_show_stats(struct pipe_proto *p)
*/
cli_msg(-1006, " Routes: %u imported, %u exported",
s1i->routes, s2i->routes);
pri_routes, sec_routes);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
s2e->updates_received, s2e->updates_rejected + s1i->updates_invalid,
@ -270,8 +273,12 @@ pipe_show_proto_info(struct proto *P)
cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
channel_show_limit(&p->pri->in_limit, "Import limit:");
channel_show_limit(&p->sec->in_limit, "Export limit:");
channel_show_limit(&p->pri->in_limit, "Import limit:",
(p->pri->limit_active & (1 << PLD_IN)), p->pri->limit_actions[PLD_IN]);
channel_show_limit(&p->sec->in_limit, "Export limit:",
(p->sec->limit_active & (1 << PLD_IN)), p->sec->limit_actions[PLD_IN]);
if (P->proto_state != PS_DOWN)
pipe_show_stats(p);