/* $OpenBSD: output.c,v 1.9 2020/05/10 13:38:46 deraadt Exp $ */ /* * Copyright (c) 2003 Henning Brauer * Copyright (c) 2004-2019 Claudio Jeker * Copyright (c) 2016 Job Snijders * Copyright (c) 2016 Peter Hessler * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "bgpd.h" #include "session.h" #include "rde.h" #include "bgpctl.h" #include "parser.h" const size_t pt_sizes[AID_MAX] = AID_PTSIZE; static void show_head(struct parse_result *res) { switch (res->action) { case SHOW: case SHOW_SUMMARY: printf("%-20s %8s %10s %10s %5s %-8s %s\n", "Neighbor", "AS", "MsgRcvd", "MsgSent", "OutQ", "Up/Down", "State/PrfRcvd"); break; case SHOW_FIB: printf("flags: * = valid, B = BGP, C = Connected, " "S = Static, D = Dynamic\n"); printf(" " "N = BGP Nexthop reachable via this route\n"); printf(" r = reject route, b = blackhole route\n\n"); printf("flags prio destination gateway\n"); break; case SHOW_FIB_TABLES: printf("%-5s %-20s %-8s\n", "Table", "Description", "State"); break; case SHOW_NEXTHOP: printf("Flags: * = nexthop valid\n"); printf("\n %-15s %-19s%-4s %-15s %-20s\n", "Nexthop", "Route", "Prio", "Gateway", "Iface"); break; case SHOW_INTERFACE: printf("%-15s%-9s%-9s%-7s%s\n", "Interface", "rdomain", "Nexthop", "Flags", "Link state"); break; case SHOW_RIB: if (res->flags & F_CTL_DETAIL) break; printf("flags: " "* = Valid, > = Selected, I = via IBGP, A = Announced,\n" " S = Stale, E = Error\n"); printf("origin validation state: " "N = not-found, V = valid, ! = invalid\n"); printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n"); printf("%-5s %3s %-20s %-15s %5s %5s %s\n", "flags", "ovs", "destination", "gateway", "lpref", "med", "aspath origin"); break; case NETWORK_SHOW: printf("flags: S = Static\n"); printf("flags prio destination gateway\n"); break; default: break; } } static void show_summary(struct peer *p) { char *s; const char *a; size_t alen; s = fmt_peer(p->conf.descr, &p->conf.remote_addr, p->conf.remote_masklen); a = log_as(p->conf.remote_as); alen = strlen(a); /* max displayed length of the peers name is 28 */ if (alen < 28) { if (strlen(s) > 28 - alen) s[28 - alen] = '\0'; } else alen = 0; printf("%-*s %s %10llu %10llu %5u %-8s ", (28 - (int)alen), s, a, p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + p->stats.msg_rcvd_rrefresh, p->stats.msg_sent_open + p->stats.msg_sent_notification + p->stats.msg_sent_update + p->stats.msg_sent_keepalive + p->stats.msg_sent_rrefresh, p->wbuf.queued, fmt_monotime(p->stats.last_updown)); if (p->state == STATE_ESTABLISHED) { printf("%6u", p->stats.prefix_cnt); if (p->conf.max_prefix != 0) printf("/%u", p->conf.max_prefix); } else if (p->conf.template) printf("Template"); else printf("%s", statenames[p->state]); printf("\n"); free(s); } static void show_neighbor_capa_mp(struct peer *p) { int comma; u_int8_t i; printf(" Multiprotocol extensions: "); for (i = 0, comma = 0; i < AID_MAX; i++) if (p->capa.peer.mp[i]) { printf("%s%s", comma ? ", " : "", aid2str(i)); comma = 1; } printf("\n"); } static void show_neighbor_capa_restart(struct peer *p) { int comma; u_int8_t i; printf(" Graceful Restart"); if (p->capa.peer.grestart.timeout) printf(": Timeout: %d, ", p->capa.peer.grestart.timeout); for (i = 0, comma = 0; i < AID_MAX; i++) if (p->capa.peer.grestart.flags[i] & CAPA_GR_PRESENT) { if (!comma && p->capa.peer.grestart.flags[i] & CAPA_GR_RESTART) printf("restarted, "); if (comma) printf(", "); printf("%s", aid2str(i)); if (p->capa.peer.grestart.flags[i] & CAPA_GR_FORWARD) printf(" (preserved)"); comma = 1; } printf("\n"); } static void show_neighbor_msgstats(struct peer *p) { printf(" Message statistics:\n"); printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); printf(" %-15s %10llu %10llu\n", "Opens", p->stats.msg_sent_open, p->stats.msg_rcvd_open); printf(" %-15s %10llu %10llu\n", "Notifications", p->stats.msg_sent_notification, p->stats.msg_rcvd_notification); printf(" %-15s %10llu %10llu\n", "Updates", p->stats.msg_sent_update, p->stats.msg_rcvd_update); printf(" %-15s %10llu %10llu\n", "Keepalives", p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive); printf(" %-15s %10llu %10llu\n", "Route Refresh", p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh); printf(" %-15s %10llu %10llu\n\n", "Total", p->stats.msg_sent_open + p->stats.msg_sent_notification + p->stats.msg_sent_update + p->stats.msg_sent_keepalive + p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + p->stats.msg_rcvd_rrefresh); printf(" Update statistics:\n"); printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); printf(" %-15s %10u %10u\n", "Prefixes", p->stats.prefix_out_cnt, p->stats.prefix_cnt); printf(" %-15s %10llu %10llu\n", "Updates", p->stats.prefix_sent_update, p->stats.prefix_rcvd_update); printf(" %-15s %10llu %10llu\n", "Withdraws", p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw); printf(" %-15s %10llu %10llu\n", "End-of-Rib", p->stats.prefix_sent_eor, p->stats.prefix_rcvd_eor); } static void show_neighbor_full(struct peer *p, struct parse_result *res) { const char *errstr; struct in_addr ina; char *s; int hascapamp = 0; u_int8_t i; if ((p->conf.remote_addr.aid == AID_INET && p->conf.remote_masklen != 32) || (p->conf.remote_addr.aid == AID_INET6 && p->conf.remote_masklen != 128)) { if (asprintf(&s, "%s/%u", log_addr(&p->conf.remote_addr), p->conf.remote_masklen) == -1) err(1, NULL); } else if ((s = strdup(log_addr(&p->conf.remote_addr))) == NULL) err(1, "strdup"); printf("BGP neighbor is %s, ", s); free(s); if (p->conf.remote_as == 0 && p->conf.template) printf("remote AS: accept any"); else printf("remote AS %s", log_as(p->conf.remote_as)); if (p->conf.template) printf(", Template"); if (p->template) printf(", Cloned"); if (p->conf.passive) printf(", Passive"); if (p->conf.ebgp && p->conf.distance > 1) printf(", Multihop (%u)", (int)p->conf.distance); printf("\n"); if (p->conf.descr[0]) printf(" Description: %s\n", p->conf.descr); if (p->conf.max_prefix) { printf(" Max-prefix: %u", p->conf.max_prefix); if (p->conf.max_prefix_restart) printf(" (restart %u)", p->conf.max_prefix_restart); } if (p->conf.max_out_prefix) { printf(" Max-prefix out: %u", p->conf.max_out_prefix); if (p->conf.max_out_prefix_restart) printf(" (restart %u)", p->conf.max_out_prefix_restart); } if (p->conf.max_prefix || p->conf.max_out_prefix) printf("\n"); if (p->state == STATE_ESTABLISHED) { ina.s_addr = p->remote_bgpid; printf(" BGP version 4, remote router-id %s", inet_ntoa(ina)); printf("%s\n", fmt_auth_method(p->auth.method)); } printf(" BGP state = %s", statenames[p->state]); if (p->conf.down) { printf(", marked down"); } if (*(p->conf.reason)) { printf(" with shutdown reason \"%s\"", log_reason(p->conf.reason)); } if (p->stats.last_updown != 0) printf(", %s for %s", p->state == STATE_ESTABLISHED ? "up" : "down", fmt_monotime(p->stats.last_updown)); printf("\n"); printf(" Last read %s, holdtime %us, keepalive interval %us\n", fmt_monotime(p->stats.last_read), p->holdtime, p->holdtime/3); printf(" Last write %s\n", fmt_monotime(p->stats.last_write)); for (i = 0; i < AID_MAX; i++) if (p->capa.peer.mp[i]) hascapamp = 1; if (hascapamp || p->capa.peer.refresh || p->capa.peer.grestart.restart || p->capa.peer.as4byte) { printf(" Neighbor capabilities:\n"); if (hascapamp) show_neighbor_capa_mp(p); if (p->capa.peer.refresh) printf(" Route Refresh\n"); if (p->capa.peer.grestart.restart) show_neighbor_capa_restart(p); if (p->capa.peer.as4byte) printf(" 4-byte AS numbers\n"); } printf("\n"); if (res->action == SHOW_NEIGHBOR_TIMERS) return; show_neighbor_msgstats(p); printf("\n"); if (*(p->stats.last_reason)) { printf(" Last received shutdown reason: \"%s\"\n", log_reason(p->stats.last_reason)); } errstr = fmt_errstr(p->stats.last_sent_errcode, p->stats.last_sent_suberr); if (errstr) printf(" Last error sent: %s\n", errstr); errstr = fmt_errstr(p->stats.last_rcvd_errcode, p->stats.last_rcvd_suberr); if (errstr) printf(" Last error received: %s\n", errstr); if (p->state >= STATE_OPENSENT) { printf(" Local host: %20s, Local port: %5u\n", log_addr(&p->local), p->local_port); printf(" Remote host: %20s, Remote port: %5u\n", log_addr(&p->remote), p->remote_port); printf("\n"); } } static void show_neighbor(struct peer *p, struct parse_result *res) { char *s; switch (res->action) { case SHOW: case SHOW_SUMMARY: show_summary(p); break; case SHOW_SUMMARY_TERSE: s = fmt_peer(p->conf.descr, &p->conf.remote_addr, p->conf.remote_masklen); printf("%s %s %s\n", s, log_as(p->conf.remote_as), p->conf.template ? "Template" : statenames[p->state]); free(s); break; case SHOW_NEIGHBOR: case SHOW_NEIGHBOR_TIMERS: show_neighbor_full(p, res); break; case SHOW_NEIGHBOR_TERSE: s = fmt_peer(NULL, &p->conf.remote_addr, p->conf.remote_masklen); printf("%llu %llu %llu %llu %llu %llu %llu %llu %llu " "%llu %u %u %llu %llu %llu %llu %s %s \"%s\"\n", p->stats.msg_sent_open, p->stats.msg_rcvd_open, p->stats.msg_sent_notification, p->stats.msg_rcvd_notification, p->stats.msg_sent_update, p->stats.msg_rcvd_update, p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive, p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh, p->stats.prefix_cnt, p->conf.max_prefix, p->stats.prefix_sent_update, p->stats.prefix_rcvd_update, p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw, s, log_as(p->conf.remote_as), p->conf.descr); free(s); break; default: break; } } static void show_timer(struct ctl_timer *t) { printf(" %-20s ", timernames[t->type]); if (t->val <= 0) printf("%-20s\n", "due"); else printf("due in %-13s\n", fmt_timeframe(t->val)); } static void show_fib(struct kroute_full *kf) { char *p; if (asprintf(&p, "%s/%u", log_addr(&kf->prefix), kf->prefixlen) == -1) err(1, NULL); printf("%s%4i %-20s ", fmt_fib_flags(kf->flags), kf->priority, p); free(p); if (kf->flags & F_CONNECTED) printf("link#%u", kf->ifindex); else printf("%s", log_addr(&kf->nexthop)); printf("\n"); } static void show_fib_table(struct ktable *kt) { printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr, kt->fib_sync ? "coupled" : "decoupled", kt->fib_sync != kt->fib_conf ? "*" : ""); } static void show_nexthop(struct ctl_show_nexthop *nh) { struct kroute *k; struct kroute6 *k6; char *s; printf("%s %-15s ", nh->valid ? "*" : " ", log_addr(&nh->addr)); if (!nh->krvalid) { printf("\n"); return; } switch (nh->addr.aid) { case AID_INET: k = &nh->kr.kr4; if (asprintf(&s, "%s/%u", inet_ntoa(k->prefix), k->prefixlen) == -1) err(1, NULL); printf("%-20s", s); free(s); printf("%3i %-15s ", k->priority, k->flags & F_CONNECTED ? "connected" : inet_ntoa(k->nexthop)); break; case AID_INET6: k6 = &nh->kr.kr6; if (asprintf(&s, "%s/%u", log_in6addr(&k6->prefix), k6->prefixlen) == -1) err(1, NULL); printf("%-20s", s); free(s); printf("%3i %-15s ", k6->priority, k6->flags & F_CONNECTED ? "connected" : log_in6addr(&k6->nexthop)); break; default: printf("unknown address family\n"); return; } if (nh->iface.ifname[0]) { printf("%s (%s, %s)", nh->iface.ifname, nh->iface.is_up ? "UP" : "DOWN", nh->iface.baudrate ? get_baudrate(nh->iface.baudrate, "bps") : nh->iface.linkstate); } printf("\n"); } static void show_interface(struct ctl_show_interface *iface) { printf("%-15s", iface->ifname); printf("%-9u", iface->rdomain); printf("%-9s", iface->nh_reachable ? "ok" : "invalid"); printf("%-7s", iface->is_up ? "UP" : ""); if (iface->media[0]) printf("%s, ", iface->media); printf("%s", iface->linkstate); if (iface->baudrate > 0) printf(", %s", get_baudrate(iface->baudrate, "Bit/s")); printf("\n"); } static void show_communities(u_char *data, size_t len, struct parse_result *res) { struct community c; size_t i; u_int64_t ext; u_int8_t type = 0; if (len % sizeof(c)) return; for (i = 0; i < len; i += sizeof(c)) { memcpy(&c, data + i, sizeof(c)); if (type != c.flags) { if (type != 0) printf("%c", EOL0(res->flags)); printf(" %s:", fmt_attr(c.flags, ATTR_OPTIONAL | ATTR_TRANSITIVE)); type = c.flags; } switch (c.flags) { case COMMUNITY_TYPE_BASIC: printf(" %s", fmt_community(c.data1, c.data2)); break; case COMMUNITY_TYPE_LARGE: printf(" %s", fmt_large_community(c.data1, c.data2, c.data3)); break; case COMMUNITY_TYPE_EXT: ext = (u_int64_t)c.data3 << 48; switch (c.data3 >> 8) { case EXT_COMMUNITY_TRANS_TWO_AS: case EXT_COMMUNITY_TRANS_OPAQUE: case EXT_COMMUNITY_TRANS_EVPN: case EXT_COMMUNITY_NON_TRANS_OPAQUE: ext |= ((u_int64_t)c.data1 & 0xffff) << 32; ext |= (u_int64_t)c.data2; break; case EXT_COMMUNITY_TRANS_FOUR_AS: case EXT_COMMUNITY_TRANS_IPV4: ext |= (u_int64_t)c.data1 << 16; ext |= (u_int64_t)c.data2 & 0xffff; break; } ext = htobe64(ext); printf(" %s", fmt_ext_community((void *)&ext)); break; } } printf("%c", EOL0(res->flags)); } static void show_community(u_char *data, u_int16_t len) { u_int16_t a, v; u_int16_t i; if (len & 0x3) { printf("bad length"); return; } for (i = 0; i < len; i += 4) { memcpy(&a, data + i, sizeof(a)); memcpy(&v, data + i + 2, sizeof(v)); a = ntohs(a); v = ntohs(v); printf("%s", fmt_community(a, v)); if (i + 4 < len) printf(" "); } } static void show_large_community(u_char *data, u_int16_t len) { u_int32_t a, l1, l2; u_int16_t i; if (len % 12) { printf("bad length"); return; } for (i = 0; i < len; i += 12) { memcpy(&a, data + i, sizeof(a)); memcpy(&l1, data + i + 4, sizeof(l1)); memcpy(&l2, data + i + 8, sizeof(l2)); a = ntohl(a); l1 = ntohl(l1); l2 = ntohl(l2); printf("%s", fmt_large_community(a, l1, l2)); if (i + 12 < len) printf(" "); } } static void show_ext_community(u_char *data, u_int16_t len) { u_int16_t i; if (len & 0x7) { printf("bad length"); return; } for (i = 0; i < len; i += 8) { printf("%s", fmt_ext_community(data + i)); if (i + 8 < len) printf(" "); } } static void show_attr(u_char *data, size_t len, struct parse_result *res) { u_char *path; struct in_addr id; struct bgpd_addr prefix; char *aspath; u_int32_t as; u_int16_t alen, ioff, short_as, afi; u_int8_t flags, type, safi, aid, prefixlen; int i, pos, e2, e4; if (len < 3) { warnx("Too short BGP attrbute"); return; } flags = data[0]; type = data[1]; /* get the attribute length */ if (flags & ATTR_EXTLEN) { if (len < 4) { warnx("Too short BGP attrbute"); return; } memcpy(&alen, data+2, sizeof(u_int16_t)); alen = ntohs(alen); data += 4; len -= 4; } else { alen = data[2]; data += 3; len -= 3; } /* bad imsg len how can that happen!? */ if (alen > len) { warnx("Bad BGP attrbute length"); return; } printf(" %s: ", fmt_attr(type, flags)); switch (type) { case ATTR_ORIGIN: if (alen == 1) printf("%s", fmt_origin(*data, 0)); else printf("bad length"); break; case ATTR_ASPATH: case ATTR_AS4_PATH: /* prefer 4-byte AS here */ e4 = aspath_verify(data, alen, 1); e2 = aspath_verify(data, alen, 0); if (e4 == 0 || e4 == AS_ERR_SOFT) { path = data; } else if (e2 == 0 || e2 == AS_ERR_SOFT) { path = aspath_inflate(data, alen, &alen); if (path == NULL) errx(1, "aspath_inflate failed"); } else { printf("bad AS-Path"); break; } if (aspath_asprint(&aspath, path, alen) == -1) err(1, NULL); printf("%s", aspath); free(aspath); if (path != data) free(path); break; case ATTR_NEXTHOP: if (alen == 4) { memcpy(&id, data, sizeof(id)); printf("%s", inet_ntoa(id)); } else printf("bad length"); break; case ATTR_MED: case ATTR_LOCALPREF: if (alen == 4) { u_int32_t val; memcpy(&val, data, sizeof(val)); val = ntohl(val); printf("%u", val); } else printf("bad length"); break; case ATTR_AGGREGATOR: case ATTR_AS4_AGGREGATOR: if (alen == 8) { memcpy(&as, data, sizeof(as)); memcpy(&id, data + sizeof(as), sizeof(id)); as = ntohl(as); } else if (alen == 6) { memcpy(&short_as, data, sizeof(short_as)); memcpy(&id, data + sizeof(short_as), sizeof(id)); as = ntohs(short_as); } else { printf("bad length"); break; } printf("%s [%s]", log_as(as), inet_ntoa(id)); break; case ATTR_COMMUNITIES: show_community(data, alen); break; case ATTR_ORIGINATOR_ID: if (alen == 4) { memcpy(&id, data, sizeof(id)); printf("%s", inet_ntoa(id)); } else printf("bad length"); break; case ATTR_CLUSTER_LIST: for (ioff = 0; ioff + sizeof(id) <= alen; ioff += sizeof(id)) { memcpy(&id, data + ioff, sizeof(id)); printf(" %s", inet_ntoa(id)); } break; case ATTR_MP_REACH_NLRI: case ATTR_MP_UNREACH_NLRI: if (alen < 3) { bad_len: printf("bad length"); break; } memcpy(&afi, data, 2); data += 2; alen -= 2; afi = ntohs(afi); safi = *data++; alen--; if (afi2aid(afi, safi, &aid) == -1) { printf("bad AFI/SAFI pair"); break; } printf(" %s", aid2str(aid)); if (type == ATTR_MP_REACH_NLRI) { struct bgpd_addr nexthop; u_int8_t nhlen; if (len == 0) goto bad_len; nhlen = *data++; alen--; if (nhlen > len) goto bad_len; memset(&nexthop, 0, sizeof(nexthop)); switch (aid) { case AID_INET6: nexthop.aid = aid; if (nhlen != 16 && nhlen != 32) goto bad_len; memcpy(&nexthop.v6.s6_addr, data, 16); break; case AID_VPN_IPv4: if (nhlen != 12) goto bad_len; nexthop.aid = AID_INET; memcpy(&nexthop.v4, data + sizeof(u_int64_t), sizeof(nexthop.v4)); break; case AID_VPN_IPv6: if (nhlen != 24) goto bad_len; nexthop.aid = AID_INET6; memcpy(&nexthop.v6, data + sizeof(u_int64_t), sizeof(nexthop.v6)); break; default: printf("unhandled AID #%u", aid); goto done; } /* ignore reserved (old SNPA) field as per RFC4760 */ data += nhlen + 1; alen -= nhlen + 1; printf(" nexthop: %s", log_addr(&nexthop)); } while (alen > 0) { switch (aid) { case AID_INET6: pos = nlri_get_prefix6(data, alen, &prefix, &prefixlen); break; case AID_VPN_IPv4: pos = nlri_get_vpn4(data, alen, &prefix, &prefixlen, 1); break; case AID_VPN_IPv6: pos = nlri_get_vpn6(data, alen, &prefix, &prefixlen, 1); break; default: printf("unhandled AID #%u", aid); goto done; } if (pos == -1) { printf("bad %s prefix", aid2str(aid)); break; } printf(" %s/%u", log_addr(&prefix), prefixlen); data += pos; alen -= pos; } break; case ATTR_EXT_COMMUNITIES: show_ext_community(data, alen); break; case ATTR_LARGE_COMMUNITIES: show_large_community(data, alen); break; case ATTR_ATOMIC_AGGREGATE: default: printf(" len %u", alen); if (alen) { printf(":"); for (i=0; i < alen; i++) printf(" %02x", *(data+i)); } break; } done: printf("%c", EOL0(res->flags)); } static void show_rib_brief(struct ctl_show_rib *r, u_char *asdata, size_t aslen) { char *p, *aspath; if (asprintf(&p, "%s/%u", log_addr(&r->prefix), r->prefixlen) == -1) err(1, NULL); printf("%s %3s %-20s %-15s %5u %5u ", fmt_flags(r->flags, 1), fmt_ovs(r->validation_state, 1), p, log_addr(&r->exit_nexthop), r->local_pref, r->med); free(p); if (aspath_asprint(&aspath, asdata, aslen) == -1) err(1, NULL); if (strlen(aspath) > 0) printf("%s ", aspath); free(aspath); printf("%s\n", fmt_origin(r->origin, 1)); } static void show_rib_detail(struct ctl_show_rib *r, u_char *asdata, size_t aslen, int flag0) { struct in_addr id; char *aspath, *s; printf("\nBGP routing table entry for %s/%u%c", log_addr(&r->prefix), r->prefixlen, EOL0(flag0)); if (aspath_asprint(&aspath, asdata, aslen) == -1) err(1, NULL); if (strlen(aspath) > 0) printf(" %s%c", aspath, EOL0(flag0)); free(aspath); s = fmt_peer(r->descr, &r->remote_addr, -1); printf(" Nexthop %s ", log_addr(&r->exit_nexthop)); printf("(via %s) Neighbor %s (", log_addr(&r->true_nexthop), s); free(s); id.s_addr = htonl(r->remote_id); printf("%s)%c", inet_ntoa(id), EOL0(flag0)); printf(" Origin %s, metric %u, localpref %u, weight %u, ovs %s, ", fmt_origin(r->origin, 0), r->med, r->local_pref, r->weight, fmt_ovs(r->validation_state, 0)); printf("%s", fmt_flags(r->flags, 0)); printf("%c Last update: %s ago%c", EOL0(flag0), fmt_timeframe(r->age), EOL0(flag0)); } static void show_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen, struct parse_result *res) { if (res->flags & F_CTL_DETAIL) show_rib_detail(r, asdata, aslen, res->flags); else show_rib_brief(r, asdata, aslen); } static void show_rib_mem(struct rde_memstats *stats) { size_t pts = 0; int i; printf("RDE memory statistics\n"); for (i = 0; i < AID_MAX; i++) { if (stats->pt_cnt[i] == 0) continue; pts += stats->pt_cnt[i] * pt_sizes[i]; printf("%10lld %s network entries using %s of memory\n", stats->pt_cnt[i], aid_vals[i].name, fmt_mem(stats->pt_cnt[i] * pt_sizes[i])); } printf("%10lld rib entries using %s of memory\n", stats->rib_cnt, fmt_mem(stats->rib_cnt * sizeof(struct rib_entry))); printf("%10lld prefix entries using %s of memory\n", stats->prefix_cnt, fmt_mem(stats->prefix_cnt * sizeof(struct prefix))); printf("%10lld BGP path attribute entries using %s of memory\n", stats->path_cnt, fmt_mem(stats->path_cnt * sizeof(struct rde_aspath))); printf("\t and holding %lld references\n", stats->path_refs); printf("%10lld BGP AS-PATH attribute entries using " "%s of memory\n\t and holding %lld references\n", stats->aspath_cnt, fmt_mem(stats->aspath_size), stats->aspath_refs); printf("%10lld entries for %lld BGP communities " "using %s of memory\n", stats->comm_cnt, stats->comm_nmemb, fmt_mem(stats->comm_cnt * sizeof(struct rde_community) + stats->comm_size * sizeof(struct community))); printf("\t and holding %lld references\n", stats->comm_refs); printf("%10lld BGP attributes entries using %s of memory\n", stats->attr_cnt, fmt_mem(stats->attr_cnt * sizeof(struct attr))); printf("\t and holding %lld references\n", stats->attr_refs); printf("%10lld BGP attributes using %s of memory\n", stats->attr_dcnt, fmt_mem(stats->attr_data)); printf("%10lld as-set elements in %lld tables using " "%s of memory\n", stats->aset_nmemb, stats->aset_cnt, fmt_mem(stats->aset_size)); printf("%10lld prefix-set elements using %s of memory\n", stats->pset_cnt, fmt_mem(stats->pset_size)); printf("RIB using %s of memory\n", fmt_mem(pts + stats->prefix_cnt * sizeof(struct prefix) + stats->rib_cnt * sizeof(struct rib_entry) + stats->path_cnt * sizeof(struct rde_aspath) + stats->aspath_size + stats->attr_cnt * sizeof(struct attr) + stats->attr_data)); printf("Sets using %s of memory\n", fmt_mem(stats->aset_size + stats->pset_size)); printf("\nRDE hash statistics\n"); } static void show_rib_hash(struct rde_hashstats *hash) { double avg, dev; printf("\t%s: size %lld, %lld entries\n", hash->name, hash->num, hash->sum); avg = (double)hash->sum / (double)hash->num; dev = sqrt(fmax(0, hash->sumq / hash->num - avg * avg)); printf("\t min %lld max %lld avg/std-dev = %.3f/%.3f\n", hash->min, hash->max, avg, dev); } static void show_result(u_int rescode) { if (rescode == 0) printf("request processed\n"); else if (rescode > sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) printf("unknown result error code %u\n", rescode); else printf("%s\n", ctl_res_strerror[rescode]); } static void show_tail(void) { /* nothing */ } const struct output show_output = { .head = show_head, .neighbor = show_neighbor, .timer = show_timer, .fib = show_fib, .fib_table = show_fib_table, .nexthop = show_nexthop, .interface = show_interface, .communities = show_communities, .attr = show_attr, .rib = show_rib, .rib_mem = show_rib_mem, .rib_hash = show_rib_hash, .result = show_result, .tail = show_tail };