btime time;
struct f_prefix px;
struct proto_spec ps;
+ struct table_spec ts;
struct channel_limit cl;
struct timeformat *tf;
mpls_label_stack *mls;
const struct adata *bs;
struct aggr_item_node *ai;
+ struct logging_rate_targets *lrt;
+ struct tbf_config *tc;
+ enum tbf_targets tt;
}
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
/* Rate limiting */
+struct tbf_config {
+ u16 burst; /* Max number of tokens */
+ u16 rate; /* Rate of replenishment (tokens / sec) */
+ };
+
struct tbf {
btime timestamp; /* Last update */
u64 count; /* Available micro-tokens */
- u16 burst; /* Max number of tokens */
- u16 rate; /* Rate of replenishment (tokens / sec) */
u32 drop; /* Number of failed request since last successful */
+ struct tbf_config cf; /* Configuration */
+};
+
+enum tbf_targets {
+ TBF_INVALID = 0,
+ TBF_OSPF_PKT,
+ TBF_OSPF_LSA,
+ TBF_RIP_PKT,
+ TBF_RIP_RTE,
+ TBF_BABEL_PKT,
+ TBF_ALL
+};
+
+struct logging_rate_targets {
+ enum tbf_targets target;
+ struct logging_rate_targets *next;
+};
+
+struct cmd_logging_rate_info {
+ struct tbf_config *tbfc;
+ struct logging_rate_targets *targets;
+};
+
+
+struct table_spec {
+ const void *ptr;
+ int patt;
};
/* Default TBF values for rate limiting log messages */
-#define TBF_DEFAULT_LOG_LIMITS { .rate = 1, .burst = 5 }
+#define TBF_DEFAULT_LOG_LIMITS { .cf.rate = 1, .cf.burst = 5 }
int tbf_limit(struct tbf *f);
if (delta > 0)
{
- u64 next = f->count + delta * f->rate;
- u64 burst = (u64) f->burst << 20;
+ u64 next = f->count + delta * f->cf.rate;
+ u64 burst = (u64) f->cf.burst << 20;
f->count = MIN(next, burst);
f->timestamp += delta;
}
#include "nest/mpls.h"
#include "lib/lists.h"
#include "lib/mac.h"
+#include "lib/birdlib.h"
CF_DEFINES
CF_KEYWORDS(CHECK, LINK)
CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
CF_KEYWORDS(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS)
+CF_KEYWORDS(ASPA_PROVIDERS)
+CF_KEYWORDS(LOGGING, RATE)
+CF_KEYWORDS( OSPF_PKT, OSPF_LSA, RIP_PKT, RIP_RTE, BABEL_PKT)
/* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
%type <sd> sym_args
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type net_type_base tos password_algorithm
%type <ps> proto_patt proto_patt2
+%type <ts> table_patt
%type <cc> channel_start proto_channel
%type <cl> limit_spec
%type <net> r_args_for_val
%type <net_ptr> r_args_for
%type <t> channel_sym
%type <c> channel_arg
+%type <lrt> logging_rate_targets
+%type <tc> logging_rate
+%type <tt> tbf_target
CF_GRAMMAR
table_start: net_type TABLE symbol {
this_table = rt_new_table($3, $1);
+ this_table->log_tbf_cf.rate = 1;
+ this_table->log_tbf_cf.burst = 5;
}
;
| MAX SETTLE TIME expr_us { this_table->max_settle_time = $4; }
| GC THRESHOLD expr { this_table->gc_threshold = $3; }
| GC PERIOD expr_us { this_table->gc_period = (uint) $3; if ($3 > 3600 S_) cf_error("GC period must be at most 3600 s"); }
+ | LOGGING RATE expr expr { this_table->log_tbf_cf.rate = $3; this_table->log_tbf_cf.burst = $4; }
;
table_opts:
CF_CLI(DEBUG, debug_args, (<protocol> | <channel> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])
{ /* Done in debug_args */ };
+logging_rate_target: PIPE ;
+
+tbf_target:
+ OSPF_PKT { $$ = TBF_OSPF_PKT; }
+ |OSPF_LSA { $$ = TBF_OSPF_LSA; }
+ |RIP_PKT { $$ = TBF_RIP_PKT; }
+ |RIP_RTE { $$ = TBF_RIP_RTE; }
+ |BABEL_PKT { $$ = TBF_BABEL_PKT; }
+ |ALL { $$ = TBF_ALL; }
+
+logging_rate_targets:
+ logging_rate_targets tbf_target {
+ $$ = (struct logging_rate_targets *) cfg_allocz(sizeof(struct logging_rate_targets));
+ $$->next = $1;
+ $$->target = $2;
+ } | tbf_target {
+ $$ = cfg_allocz(sizeof(struct logging_rate_targets));
+ $$->next = NULL;
+ $$->target = $1;
+ };
+
+logging_rate: expr expr
+{
+ $$ = cfg_allocz(sizeof(struct tbf_config));
+ $$->rate = $1;
+ $$->burst = $2;
+};
+
+CF_CLI(LOGGING RATE PROTOCOL, proto_patt logging_rate_targets logging_rate, (<protocol> | \"<pattern>\" | all) targets <rate> <burst>, [[Set logging rate for given protocols and targets]])
+{
+ struct cmd_logging_rate_info info = {
+ .tbfc = $6,
+ .targets = $5,
+ };
+ proto_apply_cmd($4, proto_cmd_logging_rate, 1, (uintptr_t) &info);
+};
+
+CF_CLI(LOGGING RATE TABLE, table_patt logging_rate, (<table> | \"<pattern>\" | all) <rate> <burst>, [[Set logging rate for given tables and targets]])
+{
+ table_logging_cmd($4, $5);
+};
+
+
CF_CLI_OPT(DEBUG ALL)
CF_CLI_OPT(DEBUG OFF)
CF_CLI_OPT(DEBUG STATES)
| TEXT { $$.ptr = $1; $$.patt = 1; }
;
+table_patt:
+ CF_SYM_KNOWN { cf_assert_symbol($1, SYM_TABLE); $$.ptr = $1; $$.patt = 0; }
+ | ALL { $$.ptr = NULL; $$.patt = 1; }
+ | TEXT { $$.ptr = $1; $$.patt = 1; }
+ ;
+
dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); } ;
dynamic_attr: MPLS_LABEL { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_MPLS_LABEL); } ;
p->mrtdump = mask;
}
+void
+proto_cmd_logging_rate(struct proto *p, uintptr_t arg, int cnt UNUSED)
+{
+ if (p->set_logging_rate)
+ p->set_logging_rate(p, arg);
+ else
+ cli_msg(9002, "protocol %s does not support logging rate changes", p->name);
+}
+
static void
proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
{
void (*rte_insert)(struct network *, struct rte *);
void (*rte_remove)(struct network *, struct rte *);
u32 (*rte_igp_metric)(struct rte *);
+ void (*set_logging_rate)(struct proto *P, uintptr_t arg);
/* Hic sunt protocol-specific data */
};
void proto_cmd_reload(struct proto *, uintptr_t, int);
void proto_cmd_debug(struct proto *, uintptr_t, int);
void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
+void proto_cmd_logging_rate(struct proto *, uintptr_t, int);
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg);
struct proto *proto_get_named(struct symbol *, struct protocol *);
byte trie_used; /* Rtable has attached trie */
btime min_settle_time; /* Minimum settle time for notifications */
btime max_settle_time; /* Maximum settle time for notifications */
+ struct tbf_config log_tbf_cf; /* Config logging rate for rtable */
};
typedef struct rtable {
list flowspec_links; /* List of flowspec links, src for NET_IPx and dst for NET_FLOWx */
struct f_trie *flowspec_trie; /* Trie for evaluation of flowspec notifications */
// struct mpls_domain *mpls_domain; /* Label allocator for MPLS */
+ struct tbf log_tbf; /* Actual logging rate for rtable (might be changed in cmd) */
} rtable;
struct rt_subscription {
void rta_dump_all(void);
void rta_show(struct cli *, rta *);
+void table_logging_cmd(struct table_spec ts, struct tbf_config *rate);
+
u32 rt_get_igp_metric(rte *rt);
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
#include "lib/string.h"
#include "lib/alloca.h"
#include "lib/flowspec.h"
+#include "nest/cli.h"
#ifdef CONFIG_BGP
#include "proto/bgp/bgp.h"
struct proto *p = c->proto;
struct rtable *table = c->table;
struct proto_stats *stats = &c->stats;
- static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
rte *before_old = NULL;
rte *old_best = net->routes;
rte *old = NULL;
{
if (new)
{
- log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s",
+ log_rl(&table->log_tbf, L_ERR "Pipe collision detected when sending %N to table %s",
net->n.addr, table->name);
rte_free_quick(new);
}
t->config = cf;
t->addr_type = cf->addr_type;
t->debug = cf->debug;
+ t->log_tbf.cf.rate = cf->log_tbf_cf.rate;
+ t->log_tbf.cf.burst = cf->log_tbf_cf.burst;
fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
tab->name = new->name;
tab->config = new;
tab->debug = new->debug;
+ tab->log_tbf.cf.rate = new->log_tbf_cf.rate;
+ tab->log_tbf.cf.burst = new->log_tbf_cf.burst;
return 1;
}
return he;
}
+void
+cmd_logging_rate(rtable *table, struct tbf_config *rate)
+{
+ table->log_tbf.cf.rate = rate->rate;
+ table->log_tbf.cf.burst = rate->burst;
+}
+
+void
+table_logging_cmd(struct table_spec ts, struct tbf_config *rate)
+{
+ if (ts.patt)
+ {
+ const char *patt = (void *) ts.ptr;
+ int cnt = 0;
+ rtable *t;
+ node *n;
+
+ WALK_LIST2(t, n, routing_tables, n)
+ if (!ts.ptr || patmatch(patt, t->name))
+ {
+ cmd_logging_rate(t, rate);
+ cnt++;
+ }
+
+ if (!cnt)
+ cli_msg(8003, "No tables match");
+ else
+ cli_msg(0, "");
+ }
+ else
+ {
+ const struct symbol *s = (struct symbol*) ts.ptr;
+ if (s->table->table)
+ {
+ cmd_logging_rate(s->table->table, rate);
+ cli_msg(0, "");
+ }
+ else
+ cli_msg(9002, "%s does not exist", s->name);
+ }
+}
/*
* Documentation for functions declared inline in route.h
p->msg_slab = sl_new(P->pool, sizeof(struct babel_msg_node));
p->seqno_slab = sl_new(P->pool, sizeof(struct babel_seqno_request));
- p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
+ P->set_logging_rate = babel_set_logging_rate;
+ p->log_pkt_tbf = (struct tbf){ .cf.rate = cf->log_pkt_tbf.rate, .cf.burst = cf->log_pkt_tbf.burst };
return PS_UP;
}
p->p.cf = CF;
babel_reconfigure_ifaces(p, new);
+ p->log_pkt_tbf.cf.rate = new->log_pkt_tbf.rate;
+ p->log_pkt_tbf.cf.burst = new->log_pkt_tbf.burst;
babel_trigger_update(p);
babel_kick_timer(p);
return 1;
}
+void
+babel_set_logging_rate(struct proto *P, uintptr_t arg)
+{
+ struct babel_proto *p = (void *) P;
+ struct cmd_logging_rate_info *info = (struct cmd_logging_rate_info*) arg;
+ struct logging_rate_targets *targets = info->targets;
+ while (targets)
+ {
+ if (targets->target == TBF_BABEL_PKT || targets->target == TBF_ALL)
+ {
+ p->log_pkt_tbf.cf.rate = info->tbfc->rate;
+ p->log_pkt_tbf.cf.burst = info->tbfc->burst;
+ }
+ else
+ cli_msg(9002, "protocol %s: wrong logging rate change type for babel protocol", P->name);
+ targets = targets->next;
+ }
+}
+
+
struct protocol proto_babel = {
.name = "Babel",
struct channel_config *ip4_channel;
struct channel_config *ip6_channel;
+
+ struct tbf_config log_pkt_tbf;
};
struct babel_iface_config {
void babel_auth_reset_index(struct babel_iface *ifa);
int babel_auth_check_pc(struct babel_iface *ifa, struct babel_msg_auth *msg);
+void babel_set_logging_rate(struct proto *P, uintptr_t arg);
/* packets.c */
void babel_enqueue(union babel_msg *msg, struct babel_iface *ifa);
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
- EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS, COST, DELAY)
+ EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS, COST, DELAY,
+ PKT, LOGGING, RATE, BURST)
CF_GRAMMAR
this_proto = proto_config_new(&proto_babel, $1);
init_list(&BABEL_CFG->iface_list);
BABEL_CFG->hold_time = 1 S_;
+ BABEL_CFG->log_pkt_tbf.rate = 1;
+ BABEL_CFG->log_pkt_tbf.burst = 5;
};
babel_proto_item:
| proto_channel
| INTERFACE babel_iface
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
+ | LOGGING RATE PKT expr expr { BABEL_CFG->log_pkt_tbf.rate = $4; BABEL_CFG->log_pkt_tbf.burst = $5; }
;
babel_proto_opts:
CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
CF_KEYWORDS(MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838, VPN, PE, ADDRESS)
CF_KEYWORDS(GRACEFUL, RESTART, AWARE, TIME)
+CF_KEYWORDS(PKT, LSA, LOGGING, RATE, BURST)
%type <ld> lsadb_args
%type <i> ospf_variant ospf_af_mc nbma_eligible
OSPF_CFG->af_ext = !$2;
OSPF_CFG->gr_mode = OSPF_GR_AWARE;
OSPF_CFG->gr_time = OSPF_DEFAULT_GR_TIME;
+ OSPF_CFG->log_pkt_tbf.rate = 1;
+ OSPF_CFG->log_lsa_tbf.rate = 4;
+ OSPF_CFG->log_pkt_tbf.burst = 5;
+ OSPF_CFG->log_lsa_tbf.burst = 20;
};
ospf_proto:
| TICK expr { OSPF_CFG->tick = $2; if($2 <= 0) cf_error("Tick must be greater than zero"); }
| INSTANCE ID expr { OSPF_CFG->instance_id = $3; OSPF_CFG->instance_id_set = 1; if ($3 > 255) cf_error("Instance ID must be in range 0-255"); }
| ospf_area
+ | LOGGING RATE PKT expr expr { OSPF_CFG->log_pkt_tbf.rate = $4; OSPF_CFG->log_pkt_tbf.burst = $5; }
+ | LOGGING RATE LSA expr expr { OSPF_CFG->log_lsa_tbf.rate = $4; OSPF_CFG->log_lsa_tbf.burst = $4; }
;
ospf_area_start: AREA idval {
| lsadb_args CF_SYM_KNOWN { cf_assert_symbol($2, SYM_PROTO); $$ = $1; $$->proto = (struct ospf_proto *) proto_get_named($2, &proto_ospf); }
;
+logging_rate_target: OSPF_PKT | OSPF_LSA ;
+
CF_CODE
CF_END
p->flood_event = ev_new_init(P->pool, ospf_flood_event, p);
- p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
- p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 };
+ p->log_pkt_tbf = (struct tbf){ .cf.rate = c->log_pkt_tbf.rate, .cf.burst = c->log_pkt_tbf.burst };
+ p->log_lsa_tbf = (struct tbf){ .cf.rate = c->log_lsa_tbf.rate, .cf.burst = c->log_lsa_tbf.burst };
+ P->set_logging_rate = ospf_set_logging_rate; // for setting logging tbf temporarily from cmd
/* Lock the channel when in GR recovery mode */
if (p->p.gr_recovery && (p->gr_mode == OSPF_GR_ABLE))
return 1;
}
+void
+ospf_set_logging_rate(struct proto *P, uintptr_t arg)
+{
+ struct ospf_proto *p = (void *) P;
+ struct cmd_logging_rate_info *info = (struct cmd_logging_rate_info*) arg;
+ struct logging_rate_targets *targets = info->targets;
+ while (targets)
+ {
+ if (targets->target == TBF_OSPF_PKT || targets->target == TBF_ALL)
+ {
+ p->log_pkt_tbf.cf.rate = info->tbfc->rate;
+ p->log_pkt_tbf.cf.burst = info->tbfc->burst;
+ }
+ else if (targets->target == TBF_OSPF_LSA || targets->target == TBF_ALL)
+ {
+ p->log_lsa_tbf.cf.rate = info->tbfc->rate;
+ p->log_lsa_tbf.cf.burst = info->tbfc->burst;
+ }
+ else
+ cli_msg(9002, "protocol %s: wrong logging rate change type for ospf protocol", P->name);
+ targets = targets->next;
+ }
+}
+
void
ospf_sh_neigh(struct proto *P, const char *iff)
uint ecmp;
list area_list; /* list of area configs (struct ospf_area_config) */
list vlink_list; /* list of configured vlinks (struct ospf_iface_patt) */
+ struct tbf_config log_pkt_tbf;
+ struct tbf_config log_lsa_tbf;
};
struct ospf_area_config
void ospf_iface_shutdown(struct ospf_iface *ifa);
int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen);
int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new);
+void ospf_set_logging_rate(struct proto *P, uintptr_t arg);
void ospf_reconfigure_ifaces(struct ospf_proto *p);
void ospf_open_vlink_sk(struct ospf_proto *p);
struct nbma_node *find_nbma_node_(list *nnl, ip_addr ip);
PASSIVE, VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO,
TIME, BFD, AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5,
TTL, SECURITY, RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK,
- DEMAND, CIRCUIT, RIP_METRIC, RIP_TAG)
+ DEMAND, CIRCUIT, RIP_METRIC, RIP_TAG, PKT, RTE, LOGGING, RATE, BURST)
%type <i> rip_variant rip_auth
RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
RIP_CFG->min_timeout_time = 60 S_;
RIP_CFG->max_garbage_time = 60 S_;
+ RIP_CFG->log_pkt_tbf.rate = 1;
+ RIP_CFG->log_rte_tbf.rate = 4;
+ RIP_CFG->log_pkt_tbf.burst = 5;
+ RIP_CFG->log_rte_tbf.burst = 20;
};
rip_proto_item:
| ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; }
| INFINITY expr { RIP_CFG->infinity = $2; }
| INTERFACE rip_iface
+ | LOGGING RATE PKT expr expr { RIP_CFG->log_pkt_tbf.rate = $4; RIP_CFG->log_pkt_tbf.burst = $5; }
+ | LOGGING RATE RTE expr expr { RIP_CFG->log_rte_tbf.rate = $4; RIP_CFG->log_rte_tbf.burst = $4; }
;
rip_proto_opts:
CF_CLI(SHOW RIP NEIGHBORS, optproto opttext, [<name>] [\"<interface>\"], [[Show information about RIP neighbors]])
{ PROTO_WALK_CMD($4, &proto_rip, p) rip_show_neighbors(p, $5); };
+logging_rate_target: RIP_PKT | RIP_RTE ;
+
CF_CODE
p->infinity = cf->infinity;
p->triggered = 0;
- p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
- p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 };
+ p->log_pkt_tbf = (struct tbf){ .cf.rate = cf->log_pkt_tbf.rate, .cf.burst = cf->log_pkt_tbf.burst };
+ p->log_rte_tbf = (struct tbf){ .cf.rate = cf->log_rte_tbf.rate, .cf.burst = cf->log_pkt_tbf.burst };
+ P->set_logging_rate = rip_set_logging_rate;
tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time));
p->p.cf = CF;
p->ecmp = new->ecmp;
rip_reconfigure_ifaces(p, new);
+ p->log_pkt_tbf.cf.rate = new->log_pkt_tbf.rate;
+ p->log_pkt_tbf.cf.burst = new->log_pkt_tbf.burst;
+ p->log_rte_tbf.cf.rate = new->log_rte_tbf.rate;
+ p->log_rte_tbf.cf.burst = new->log_rte_tbf.burst;
p->rt_reload = 1;
rip_kick_timer(p);
return 1;
}
+void
+rip_set_logging_rate(struct proto *P, uintptr_t arg)
+{
+ struct rip_proto *p = (void *) P;
+ struct cmd_logging_rate_info *info = (struct cmd_logging_rate_info*) arg;
+ struct logging_rate_targets *targets = info->targets;
+ while (targets)
+ {
+ if (targets->target == TBF_RIP_PKT || targets->target == TBF_ALL)
+ {
+ p->log_pkt_tbf.cf.rate = info->tbfc->rate;
+ p->log_pkt_tbf.cf.burst = info->tbfc->burst;
+ }
+ else if (targets->target == TBF_RIP_RTE || targets->target == TBF_ALL)
+ {
+ p->log_rte_tbf.cf.rate = info->tbfc->rate;
+ p->log_rte_tbf.cf.burst = info->tbfc->burst;
+ }
+ else
+ cli_msg(9002, "protocol %s: wrong logging rate change type for rip protocol", P->name);
+ targets = targets->next;
+ }
+}
+
static void
rip_get_route_info(rte *rte, byte *buf)
{
btime min_timeout_time; /* Minimum of interface timeout_time */
btime max_garbage_time; /* Maximum of interface garbage_time */
+ struct tbf_config log_pkt_tbf;
+ struct tbf_config log_rte_tbf;
};
struct rip_iface_config
void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n);
void rip_show_interfaces(struct proto *P, const char *iff);
void rip_show_neighbors(struct proto *P, const char *iff);
+void rip_set_logging_rate(struct proto *P, uintptr_t arg);
/* packets.c */
void rip_send_request(struct rip_proto *p, struct rip_iface *ifa);