1 /* $OpenBSD: output.c,v 1.50 2024/01/31 11:23:20 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2004-2019 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2016 Job Snijders <job@instituut.net> 7 * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <endian.h> 23 #include <err.h> 24 #include <math.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 #include "bgpd.h" 30 #include "session.h" 31 #include "rde.h" 32 33 #include "bgpctl.h" 34 #include "parser.h" 35 36 static void 37 show_head(struct parse_result *res) 38 { 39 switch (res->action) { 40 case SHOW: 41 case SHOW_SUMMARY: 42 printf("%-20s %8s %10s %10s %5s %-8s %s\n", "Neighbor", "AS", 43 "MsgRcvd", "MsgSent", "OutQ", "Up/Down", "State/PrfRcvd"); 44 break; 45 case SHOW_FIB: 46 printf("flags: B = BGP, C = Connected, S = Static\n"); 47 printf(" N = BGP Nexthop reachable via this route\n"); 48 printf(" r = reject route, b = blackhole route\n\n"); 49 printf("%-5s %-4s %-32s %-32s\n", "flags", "prio", 50 "destination", "gateway"); 51 break; 52 case SHOW_FIB_TABLES: 53 printf("%-5s %-20s %-8s\n", "Table", "Description", "State"); 54 break; 55 case SHOW_NEXTHOP: 56 printf("Flags: * = nexthop valid\n"); 57 printf("\n %-15s %-19s%-4s %-15s %-20s\n", "Nexthop", "Route", 58 "Prio", "Gateway", "Iface"); 59 break; 60 case SHOW_INTERFACE: 61 printf("%-15s%-9s%-9s%-7s%s\n", "Interface", "rdomain", 62 "Nexthop", "Flags", "Link state"); 63 break; 64 case SHOW_RIB: 65 if (res->flags & F_CTL_DETAIL) 66 break; 67 printf("flags: " 68 "* = Valid, > = Selected, I = via IBGP, A = Announced,\n" 69 " S = Stale, E = Error\n"); 70 printf("origin validation state: " 71 "N = not-found, V = valid, ! = invalid\n"); 72 printf("aspa validation state: " 73 "? = unknown, V = valid, ! = invalid\n"); 74 printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n"); 75 printf("%-5s %3s %-20s %-15s %5s %5s %s\n", 76 "flags", "vs", "destination", "gateway", "lpref", "med", 77 "aspath origin"); 78 break; 79 case SHOW_SET: 80 printf("%-6s %-34s %7s %7s %6s %11s\n", "Type", "Name", 81 "#IPv4", "#IPv6", "#ASnum", "Last Change"); 82 break; 83 case NETWORK_SHOW: 84 printf("flags: S = Static\n"); 85 printf("%-5s %-4s %-32s %-32s\n", "flags", "prio", 86 "destination", "gateway"); 87 break; 88 case FLOWSPEC_SHOW: 89 printf("flags: S = Static\n"); 90 default: 91 break; 92 } 93 } 94 95 static void 96 show_summary(struct peer *p) 97 { 98 char *s; 99 const char *a; 100 size_t alen; 101 102 s = fmt_peer(p->conf.descr, &p->conf.remote_addr, 103 p->conf.remote_masklen); 104 105 a = log_as(p->conf.remote_as); 106 alen = strlen(a); 107 /* max displayed length of the peers name is 28 */ 108 if (alen < 28) { 109 if (strlen(s) > 28 - alen) 110 s[28 - alen] = '\0'; 111 } else 112 alen = 0; 113 114 printf("%-*s %s %10llu %10llu %5u %-8s ", 115 (28 - (int)alen), s, a, 116 p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + 117 p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + 118 p->stats.msg_rcvd_rrefresh, 119 p->stats.msg_sent_open + p->stats.msg_sent_notification + 120 p->stats.msg_sent_update + p->stats.msg_sent_keepalive + 121 p->stats.msg_sent_rrefresh, 122 p->wbuf.queued, 123 fmt_monotime(p->stats.last_updown)); 124 if (p->state == STATE_ESTABLISHED) { 125 printf("%6u", p->stats.prefix_cnt); 126 if (p->conf.max_prefix != 0) 127 printf("/%u", p->conf.max_prefix); 128 } else if (p->conf.template) 129 printf("Template"); 130 else 131 printf("%s", statenames[p->state]); 132 printf("\n"); 133 free(s); 134 } 135 136 static void 137 show_neighbor_capa_mp(struct capabilities *capa) 138 { 139 int comma; 140 uint8_t i; 141 142 printf(" Multiprotocol extensions: "); 143 for (i = AID_MIN, comma = 0; i < AID_MAX; i++) 144 if (capa->mp[i]) { 145 printf("%s%s", comma ? ", " : "", aid2str(i)); 146 comma = 1; 147 } 148 printf("\n"); 149 } 150 151 static void 152 show_neighbor_capa_add_path(struct capabilities *capa) 153 { 154 const char *mode; 155 int comma; 156 uint8_t i; 157 158 printf(" Add-path: "); 159 for (i = AID_MIN, comma = 0; i < AID_MAX; i++) { 160 switch (capa->add_path[i]) { 161 case 0: 162 default: 163 continue; 164 case CAPA_AP_RECV: 165 mode = "recv"; 166 break; 167 case CAPA_AP_SEND: 168 mode = "send"; 169 break; 170 case CAPA_AP_BIDIR: 171 mode = "bidir"; 172 } 173 printf("%s%s %s", comma ? ", " : "", aid2str(i), mode); 174 comma = 1; 175 } 176 printf("\n"); 177 } 178 179 static void 180 show_neighbor_capa_restart(struct capabilities *capa) 181 { 182 int comma; 183 uint8_t i; 184 185 printf(" Graceful Restart"); 186 if (capa->grestart.timeout) 187 printf(": Timeout: %d, ", capa->grestart.timeout); 188 for (i = AID_MIN, comma = 0; i < AID_MAX; i++) 189 if (capa->grestart.flags[i] & CAPA_GR_PRESENT) { 190 if (!comma && 191 capa->grestart.flags[i] & CAPA_GR_RESTART) 192 printf("restarted, "); 193 if (comma) 194 printf(", "); 195 printf("%s", aid2str(i)); 196 if (capa->grestart.flags[i] & CAPA_GR_FORWARD) 197 printf(" (preserved)"); 198 comma = 1; 199 } 200 printf("\n"); 201 } 202 203 static void 204 show_neighbor_msgstats(struct peer *p) 205 { 206 printf(" Message statistics:\n"); 207 printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); 208 printf(" %-15s %10llu %10llu\n", "Opens", 209 p->stats.msg_sent_open, p->stats.msg_rcvd_open); 210 printf(" %-15s %10llu %10llu\n", "Notifications", 211 p->stats.msg_sent_notification, p->stats.msg_rcvd_notification); 212 printf(" %-15s %10llu %10llu\n", "Updates", 213 p->stats.msg_sent_update, p->stats.msg_rcvd_update); 214 printf(" %-15s %10llu %10llu\n", "Keepalives", 215 p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive); 216 printf(" %-15s %10llu %10llu\n", "Route Refresh", 217 p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh); 218 printf(" %-15s %10llu %10llu\n\n", "Total", 219 p->stats.msg_sent_open + p->stats.msg_sent_notification + 220 p->stats.msg_sent_update + p->stats.msg_sent_keepalive + 221 p->stats.msg_sent_rrefresh, 222 p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + 223 p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + 224 p->stats.msg_rcvd_rrefresh); 225 printf(" Update statistics:\n"); 226 printf(" %-15s %-10s %-10s %-10s\n", "", "Sent", "Received", 227 "Pending"); 228 printf(" %-15s %10u %10u\n", "Prefixes", 229 p->stats.prefix_out_cnt, p->stats.prefix_cnt); 230 printf(" %-15s %10llu %10llu %10u\n", "Updates", 231 p->stats.prefix_sent_update, p->stats.prefix_rcvd_update, 232 p->stats.pending_update); 233 printf(" %-15s %10llu %10llu %10u\n", "Withdraws", 234 p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw, 235 p->stats.pending_withdraw); 236 printf(" %-15s %10llu %10llu\n", "End-of-Rib", 237 p->stats.prefix_sent_eor, p->stats.prefix_rcvd_eor); 238 printf(" Route Refresh statistics:\n"); 239 printf(" %-15s %10llu %10llu\n", "Request", 240 p->stats.refresh_sent_req, p->stats.refresh_rcvd_req); 241 printf(" %-15s %10llu %10llu\n", "Begin-of-RR", 242 p->stats.refresh_sent_borr, p->stats.refresh_rcvd_borr); 243 printf(" %-15s %10llu %10llu\n", "End-of-RR", 244 p->stats.refresh_sent_eorr, p->stats.refresh_rcvd_eorr); 245 } 246 247 static void 248 show_neighbor_full(struct peer *p, struct parse_result *res) 249 { 250 const char *errstr; 251 struct in_addr ina; 252 char *s; 253 int hascapamp, hascapaap; 254 uint8_t i; 255 256 if ((p->conf.remote_addr.aid == AID_INET && 257 p->conf.remote_masklen != 32) || 258 (p->conf.remote_addr.aid == AID_INET6 && 259 p->conf.remote_masklen != 128)) { 260 if (asprintf(&s, "%s/%u", 261 log_addr(&p->conf.remote_addr), 262 p->conf.remote_masklen) == -1) 263 err(1, NULL); 264 } else if ((s = strdup(log_addr(&p->conf.remote_addr))) == NULL) 265 err(1, "strdup"); 266 267 printf("BGP neighbor is %s, ", s); 268 free(s); 269 if (p->conf.remote_as == 0 && p->conf.template) 270 printf("remote AS: accept any"); 271 else 272 printf("remote AS %s", log_as(p->conf.remote_as)); 273 if (p->conf.template) 274 printf(", Template"); 275 if (p->template) 276 printf(", Cloned"); 277 if (p->conf.passive) 278 printf(", Passive"); 279 if (p->conf.ebgp && p->conf.distance > 1) 280 printf(", Multihop (%u)", (int)p->conf.distance); 281 printf("\n"); 282 if (p->conf.descr[0]) 283 printf(" Description: %s\n", p->conf.descr); 284 if (p->conf.ebgp && p->conf.role != ROLE_NONE) 285 printf(" Role: %s\n", log_policy(p->conf.role)); 286 if (p->conf.max_prefix) { 287 printf(" Max-prefix: %u", p->conf.max_prefix); 288 if (p->conf.max_prefix_restart) 289 printf(" (restart %u)", 290 p->conf.max_prefix_restart); 291 } 292 if (p->conf.max_out_prefix) { 293 printf(" Max-prefix out: %u", p->conf.max_out_prefix); 294 if (p->conf.max_out_prefix_restart) 295 printf(" (restart %u)", 296 p->conf.max_out_prefix_restart); 297 } 298 if (p->conf.max_prefix || p->conf.max_out_prefix) 299 printf("\n"); 300 301 if (p->state == STATE_ESTABLISHED) { 302 ina.s_addr = p->remote_bgpid; 303 printf(" BGP version 4, remote router-id %s", 304 inet_ntoa(ina)); 305 printf("%s\n", fmt_auth_method(p->auth.method)); 306 } 307 printf(" BGP state = %s", statenames[p->state]); 308 if (p->conf.down) { 309 printf(", marked down"); 310 } 311 if (p->conf.reason[0]) { 312 printf(" with shutdown reason \"%s\"", 313 log_reason(p->conf.reason)); 314 } 315 if (p->stats.last_updown != 0) 316 printf(", %s for %s", 317 p->state == STATE_ESTABLISHED ? "up" : "down", 318 fmt_monotime(p->stats.last_updown)); 319 printf("\n"); 320 printf(" Last read %s, holdtime %us, keepalive interval %us\n", 321 fmt_monotime(p->stats.last_read), 322 p->holdtime, p->holdtime/3); 323 printf(" Last write %s\n", fmt_monotime(p->stats.last_write)); 324 325 hascapamp = 0; 326 hascapaap = 0; 327 for (i = AID_MIN; i < AID_MAX; i++) { 328 if (p->capa.peer.mp[i]) 329 hascapamp = 1; 330 if (p->capa.peer.add_path[i]) 331 hascapaap = 1; 332 } 333 if (hascapamp || hascapaap || p->capa.peer.grestart.restart || 334 p->capa.peer.refresh || p->capa.peer.enhanced_rr || 335 p->capa.peer.as4byte || p->capa.peer.policy) { 336 printf(" Neighbor capabilities:\n"); 337 if (hascapamp) 338 show_neighbor_capa_mp(&p->capa.peer); 339 if (p->capa.peer.as4byte) 340 printf(" 4-byte AS numbers\n"); 341 if (p->capa.peer.refresh) 342 printf(" Route Refresh\n"); 343 if (p->capa.peer.enhanced_rr) 344 printf(" Enhanced Route Refresh\n"); 345 if (p->capa.peer.grestart.restart) 346 show_neighbor_capa_restart(&p->capa.peer); 347 if (hascapaap) 348 show_neighbor_capa_add_path(&p->capa.peer); 349 if (p->capa.peer.policy) 350 printf(" Open Policy role %s (local %s)\n", 351 log_policy(p->remote_role), 352 log_policy(p->conf.role)); 353 } 354 355 hascapamp = 0; 356 hascapaap = 0; 357 for (i = AID_MIN; i < AID_MAX; i++) { 358 if (p->capa.neg.mp[i]) 359 hascapamp = 1; 360 if (p->capa.neg.add_path[i]) 361 hascapaap = 1; 362 } 363 if (hascapamp || hascapaap || p->capa.neg.grestart.restart || 364 p->capa.neg.refresh || p->capa.neg.enhanced_rr || 365 p->capa.neg.as4byte || p->capa.neg.policy) { 366 printf(" Negotiated capabilities:\n"); 367 if (hascapamp) 368 show_neighbor_capa_mp(&p->capa.neg); 369 if (p->capa.neg.as4byte) 370 printf(" 4-byte AS numbers\n"); 371 if (p->capa.neg.refresh) 372 printf(" Route Refresh\n"); 373 if (p->capa.neg.enhanced_rr) 374 printf(" Enhanced Route Refresh\n"); 375 if (p->capa.neg.grestart.restart) 376 show_neighbor_capa_restart(&p->capa.neg); 377 if (hascapaap) 378 show_neighbor_capa_add_path(&p->capa.neg); 379 if (p->capa.neg.policy) 380 printf(" Open Policy role %s (local %s)\n", 381 log_policy(p->remote_role), 382 log_policy(p->conf.role)); 383 } 384 printf("\n"); 385 386 if (res->action == SHOW_NEIGHBOR_TIMERS) 387 return; 388 389 show_neighbor_msgstats(p); 390 printf("\n"); 391 392 errstr = fmt_errstr(p->stats.last_sent_errcode, 393 p->stats.last_sent_suberr); 394 if (errstr) 395 printf(" Last error sent: %s\n", errstr); 396 errstr = fmt_errstr(p->stats.last_rcvd_errcode, 397 p->stats.last_rcvd_suberr); 398 if (errstr) 399 printf(" Last error received: %s\n", errstr); 400 if (p->stats.last_reason[0]) { 401 printf(" Last received shutdown reason: \"%s\"\n", 402 log_reason(p->stats.last_reason)); 403 } 404 405 if (p->state >= STATE_OPENSENT) { 406 printf(" Local host: %20s, Local port: %5u\n", 407 log_addr(&p->local), p->local_port); 408 409 printf(" Remote host: %20s, Remote port: %5u\n", 410 log_addr(&p->remote), p->remote_port); 411 printf("\n"); 412 } 413 } 414 415 static void 416 show_neighbor(struct peer *p, struct parse_result *res) 417 { 418 char *s; 419 420 switch (res->action) { 421 case SHOW: 422 case SHOW_SUMMARY: 423 show_summary(p); 424 break; 425 case SHOW_SUMMARY_TERSE: 426 s = fmt_peer(p->conf.descr, &p->conf.remote_addr, 427 p->conf.remote_masklen); 428 printf("%s %s %s\n", s, log_as(p->conf.remote_as), 429 p->conf.template ? "Template" : statenames[p->state]); 430 free(s); 431 break; 432 case SHOW_NEIGHBOR: 433 case SHOW_NEIGHBOR_TIMERS: 434 show_neighbor_full(p, res); 435 break; 436 case SHOW_NEIGHBOR_TERSE: 437 s = fmt_peer(NULL, &p->conf.remote_addr, 438 p->conf.remote_masklen); 439 printf("%llu %llu %llu %llu %llu %llu %llu %llu %llu " 440 "%llu %u %u %llu %llu %llu %llu %s %s \"%s\"\n", 441 p->stats.msg_sent_open, p->stats.msg_rcvd_open, 442 p->stats.msg_sent_notification, 443 p->stats.msg_rcvd_notification, 444 p->stats.msg_sent_update, p->stats.msg_rcvd_update, 445 p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive, 446 p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh, 447 p->stats.prefix_cnt, p->conf.max_prefix, 448 p->stats.prefix_sent_update, p->stats.prefix_rcvd_update, 449 p->stats.prefix_sent_withdraw, 450 p->stats.prefix_rcvd_withdraw, s, 451 log_as(p->conf.remote_as), p->conf.descr); 452 free(s); 453 break; 454 default: 455 break; 456 } 457 } 458 459 static void 460 show_timer(struct ctl_timer *t) 461 { 462 printf(" %-20s ", timernames[t->type]); 463 464 if (t->val <= 0) 465 printf("%-20s\n", "due"); 466 else 467 printf("due in %-13s\n", fmt_timeframe(t->val)); 468 } 469 470 static void 471 show_fib(struct kroute_full *kf) 472 { 473 char *p; 474 475 if (asprintf(&p, "%s/%u", log_addr(&kf->prefix), kf->prefixlen) == -1) 476 err(1, NULL); 477 printf("%-5s %4i %-32s ", fmt_fib_flags(kf->flags), kf->priority, p); 478 free(p); 479 480 if (kf->flags & F_CONNECTED) 481 printf("link#%u", kf->ifindex); 482 else 483 printf("%s", log_addr(&kf->nexthop)); 484 if (kf->flags & F_MPLS) 485 printf(" mpls %d", ntohl(kf->mplslabel) >> MPLS_LABEL_OFFSET); 486 printf("\n"); 487 } 488 489 static void 490 show_fib_table(struct ktable *kt) 491 { 492 printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr, 493 kt->fib_sync ? "coupled" : "decoupled", 494 kt->fib_sync != kt->fib_conf ? "*" : ""); 495 } 496 497 static void 498 print_flowspec_list(struct flowspec *f, int type, int is_v6) 499 { 500 const uint8_t *comp; 501 const char *fmt; 502 int complen, off = 0; 503 504 if (flowspec_get_component(f->data, f->len, type, is_v6, 505 &comp, &complen) != 1) 506 return; 507 508 printf("%s ", flowspec_fmt_label(type)); 509 fmt = flowspec_fmt_num_op(comp, complen, &off); 510 if (off == -1) { 511 printf("%s ", fmt); 512 } else { 513 printf("{ %s ", fmt); 514 do { 515 fmt = flowspec_fmt_num_op(comp, complen, &off); 516 printf("%s ", fmt); 517 } while (off != -1); 518 printf("} "); 519 } 520 } 521 522 static void 523 print_flowspec_flags(struct flowspec *f, int type, int is_v6) 524 { 525 const uint8_t *comp; 526 const char *fmt, *flags; 527 int complen, off = 0; 528 529 switch (type) { 530 case FLOWSPEC_TYPE_TCP_FLAGS: 531 flags = FLOWSPEC_TCP_FLAG_STRING; 532 break; 533 case FLOWSPEC_TYPE_FRAG: 534 if (!is_v6) 535 flags = FLOWSPEC_FRAG_STRING4; 536 else 537 flags = FLOWSPEC_FRAG_STRING6; 538 break; 539 default: 540 printf("??? "); 541 return; 542 } 543 544 if (flowspec_get_component(f->data, f->len, type, is_v6, 545 &comp, &complen) != 1) 546 return; 547 548 printf("%s ", flowspec_fmt_label(type)); 549 550 fmt = flowspec_fmt_bin_op(comp, complen, &off, flags); 551 if (off == -1) { 552 printf("%s ", fmt); 553 } else { 554 printf("{ %s ", fmt); 555 do { 556 fmt = flowspec_fmt_bin_op(comp, complen, &off, flags); 557 printf("%s ", fmt); 558 } while (off != -1); 559 printf("} "); 560 } 561 } 562 563 static void 564 print_flowspec_addr(struct flowspec *f, int type, int is_v6) 565 { 566 struct bgpd_addr addr; 567 uint8_t plen; 568 569 flowspec_get_addr(f->data, f->len, type, is_v6, &addr, &plen, NULL); 570 if (plen == 0) 571 printf("%s any ", flowspec_fmt_label(type)); 572 else 573 printf("%s %s/%u ", flowspec_fmt_label(type), 574 log_addr(&addr), plen); 575 } 576 577 static void 578 show_flowspec(struct flowspec *f) 579 { 580 int is_v6 = (f->aid == AID_FLOWSPECv6); 581 582 printf("%-5s ", fmt_fib_flags(f->flags)); 583 print_flowspec_list(f, FLOWSPEC_TYPE_PROTO, is_v6); 584 585 print_flowspec_addr(f, FLOWSPEC_TYPE_SOURCE, is_v6); 586 print_flowspec_list(f, FLOWSPEC_TYPE_SRC_PORT, is_v6); 587 588 print_flowspec_addr(f, FLOWSPEC_TYPE_DEST, is_v6); 589 print_flowspec_list(f, FLOWSPEC_TYPE_DST_PORT, is_v6); 590 591 print_flowspec_list(f, FLOWSPEC_TYPE_DSCP, is_v6); 592 print_flowspec_list(f, FLOWSPEC_TYPE_PKT_LEN, is_v6); 593 print_flowspec_flags(f, FLOWSPEC_TYPE_TCP_FLAGS, is_v6); 594 print_flowspec_flags(f, FLOWSPEC_TYPE_FRAG, is_v6); 595 /* TODO: fixup the code handling to be like in the parser */ 596 print_flowspec_list(f, FLOWSPEC_TYPE_ICMP_TYPE, is_v6); 597 print_flowspec_list(f, FLOWSPEC_TYPE_ICMP_CODE, is_v6); 598 599 printf("\n"); 600 } 601 602 static void 603 show_nexthop(struct ctl_show_nexthop *nh) 604 { 605 char *s; 606 607 printf("%s %-15s ", nh->valid ? "*" : " ", log_addr(&nh->addr)); 608 if (!nh->krvalid) { 609 printf("\n"); 610 return; 611 } 612 if (asprintf(&s, "%s/%u", log_addr(&nh->kr.prefix), 613 nh->kr.prefixlen) == -1) 614 err(1, NULL); 615 printf("%-20s", s); 616 free(s); 617 printf("%3i %-15s ", nh->kr.priority, 618 nh->kr.flags & F_CONNECTED ? "connected" : 619 log_addr(&nh->kr.nexthop)); 620 621 if (nh->iface.ifname[0]) { 622 printf("%s (%s, %s)", nh->iface.ifname, 623 nh->iface.is_up ? "UP" : "DOWN", 624 nh->iface.baudrate ? 625 get_baudrate(nh->iface.baudrate, "bps") : 626 nh->iface.linkstate); 627 } 628 printf("\n"); 629 } 630 631 static void 632 show_interface(struct ctl_show_interface *iface) 633 { 634 printf("%-15s", iface->ifname); 635 printf("%-9u", iface->rdomain); 636 printf("%-9s", iface->nh_reachable ? "ok" : "invalid"); 637 printf("%-7s", iface->is_up ? "UP" : ""); 638 639 if (iface->media[0]) 640 printf("%s, ", iface->media); 641 printf("%s", iface->linkstate); 642 643 if (iface->baudrate > 0) 644 printf(", %s", get_baudrate(iface->baudrate, "Bit/s")); 645 printf("\n"); 646 } 647 648 static void 649 show_communities(struct ibuf *data, struct parse_result *res) 650 { 651 struct community c; 652 uint64_t ext; 653 uint8_t type = 0; 654 655 while (ibuf_size(data) != 0) { 656 if (ibuf_get(data, &c, sizeof(c)) == -1) { 657 warn("communities"); 658 break; 659 } 660 661 if (type != c.flags) { 662 if (type != 0) 663 printf("%c", EOL0(res->flags)); 664 printf(" %s:", fmt_attr(c.flags, 665 ATTR_OPTIONAL | ATTR_TRANSITIVE)); 666 type = c.flags; 667 } 668 669 switch (c.flags) { 670 case COMMUNITY_TYPE_BASIC: 671 printf(" %s", fmt_community(c.data1, c.data2)); 672 break; 673 case COMMUNITY_TYPE_LARGE: 674 printf(" %s", 675 fmt_large_community(c.data1, c.data2, c.data3)); 676 break; 677 case COMMUNITY_TYPE_EXT: 678 ext = (uint64_t)c.data3 << 48; 679 switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) { 680 case EXT_COMMUNITY_TRANS_TWO_AS: 681 case EXT_COMMUNITY_TRANS_OPAQUE: 682 case EXT_COMMUNITY_TRANS_EVPN: 683 ext |= ((uint64_t)c.data1 & 0xffff) << 32; 684 ext |= (uint64_t)c.data2; 685 break; 686 case EXT_COMMUNITY_TRANS_FOUR_AS: 687 case EXT_COMMUNITY_TRANS_IPV4: 688 ext |= (uint64_t)c.data1 << 16; 689 ext |= (uint64_t)c.data2 & 0xffff; 690 break; 691 } 692 printf(" %s", fmt_ext_community(ext)); 693 break; 694 } 695 } 696 697 printf("%c", EOL0(res->flags)); 698 } 699 700 static void 701 show_community(struct ibuf *buf) 702 { 703 uint16_t a, v; 704 705 while (ibuf_size(buf) > 0) { 706 if (ibuf_get_n16(buf, &a) == -1 || 707 ibuf_get_n16(buf, &v) == -1) { 708 printf("bad length"); 709 return; 710 } 711 printf("%s", fmt_community(a, v)); 712 713 if (ibuf_size(buf) > 0) 714 printf(" "); 715 } 716 } 717 718 static void 719 show_large_community(struct ibuf *buf) 720 { 721 uint32_t a, l1, l2; 722 723 while (ibuf_size(buf) > 0) { 724 if (ibuf_get_n32(buf, &a) == -1 || 725 ibuf_get_n32(buf, &l1) == -1 || 726 ibuf_get_n32(buf, &l2) == -1) { 727 printf("bad length"); 728 return; 729 } 730 printf("%s", fmt_large_community(a, l1, l2)); 731 732 if (ibuf_size(buf) > 0) 733 printf(" "); 734 } 735 } 736 737 static void 738 show_ext_community(struct ibuf *buf) 739 { 740 uint64_t ext; 741 742 while (ibuf_size(buf) > 0) { 743 if (ibuf_get_n64(buf, &ext) == -1) { 744 printf("bad length"); 745 return; 746 } 747 printf("%s", fmt_ext_community(ext)); 748 749 if (ibuf_size(buf) > 0) 750 printf(" "); 751 } 752 } 753 754 static void 755 show_attr(struct ibuf *buf, int reqflags, int addpath) 756 { 757 struct in_addr id; 758 struct bgpd_addr prefix; 759 struct ibuf asbuf, *path = NULL; 760 char *aspath; 761 size_t i, alen; 762 uint32_t as, pathid, val; 763 uint16_t short_as, afi; 764 uint8_t flags, type, safi, aid, prefixlen, origin, b; 765 int e2, e4; 766 767 if (ibuf_get_n8(buf, &flags) == -1 || 768 ibuf_get_n8(buf, &type) == -1) 769 goto bad_len; 770 771 /* get the attribute length */ 772 if (flags & ATTR_EXTLEN) { 773 uint16_t attr_len; 774 if (ibuf_get_n16(buf, &attr_len) == -1) 775 goto bad_len; 776 alen = attr_len; 777 } else { 778 uint8_t attr_len; 779 if (ibuf_get_n8(buf, &attr_len) == -1) 780 goto bad_len; 781 alen = attr_len; 782 } 783 784 /* bad imsg len how can that happen!? */ 785 if (alen > ibuf_size(buf)) 786 goto bad_len; 787 788 printf(" %s: ", fmt_attr(type, flags)); 789 790 switch (type) { 791 case ATTR_ORIGIN: 792 if (alen != 1 || ibuf_get_n8(buf, &origin) == -1) 793 goto bad_len; 794 printf("%s", fmt_origin(origin, 0)); 795 break; 796 case ATTR_ASPATH: 797 case ATTR_AS4_PATH: 798 /* prefer 4-byte AS here */ 799 e4 = aspath_verify(buf, 1, 0); 800 e2 = aspath_verify(buf, 0, 0); 801 if (e4 == 0 || e4 == AS_ERR_SOFT) { 802 ibuf_from_ibuf(&asbuf, buf); 803 } else if (e2 == 0 || e2 == AS_ERR_SOFT) { 804 if ((path = aspath_inflate(buf)) == NULL) { 805 printf("aspath_inflate failed"); 806 break; 807 } 808 ibuf_from_ibuf(&asbuf, path); 809 } else { 810 printf("bad AS-Path"); 811 break; 812 } 813 if (aspath_asprint(&aspath, &asbuf) == -1) 814 err(1, NULL); 815 printf("%s", aspath); 816 free(aspath); 817 ibuf_free(path); 818 break; 819 case ATTR_NEXTHOP: 820 case ATTR_ORIGINATOR_ID: 821 if (alen != 4 || ibuf_get(buf, &id, sizeof(id)) == -1) 822 goto bad_len; 823 printf("%s", inet_ntoa(id)); 824 break; 825 case ATTR_MED: 826 case ATTR_LOCALPREF: 827 if (alen != 4 || ibuf_get_n32(buf, &val) == -1) 828 goto bad_len; 829 printf("%u", val); 830 break; 831 case ATTR_AGGREGATOR: 832 case ATTR_AS4_AGGREGATOR: 833 if (alen == 8) { 834 if (ibuf_get_n32(buf, &as) == -1 || 835 ibuf_get(buf, &id, sizeof(id)) == -1) 836 goto bad_len; 837 } else if (alen == 6) { 838 if (ibuf_get_n16(buf, &short_as) == -1 || 839 ibuf_get(buf, &id, sizeof(id)) == -1) 840 goto bad_len; 841 as = short_as; 842 } else { 843 goto bad_len; 844 } 845 printf("%s [%s]", log_as(as), inet_ntoa(id)); 846 break; 847 case ATTR_COMMUNITIES: 848 show_community(buf); 849 break; 850 case ATTR_CLUSTER_LIST: 851 while (ibuf_size(buf) > 0) { 852 if (ibuf_get(buf, &id, sizeof(id)) == -1) 853 goto bad_len; 854 printf(" %s", inet_ntoa(id)); 855 } 856 break; 857 case ATTR_MP_REACH_NLRI: 858 case ATTR_MP_UNREACH_NLRI: 859 if (ibuf_get_n16(buf, &afi) == -1 || 860 ibuf_get_n8(buf, &safi) == -1) 861 goto bad_len; 862 863 if (afi2aid(afi, safi, &aid) == -1) { 864 printf("bad AFI/SAFI pair"); 865 break; 866 } 867 printf(" %s", aid2str(aid)); 868 869 if (type == ATTR_MP_REACH_NLRI) { 870 struct bgpd_addr nexthop; 871 uint8_t nhlen; 872 if (ibuf_get_n8(buf, &nhlen) == -1) 873 goto bad_len; 874 memset(&nexthop, 0, sizeof(nexthop)); 875 switch (aid) { 876 case AID_INET6: 877 nexthop.aid = aid; 878 if (nhlen != 16 && nhlen != 32) 879 goto bad_len; 880 if (ibuf_get(buf, &nexthop.v6, 881 sizeof(nexthop.v6)) == -1) 882 goto bad_len; 883 break; 884 case AID_VPN_IPv4: 885 if (nhlen != 12) 886 goto bad_len; 887 nexthop.aid = AID_INET; 888 if (ibuf_skip(buf, sizeof(uint64_t)) == -1 || 889 ibuf_get(buf, &nexthop.v4, 890 sizeof(nexthop.v4)) == -1) 891 goto bad_len; 892 break; 893 case AID_VPN_IPv6: 894 if (nhlen != 24) 895 goto bad_len; 896 nexthop.aid = AID_INET6; 897 if (ibuf_skip(buf, sizeof(uint64_t)) == -1 || 898 ibuf_get(buf, &nexthop.v6, 899 sizeof(nexthop.v6)) == -1) 900 goto bad_len; 901 break; 902 default: 903 printf("unhandled AID #%u", aid); 904 goto done; 905 } 906 /* ignore reserved (old SNPA) field as per RFC4760 */ 907 if (ibuf_skip(buf, 1) == -1) 908 goto bad_len; 909 910 printf(" nexthop: %s", log_addr(&nexthop)); 911 } 912 913 while (ibuf_size(buf) > 0) { 914 if (addpath) 915 if (ibuf_get_n32(buf, &pathid) == -1) 916 goto bad_len; 917 switch (aid) { 918 case AID_INET6: 919 if (nlri_get_prefix6(buf, &prefix, 920 &prefixlen) == -1) 921 goto bad_len; 922 break; 923 case AID_VPN_IPv4: 924 if (nlri_get_vpn4(buf, &prefix, 925 &prefixlen, 1) == -1) 926 goto bad_len; 927 break; 928 case AID_VPN_IPv6: 929 if (nlri_get_vpn6(buf, &prefix, 930 &prefixlen, 1) == -1) 931 goto bad_len; 932 break; 933 default: 934 printf("unhandled AID #%u", aid); 935 goto done; 936 } 937 printf(" %s/%u", log_addr(&prefix), prefixlen); 938 if (addpath) 939 printf(" path-id %u", pathid); 940 } 941 break; 942 case ATTR_EXT_COMMUNITIES: 943 show_ext_community(buf); 944 break; 945 case ATTR_LARGE_COMMUNITIES: 946 show_large_community(buf); 947 break; 948 case ATTR_OTC: 949 if (alen != 4 || ibuf_get_n32(buf, &as) == -1) 950 goto bad_len; 951 printf("%s", log_as(as)); 952 break; 953 case ATTR_ATOMIC_AGGREGATE: 954 default: 955 printf(" len %zu", alen); 956 if (alen) { 957 printf(":"); 958 for (i = 0; i < alen; i++) { 959 if (ibuf_get_n8(buf, &b) == -1) 960 goto bad_len; 961 printf(" %02x", b); 962 } 963 } 964 break; 965 } 966 967 done: 968 printf("%c", EOL0(reqflags)); 969 return; 970 971 bad_len: 972 printf("bad length%c", EOL0(reqflags)); 973 } 974 975 static void 976 show_rib_brief(struct ctl_show_rib *r, struct ibuf *asbuf) 977 { 978 char *p, *aspath; 979 980 if (asprintf(&p, "%s/%u", log_addr(&r->prefix), r->prefixlen) == -1) 981 err(1, NULL); 982 printf("%s %s-%s %-20s %-15s %5u %5u ", 983 fmt_flags(r->flags, 1), fmt_ovs(r->roa_validation_state, 1), 984 fmt_avs(r->aspa_validation_state, 1), p, 985 log_addr(&r->exit_nexthop), r->local_pref, r->med); 986 free(p); 987 988 if (aspath_asprint(&aspath, asbuf) == -1) 989 err(1, NULL); 990 if (strlen(aspath) > 0) 991 printf("%s ", aspath); 992 free(aspath); 993 994 printf("%s\n", fmt_origin(r->origin, 1)); 995 } 996 997 static void 998 show_rib_detail(struct ctl_show_rib *r, struct ibuf *asbuf, int flag0) 999 { 1000 struct in_addr id; 1001 char *aspath, *s; 1002 1003 printf("\nBGP routing table entry for %s/%u%c", 1004 log_addr(&r->prefix), r->prefixlen, 1005 EOL0(flag0)); 1006 1007 if (aspath_asprint(&aspath, asbuf) == -1) 1008 err(1, NULL); 1009 if (strlen(aspath) > 0) 1010 printf(" %s%c", aspath, EOL0(flag0)); 1011 free(aspath); 1012 1013 s = fmt_peer(r->descr, &r->remote_addr, -1); 1014 id.s_addr = htonl(r->remote_id); 1015 printf(" Nexthop %s ", log_addr(&r->exit_nexthop)); 1016 printf("(via %s) Neighbor %s (%s)", log_addr(&r->true_nexthop), s, 1017 inet_ntoa(id)); 1018 if (r->flags & F_PREF_PATH_ID) 1019 printf(" Path-Id: %u", r->path_id); 1020 printf("%c", EOL0(flag0)); 1021 free(s); 1022 1023 printf(" Origin %s, metric %u, localpref %u, weight %u, ovs %s, ", 1024 fmt_origin(r->origin, 0), r->med, r->local_pref, r->weight, 1025 fmt_ovs(r->roa_validation_state, 0)); 1026 printf("avs %s, %s", fmt_avs(r->aspa_validation_state, 0), 1027 fmt_flags(r->flags, 0)); 1028 1029 printf("%c Last update: %s ago%c", EOL0(flag0), 1030 fmt_timeframe(r->age), EOL0(flag0)); 1031 } 1032 1033 static void 1034 show_rib(struct ctl_show_rib *r, struct ibuf *aspath, struct parse_result *res) 1035 { 1036 if (res->flags & F_CTL_DETAIL) 1037 show_rib_detail(r, aspath, res->flags); 1038 else 1039 show_rib_brief(r, aspath); 1040 } 1041 1042 static void 1043 show_rib_mem(struct rde_memstats *stats) 1044 { 1045 size_t pts = 0; 1046 int i; 1047 1048 printf("RDE memory statistics\n"); 1049 for (i = 0; i < AID_MAX; i++) { 1050 if (stats->pt_cnt[i] == 0) 1051 continue; 1052 pts += stats->pt_size[i]; 1053 printf("%10lld %s network entries using %s of memory\n", 1054 stats->pt_cnt[i], aid_vals[i].name, 1055 fmt_mem(stats->pt_size[i])); 1056 } 1057 printf("%10lld rib entries using %s of memory\n", 1058 stats->rib_cnt, fmt_mem(stats->rib_cnt * 1059 sizeof(struct rib_entry))); 1060 printf("%10lld prefix entries using %s of memory\n", 1061 stats->prefix_cnt, fmt_mem(stats->prefix_cnt * 1062 sizeof(struct prefix))); 1063 printf("%10lld BGP path attribute entries using %s of memory\n", 1064 stats->path_cnt, fmt_mem(stats->path_cnt * 1065 sizeof(struct rde_aspath))); 1066 printf("\t and holding %lld references\n", 1067 stats->path_refs); 1068 printf("%10lld BGP AS-PATH attribute entries using " 1069 "%s of memory\n", stats->aspath_cnt, fmt_mem(stats->aspath_size)); 1070 printf("%10lld entries for %lld BGP communities " 1071 "using %s of memory\n", stats->comm_cnt, stats->comm_nmemb, 1072 fmt_mem(stats->comm_cnt * sizeof(struct rde_community) + 1073 stats->comm_size * sizeof(struct community))); 1074 printf("\t and holding %lld references\n", 1075 stats->comm_refs); 1076 printf("%10lld BGP attributes entries using %s of memory\n", 1077 stats->attr_cnt, fmt_mem(stats->attr_cnt * 1078 sizeof(struct attr))); 1079 printf("\t and holding %lld references\n", 1080 stats->attr_refs); 1081 printf("%10lld BGP attributes using %s of memory\n", 1082 stats->attr_dcnt, fmt_mem(stats->attr_data)); 1083 printf("%10lld as-set elements in %lld tables using " 1084 "%s of memory\n", stats->aset_nmemb, stats->aset_cnt, 1085 fmt_mem(stats->aset_size)); 1086 printf("%10lld prefix-set elements using %s of memory\n", 1087 stats->pset_cnt, fmt_mem(stats->pset_size)); 1088 printf("RIB using %s of memory\n", fmt_mem(pts + 1089 stats->prefix_cnt * sizeof(struct prefix) + 1090 stats->rib_cnt * sizeof(struct rib_entry) + 1091 stats->path_cnt * sizeof(struct rde_aspath) + 1092 stats->aspath_size + stats->attr_cnt * sizeof(struct attr) + 1093 stats->attr_data)); 1094 printf("Sets using %s of memory\n", fmt_mem(stats->aset_size + 1095 stats->pset_size)); 1096 } 1097 1098 static void 1099 show_rib_set(struct ctl_show_set *set) 1100 { 1101 char buf[64]; 1102 1103 if (set->type == ASNUM_SET || set->type == ASPA_SET) 1104 snprintf(buf, sizeof(buf), "%7s %7s %6zu", 1105 "-", "-", set->as_cnt); 1106 else 1107 snprintf(buf, sizeof(buf), "%7zu %7zu %6s", 1108 set->v4_cnt, set->v6_cnt, "-"); 1109 1110 printf("%-6s %-34s %s %11s\n", fmt_set_type(set), set->name, 1111 buf, fmt_monotime(set->lastchange)); 1112 } 1113 1114 static void 1115 show_rtr(struct ctl_show_rtr *rtr) 1116 { 1117 static int not_first; 1118 1119 if (not_first) 1120 printf("\n"); 1121 not_first = 1; 1122 1123 printf("RTR neighbor is %s, port %u\n", 1124 log_addr(&rtr->remote_addr), rtr->remote_port); 1125 printf(" State: %s\n", rtr->state); 1126 if (rtr->descr[0]) 1127 printf(" Description: %s\n", rtr->descr); 1128 if (rtr->local_addr.aid != AID_UNSPEC) 1129 printf(" Local Address: %s\n", log_addr(&rtr->local_addr)); 1130 if (rtr->session_id != -1) 1131 printf(" Version: %u Session ID: %d Serial #: %u\n", 1132 rtr->version, rtr->session_id, rtr->serial); 1133 printf(" Refresh: %u, Retry: %u, Expire: %u\n", 1134 rtr->refresh, rtr->retry, rtr->expire); 1135 1136 if (rtr->last_sent_error != NO_ERROR) { 1137 printf(" Last sent error: %s\n", 1138 log_rtr_error(rtr->last_sent_error)); 1139 if (rtr->last_sent_msg[0]) 1140 printf(" with reason \"%s\"\n", 1141 log_reason(rtr->last_sent_msg)); 1142 } 1143 if (rtr->last_recv_error != NO_ERROR) { 1144 printf(" Last received error: %s\n", 1145 log_rtr_error(rtr->last_recv_error)); 1146 if (rtr->last_recv_msg[0]) 1147 printf(" with reason \"%s\"\n", 1148 log_reason(rtr->last_recv_msg)); 1149 } 1150 1151 printf("\n"); 1152 } 1153 1154 static void 1155 show_result(u_int rescode) 1156 { 1157 if (rescode == 0) 1158 printf("request processed\n"); 1159 else if (rescode >= 1160 sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) 1161 printf("unknown result error code %u\n", rescode); 1162 else 1163 printf("%s\n", ctl_res_strerror[rescode]); 1164 } 1165 1166 static void 1167 show_tail(void) 1168 { 1169 /* nothing */ 1170 } 1171 1172 const struct output show_output = { 1173 .head = show_head, 1174 .neighbor = show_neighbor, 1175 .timer = show_timer, 1176 .fib = show_fib, 1177 .fib_table = show_fib_table, 1178 .flowspec = show_flowspec, 1179 .nexthop = show_nexthop, 1180 .interface = show_interface, 1181 .communities = show_communities, 1182 .attr = show_attr, 1183 .rib = show_rib, 1184 .rib_mem = show_rib_mem, 1185 .set = show_rib_set, 1186 .rtr = show_rtr, 1187 .result = show_result, 1188 .tail = show_tail, 1189 }; 1190