From 9861dba5230da539e6ce7d2b6baa4f2631556d09 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 23 Nov 2018 00:18:11 +0100 Subject: [PATCH 1/4] Static: Allow multiple routes to the same network with different preference --- proto/static/config.Y | 2 ++ proto/static/static.c | 27 ++++++++++++++++++++------- proto/static/static.h | 1 + 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/proto/static/config.Y b/proto/static/config.Y index 66e5ea4c..3bb2cd7e 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -135,6 +135,8 @@ stat_route: stat_route_item: cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); } + | PREFERENCE expr ';' { this_srt->preference = $2; check_u16($2); } + | DISTANCE expr ';' { this_srt->preference = $2; check_u16($2); } ; stat_route_opts: diff --git a/proto/static/static.c b/proto/static/static.c index ede4c734..a47fdbd5 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -53,7 +53,7 @@ static void static_announce_rte(struct static_proto *p, struct static_route *r) { rta *a = allocz(RTA_MAX_SIZE); - a->src = p->p.main_source; + a->src = rt_get_source(&p->p, r->preference); a->source = RTS_STATIC; a->scope = SCOPE_UNIVERSE; a->dest = r->dest; @@ -101,11 +101,13 @@ static_announce_rte(struct static_proto *p, struct static_route *r) /* We skip rta_lookup() here */ rte *e = rte_get_temp(a); e->pflags = 0; + e->pref = r->preference; if (r->cmds) f_eval_rte(r->cmds, &e, static_lp); - rte_update(&p->p, r->net, e); + e->pref = r->preference; /* Avoid preference from filter */ + rte_update2(p->p.main_channel, r->net, e, a->src); r->state = SRS_CLEAN; if (r->cmds) @@ -117,7 +119,7 @@ withdraw: if (r->state == SRS_DOWN) return; - rte_update(&p->p, r->net, NULL); + rte_update2(p->p.main_channel, r->net, NULL, a->src); r->state = SRS_DOWN; } @@ -250,7 +252,7 @@ static void static_remove_rte(struct static_proto *p, struct static_route *r) { if (r->state) - rte_update(&p->p, r->net, NULL); + rte_update2(p->p.main_channel, r->net, NULL, rt_get_source(&p->p, r->preference)); static_reset_rte(p, r); } @@ -380,8 +382,13 @@ static_postconfig(struct proto_config *CF) cc->table : cf->c.global->def_tables[NET_IP6]; WALK_LIST(r, cf->routes) + { if (r->net && (r->net->type != CF->net_type)) cf_error("Route %N incompatible with channel type", r->net); + + if (!r->preference) + r->preference = cc->preference; + } } static struct proto * @@ -488,11 +495,17 @@ static_dump(struct proto *P) #define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL ) +static inline int srt_equal(struct static_route *a, struct static_route *b) +{ return net_equal(a->net, b->net) && (a->preference == b->preference); } + +static inline int srt_compare(struct static_route *a, struct static_route *b) +{ return net_compare(a->net, b->net) ?: uint_cmp(a->preference, b->preference); } + static inline int static_cmp_rte(const void *X, const void *Y) { struct static_route *x = *(void **)X, *y = *(void **)Y; - return net_compare(x->net, y->net); + return srt_compare(x, y); } static int @@ -521,7 +534,7 @@ static_reconfigure(struct proto *P, struct proto_config *CF) /* Reconfigure initial matching sequence */ for (or = HEAD(o->routes), nr = HEAD(n->routes); - NODE_VALID(or) && NODE_VALID(nr) && net_equal(or->net, nr->net); + NODE_VALID(or) && NODE_VALID(nr) && srt_equal(or, nr); or = NODE_NEXT(or), nr = NODE_NEXT(nr)) static_reconfigure_rte(p, or, nr); @@ -552,7 +565,7 @@ static_reconfigure(struct proto *P, struct proto_config *CF) while ((orpos < ornum) && (nrpos < nrnum)) { - int x = net_compare(orbuf[orpos]->net, nrbuf[nrpos]->net); + int x = srt_compare(orbuf[orpos], nrbuf[nrpos]); if (x < 0) static_remove_rte(p, orbuf[orpos++]); else if (x > 0) diff --git a/proto/static/static.h b/proto/static/static.h index a3c30b87..ec9dffb3 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -40,6 +40,7 @@ struct static_route { struct static_route *mp_head; /* First nexthop of this route */ struct static_route *mp_next; /* Nexthops for multipath routes */ struct f_inst *cmds; /* List of commands for setting attributes */ + u16 preference; /* Route preference */ byte dest; /* Destination type (RTD_*) */ byte state; /* State of route announcement (SRS_*) */ byte active; /* Next hop is active (nbr/iface/BFD available) */ From 80a4c23b1e7e94f6ded7fba4f0e2719d2c9704e0 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sat, 24 Nov 2018 01:52:13 +0100 Subject: [PATCH 2/4] Unix: Implement log file size limit / log rotation Allow to specify log file size limit and ensure that log file is rotated to secondary name to avoid exceeding of log size limit. The patch also fixes a bug related to keeping old fds open after reconfiguration and using old fds after 'configure undo'. --- sysdep/unix/config.Y | 38 ++++++++++++------- sysdep/unix/io.c | 29 +++++++++++---- sysdep/unix/log.c | 88 ++++++++++++++++++++++++++++++++++++++++++-- sysdep/unix/unix.h | 10 ++++- 4 files changed, 138 insertions(+), 27 deletions(-) diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index ccca4a62..253127a7 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -11,13 +11,16 @@ CF_HDR #include "sysdep/unix/unix.h" #include +CF_DEFINES + +static struct log_config *this_log; + CF_DECLS CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING) %type log_mask log_mask_list log_cat cfg_timeout -%type log_file %type cfg_name %type timeformat_which %type syslog_name @@ -26,11 +29,11 @@ CF_GRAMMAR CF_ADDTO(conf, log_config) -log_config: LOG log_file log_mask ';' { - struct log_config *c = cfg_allocz(sizeof(struct log_config)); - c->fh = $2; - c->mask = $3; - add_tail(&new_config->logfiles, &c->n); +log_begin: { this_log = cfg_allocz(sizeof(struct log_config)); }; + +log_config: LOG log_begin log_file log_mask ';' { + this_log->mask = $4; + add_tail(&new_config->logfiles, &this_log->n); } ; @@ -39,14 +42,21 @@ syslog_name: | { $$ = bird_name; } ; +log_limit: + /* empty */ + | expr text { this_log->limit = $1; this_log->backup = $2; } + ; + log_file: - text { - FILE *f = tracked_fopen(new_config->pool, $1, "a"); - if (!f) cf_error("Unable to open log file `%s': %m", $1); - $$ = f; + text log_limit { + this_log->rf = rf_open(new_config->pool, $1, "a"); + if (!this_log->rf) cf_error("Unable to open log file '%s': %m", $1); + this_log->fh = rf_file(this_log->rf); + this_log->pos = -1; + this_log->filename = $1; } - | SYSLOG syslog_name { $$ = NULL; new_config->syslog_name = $2; } - | STDERR { $$ = stderr; } + | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; } + | STDERR { this_log->fh = stderr; } ; log_mask: @@ -77,9 +87,9 @@ CF_ADDTO(conf, mrtdump_base) mrtdump_base: MRTDUMP PROTOCOLS mrtdump_mask ';' { new_config->proto_default_mrtdump = $3; } | MRTDUMP text ';' { - FILE *f = tracked_fopen(new_config->pool, $2, "a"); + struct rfile *f = rf_open(new_config->pool, $2, "a"); if (!f) cf_error("Unable to open MRTDump file '%s': %m", $2); - new_config->mrtdump_file = fileno(f); + new_config->mrtdump_file = rf_fileno(f); } ; diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 012deaf0..670cdd37 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -55,6 +55,7 @@ this to gen small latencies */ #define MAX_RX_STEPS 4 + /* * Tracked Files */ @@ -89,17 +90,29 @@ static struct resclass rf_class = { NULL }; -void * -tracked_fopen(pool *p, char *name, char *mode) +struct rfile * +rf_open(pool *p, char *name, char *mode) { FILE *f = fopen(name, mode); - if (f) - { - struct rfile *r = ralloc(p, &rf_class); - r->f = f; - } - return f; + if (!f) + return NULL; + + struct rfile *r = ralloc(p, &rf_class); + r->f = f; + return r; +} + +void * +rf_file(struct rfile *f) +{ + return f->f; +} + +int +rf_fileno(struct rfile *f) +{ + return fileno(f->f); } diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index f9dccc39..960b4c1c 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -86,6 +88,54 @@ static char *class_names[] = { "BUG" }; +static inline off_t +log_size(struct log_config *l) +{ + struct stat st; + return (!fstat(rf_fileno(l->rf), &st) && S_ISREG(st.st_mode)) ? st.st_size : 0; +} + +static void +log_close(struct log_config *l) +{ + rfree(l->rf); + l->rf = NULL; + l->fh = NULL; +} + +static int +log_open(struct log_config *l) +{ + l->rf = rf_open(config->pool, l->filename, "a"); + if (!l->rf) + { + /* Well, we cannot do much in case of error as log is closed */ + l->mask = 0; + return -1; + } + + l->fh = rf_file(l->rf); + l->pos = log_size(l); + + return 0; +} + +static int +log_rotate(struct log_config *l) +{ + log_close(l); + + /* If we cannot rename the logfile, we at least try to delete it + in order to continue logging and not exceeding logfile size */ + if ((rename(l->filename, l->backup) < 0) && + (unlink(l->filename) < 0)) + { + l->mask = 0; + return -1; + } + + return log_open(l); +} /** * log_commit - commit a log message @@ -121,6 +171,22 @@ log_commit(int class, buffer *buf) { byte tbuf[TM_DATETIME_BUFFER_SIZE]; tm_format_real_time(tbuf, config->tf_log.fmt1, current_real_time()); + + if (l->limit) + { + off_t msg_len = strlen(tbuf) + strlen(class_names[class]) + + (buf->pos - buf->start) + 5; + + if (l->pos < 0) + l->pos = log_size(l); + + if (l->pos + msg_len > l->limit) + if (log_rotate(l) < 0) + continue; + + l->pos += msg_len; + } + fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]); } fputs(buf->start, l->fh); @@ -279,12 +345,26 @@ default_log_list(int debug, int init, char **syslog_name) } void -log_switch(int debug, list *l, char *new_syslog_name) +log_switch(int debug, list *logs, char *new_syslog_name) { - if (!l || EMPTY_LIST(*l)) - l = default_log_list(debug, !l, &new_syslog_name); + struct log_config *l; - current_log_list = l; + if (!logs || EMPTY_LIST(*logs)) + logs = default_log_list(debug, !logs, &new_syslog_name); + + /* Close the logs to avoid pinning them on disk when deleted */ + if (current_log_list) + WALK_LIST(l, *current_log_list) + if (l->rf) + log_close(l); + + /* Reopen the logs, needed for 'configure undo' */ + if (logs) + WALK_LIST(l, *logs) + if (l->filename && !l->rf) + log_open(l); + + current_log_list = logs; #ifdef HAVE_SYSLOG_H if (current_syslog_name && new_syslog_name && diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index cb12fad8..12306167 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -14,6 +14,7 @@ struct pool; struct iface; struct birdsock; +struct rfile; /* main.c */ @@ -102,7 +103,9 @@ void io_init(void); void io_loop(void); void io_log_dump(void); int sk_open_unix(struct birdsock *s, char *name); -void *tracked_fopen(struct pool *, char *name, char *mode); +struct rfile *rf_open(struct pool *, char *name, char *mode); +void *rf_file(struct rfile *f); +int rf_fileno(struct rfile *f); void test_old_bird(char *path); /* krt.c bits */ @@ -119,6 +122,11 @@ struct log_config { node n; uint mask; /* Classes to log */ void *fh; /* FILE to log to, NULL=syslog */ + struct rfile *rf; /* Resource for log file */ + char *filename; /* Log filename */ + char *backup; /* Secondary filename (for log rotation) */ + off_t pos; /* Position/size of current log */ + off_t limit; /* Log size limit */ int terminal_flag; }; From 8972a08fabfb6bf074480927d79f7b2869f52697 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 30 Nov 2018 22:10:14 +0100 Subject: [PATCH 3/4] BGP: Statistics Count message rx/tx, enqueued buckets/prefixes and recoverable error conditions. Show them in 'show protocol all' --- proto/bgp/attrs.c | 21 +++++++++++++-------- proto/bgp/bgp.c | 14 ++++++++++++++ proto/bgp/bgp.h | 11 +++++++++++ proto/bgp/packets.c | 18 ++++++++++++++++-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 0f41f818..e53b09d8 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1094,8 +1094,10 @@ bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len) ADVANCE(pos, len, alen); } +#define FAIL(KEY) ({ STATS(KEY); goto fail; }) + if (s->err_withdraw) - goto withdraw; + FAIL(bad_attribute); /* If there is no reachability NLRI, we are finished */ if (!s->ip_reach_len && !s->mp_reach_len) @@ -1104,10 +1106,10 @@ bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len) /* Handle missing mandatory attributes; RFC 7606 3 (d) */ if (!BIT32_TEST(s->attrs_seen, BA_ORIGIN)) - { REPORT(NO_MANDATORY, "ORIGIN"); goto withdraw; } + { REPORT(NO_MANDATORY, "ORIGIN"); FAIL(no_mandatory); } if (!BIT32_TEST(s->attrs_seen, BA_AS_PATH)) - { REPORT(NO_MANDATORY, "AS_PATH"); goto withdraw; } + { REPORT(NO_MANDATORY, "AS_PATH"); FAIL(no_mandatory); } /* When receiving attributes from non-AS4-aware BGP speaker, we have to reconstruct AS_PATH and AGGREGATOR attributes; RFC 6793 4.2.3 */ @@ -1116,19 +1118,19 @@ bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len) /* Reject routes with our ASN in AS_PATH attribute */ if (bgp_as_path_loopy(p, attrs, p->local_as)) - goto withdraw; + FAIL(loopy); /* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4.0 */ if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as)) - goto withdraw; + FAIL(loopy); /* Reject routes with our Router ID in ORIGINATOR_ID attribute; RFC 4456 8 */ if (p->is_internal && bgp_originator_id_loopy(p, attrs)) - goto withdraw; + FAIL(loopy); /* Reject routes with our Cluster ID in CLUSTER_LIST attribute; RFC 4456 8 */ if (p->rr_client && bgp_cluster_list_loopy(p, attrs)) - goto withdraw; + FAIL(loopy); /* If there is no local preference, define one */ if (!BIT32_TEST(s->attrs_seen, BA_LOCAL_PREF)) @@ -1141,8 +1143,11 @@ framing_error: /* RFC 7606 4 - handle attribute framing errors */ REPORT("Malformed attribute list - framing error (%u/%u) at %d", alen, len, (int) (pos - s->attrs)); + FAIL(bad_alist); -withdraw: +#undef FAIL + +fail: /* RFC 7606 5.2 - handle missing NLRI during errors */ if (!s->ip_reach_len && !s->mp_reach_len) bgp_parse_error(s, 1); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 9db26050..0e60d88d 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1328,6 +1328,8 @@ bgp_start(struct proto *P) p->source_addr = p->cf->local_ip; p->link_addr = IPA_NONE; + memset(&(p->stats), 0, sizeof(struct bgp_stats)); + /* Lock all channels when in GR recovery mode */ if (p->p.gr_recovery && p->cf->gr_mode) { @@ -2104,6 +2106,15 @@ bgp_show_proto_info(struct proto *P) tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time); } + struct bgp_stats *s = &p->stats; + cli_msg(-1006, " Statistics:"); + cli_msg(-1006, " rx-open %u rx-update %u rx-notify %u rx-keepalive %u rx-refresh %u", + s->rx_pkts[0], s->rx_pkts[1], s->rx_pkts[2], s->rx_pkts[3], s->rx_pkts[4]); + cli_msg(-1006, " tx-open %u tx-update %u tx-notify %u tx-keepalive %u tx-refresh %u", + s->tx_pkts[0], s->tx_pkts[1], s->tx_pkts[2], s->tx_pkts[3], s->tx_pkts[4]); + cli_msg(-1006, " bad-alist %u bad-attribute %u bad-next-hop %u no-mandatory %u loopy %u", + s->bad_alist, s->bad_attribute, s->bad_next_hop, s->no_mandatory, s->loopy); + if ((p->last_error_class != BE_NONE) && (p->last_error_class != BE_MAN_DOWN)) { @@ -2120,6 +2131,9 @@ bgp_show_proto_info(struct proto *P) if (c->c.channel_state == CS_UP) { + cli_msg(-1006, " Enqueued updates: %u", c->bucket_hash.count); + cli_msg(-1006, " Enqueued prefixes: %u", c->prefix_hash.count); + if (ipa_zero(c->link_addr)) cli_msg(-1006, " BGP Next hop: %I", c->next_hop_addr); else diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 30424abb..3a1efc8c 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -55,6 +55,8 @@ struct eattr; #define BGP_AF_FLOW4 BGP_AF( BGP_AFI_IPV4, BGP_SAFI_FLOW ) #define BGP_AF_FLOW6 BGP_AF( BGP_AFI_IPV6, BGP_SAFI_FLOW ) +#define BGP_PKT_TYPES 5 /* PKT_OPEN .. PKT_ROUTE_REFRESH */ + struct bgp_write_state; struct bgp_parse_state; @@ -196,6 +198,14 @@ struct bgp_caps { #define WALK_AF_CAPS(caps,ac) \ for (ac = caps->af_data; ac < &caps->af_data[caps->af_count]; ac++) +struct bgp_stats { + uint rx_pkts[BGP_PKT_TYPES], tx_pkts[BGP_PKT_TYPES]; + uint bad_alist, bad_attribute, bad_next_hop, no_mandatory, loopy; +}; + +#ifndef PARSER +#define STATS(KEY) ({ p->stats.KEY++; }) +#endif struct bgp_socket { node n; /* Node in global bgp_sockets */ @@ -254,6 +264,7 @@ struct bgp_proto { struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */ struct bgp_socket *sock; /* Shared listening socket */ struct bfd_request *bfd_req; /* BFD request, if BFD is used */ + struct bgp_stats stats; /* Packet statistics */ ip_addr source_addr; /* Local address used as an advertised next hop */ ip_addr link_addr; /* Link-local version of source_addr */ event *event; /* Event for respawning and shutting process */ diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index aa08732d..1376ba67 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -2174,6 +2174,7 @@ bgp_rx_end_mark(struct bgp_parse_state *s, u32 afi) static inline void bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_list *ea, byte *nh, uint nh_len) { + struct bgp_proto *p = s->proto; struct bgp_channel *c = bgp_get_channel(s->proto, afi); rta *a = NULL; @@ -2207,7 +2208,10 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis /* Handle withdraw during next hop decoding */ if (s->err_withdraw) + { + STATS(bad_next_hop); a = NULL; + } } c->desc->decode_nlri(s, nlri, len, a); @@ -2446,6 +2450,7 @@ found: static inline int bgp_send(struct bgp_conn *conn, uint type, uint len) { + struct bgp_proto *p = conn->bgp; sock *sk = conn->sk; byte *buf = sk->tbuf; @@ -2453,6 +2458,7 @@ bgp_send(struct bgp_conn *conn, uint type, uint len) put_u16(buf+16, len); buf[18] = type; + STATS(tx_pkts[type - 1]); return sk_send(sk, len); } @@ -2812,13 +2818,22 @@ bgp_rx_keepalive(struct bgp_conn *conn) static void bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len) { + struct bgp_proto *p = conn->bgp; byte type = pkt[18]; DBG("BGP: Got packet %02x (%d bytes)\n", type, len); - if (conn->bgp->p.mrtdump & MD_MESSAGES) + if (p->p.mrtdump & MD_MESSAGES) mrt_dump_bgp_packet(conn, pkt, len); + if (type < PKT_OPEN || type > PKT_ROUTE_REFRESH) + { + bgp_error(conn, 1, 3, pkt+18, 1); + return; + } + + STATS(rx_pkts[type - 1]); + switch (type) { case PKT_OPEN: return bgp_rx_open(conn, pkt, len); @@ -2826,7 +2841,6 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len) case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len); case PKT_KEEPALIVE: return bgp_rx_keepalive(conn); case PKT_ROUTE_REFRESH: return bgp_rx_route_refresh(conn, pkt, len); - default: bgp_error(conn, 1, 3, pkt+18, 1); } } From 1a3aba56779df88b8dcf7b564a4008c5296a74b2 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sat, 1 Dec 2018 18:04:11 +0100 Subject: [PATCH 4/4] BGP: Add I/C/E flag for BGP routes to show route output The flag indicates whether the route is internal (IBGP), intra-confederation, or external (EBGP). --- proto/bgp/attrs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index e53b09d8..fc2f862e 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -2014,11 +2014,13 @@ bgp_get_attr(eattr *a, byte *buf, int buflen) void bgp_get_route_info(rte *e, byte *buf, ea_list *attrs) { + struct bgp_proto *bgp = (void *) e->attrs->src->proto; eattr *p = ea_find(attrs, EA_CODE(EAP_BGP, BA_AS_PATH)); eattr *o = ea_find(attrs, EA_CODE(EAP_BGP, BA_ORIGIN)); u32 origas; - buf += bsprintf(buf, " (%d", e->pref); + char c = bgp->is_internal ? 'I' : (bgp->is_interior ? 'C' : 'E'); + buf += bsprintf(buf, " %c (%d", c, e->pref); if (e->u.bgp.suppressed) buf += bsprintf(buf, "-");