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