1 /* $OpenBSD: bgpctl.c,v 1.187 2015/12/05 13:17:05 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/un.h> 22 #include <net/if.h> 23 #include <net/if_media.h> 24 #include <net/if_types.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <netdb.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <util.h> 35 36 #include "bgpd.h" 37 #include "session.h" 38 #include "rde.h" 39 #include "log.h" 40 #include "parser.h" 41 #include "irrfilter.h" 42 #include "mrtparser.h" 43 44 enum neighbor_views { 45 NV_DEFAULT, 46 NV_TIMERS 47 }; 48 49 int main(int, char *[]); 50 char *fmt_peer(const char *, const struct bgpd_addr *, int, int); 51 void show_summary_head(void); 52 int show_summary_msg(struct imsg *, int); 53 int show_summary_terse_msg(struct imsg *, int); 54 int show_neighbor_terse(struct imsg *); 55 int show_neighbor_msg(struct imsg *, enum neighbor_views); 56 void print_neighbor_capa_mp(struct peer *); 57 void print_neighbor_capa_restart(struct peer *); 58 void print_neighbor_msgstats(struct peer *); 59 void print_timer(const char *, time_t); 60 static char *fmt_timeframe(time_t t); 61 static char *fmt_timeframe_core(time_t t); 62 void show_fib_head(void); 63 void show_fib_tables_head(void); 64 void show_network_head(void); 65 void show_fib_flags(u_int16_t); 66 int show_fib_msg(struct imsg *); 67 void show_nexthop_head(void); 68 int show_nexthop_msg(struct imsg *); 69 void show_interface_head(void); 70 uint64_t ift2ifm(uint8_t); 71 const char * get_media_descr(uint64_t); 72 const char * get_linkstate(uint8_t, int); 73 const char * get_baudrate(u_int64_t, char *); 74 int show_interface_msg(struct imsg *); 75 void show_rib_summary_head(void); 76 void print_prefix(struct bgpd_addr *, u_int8_t, u_int8_t); 77 const char * print_origin(u_int8_t, int); 78 void print_flags(u_int8_t, int); 79 int show_rib_summary_msg(struct imsg *); 80 int show_rib_detail_msg(struct imsg *, int); 81 void show_rib_brief(struct ctl_show_rib *, u_char *); 82 void show_rib_detail(struct ctl_show_rib *, u_char *, int); 83 void show_attr(void *, u_int16_t); 84 void show_community(u_char *, u_int16_t); 85 void show_ext_community(u_char *, u_int16_t); 86 char *fmt_mem(int64_t); 87 int show_rib_memory_msg(struct imsg *); 88 void send_filterset(struct imsgbuf *, struct filter_set_head *); 89 const char *get_errstr(u_int8_t, u_int8_t); 90 int show_result(struct imsg *); 91 void show_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *); 92 void network_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *); 93 void show_mrt_state(struct mrt_bgp_state *, void *); 94 void show_mrt_msg(struct mrt_bgp_msg *, void *); 95 void mrt_to_bgpd_addr(union mrt_addr *, struct bgpd_addr *); 96 const char *msg_type(u_int8_t); 97 void network_bulk(struct parse_result *); 98 const char *print_auth_method(enum auth_method); 99 100 struct imsgbuf *ibuf; 101 struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg }; 102 struct mrt_parser net_mrt = { network_mrt_dump, NULL, NULL }; 103 104 __dead void 105 usage(void) 106 { 107 extern char *__progname; 108 109 fprintf(stderr, "usage: %s [-n] [-s socket] command [argument ...]\n", 110 __progname); 111 exit(1); 112 } 113 114 int 115 main(int argc, char *argv[]) 116 { 117 struct sockaddr_un sun; 118 int fd, n, done, ch, nodescr = 0, verbose = 0; 119 struct imsg imsg; 120 struct network_config net; 121 struct parse_result *res; 122 struct ctl_neighbor neighbor; 123 struct ctl_show_rib_request ribreq; 124 char *sockname; 125 enum imsg_type type; 126 127 if (pledge("stdio rpath wpath cpath unix inet dns", NULL) == -1) 128 err(1, "pledge"); 129 130 sockname = SOCKET_NAME; 131 while ((ch = getopt(argc, argv, "ns:")) != -1) { 132 switch (ch) { 133 case 'n': 134 if (++nodescr > 1) 135 usage(); 136 break; 137 case 's': 138 sockname = optarg; 139 break; 140 default: 141 usage(); 142 /* NOTREACHED */ 143 } 144 } 145 argc -= optind; 146 argv += optind; 147 148 if ((res = parse(argc, argv)) == NULL) 149 exit(1); 150 151 if (res->action == IRRFILTER) { 152 if (!(res->flags & (F_IPV4|F_IPV6))) 153 res->flags |= (F_IPV4|F_IPV6); 154 irr_main(res->as.as, res->flags, res->irr_outdir); 155 } 156 157 if (pledge("stdio rpath wpath unix", NULL) == -1) 158 err(1, "pledge"); 159 160 memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr)); 161 strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr)); 162 163 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 164 err(1, "control_init: socket"); 165 166 bzero(&sun, sizeof(sun)); 167 sun.sun_family = AF_UNIX; 168 if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >= 169 sizeof(sun.sun_path)) 170 errx(1, "socket name too long"); 171 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 172 err(1, "connect: %s", sockname); 173 174 if (pledge("stdio rpath wpath", NULL) == -1) 175 err(1, "pledge"); 176 177 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 178 err(1, NULL); 179 imsg_init(ibuf, fd); 180 done = 0; 181 182 switch (res->action) { 183 case NONE: 184 case IRRFILTER: 185 usage(); 186 /* NOTREACHED */ 187 case SHOW: 188 case SHOW_SUMMARY: 189 imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, NULL, 0); 190 show_summary_head(); 191 break; 192 case SHOW_SUMMARY_TERSE: 193 imsg_compose(ibuf, IMSG_CTL_SHOW_TERSE, 0, 0, -1, NULL, 0); 194 break; 195 case SHOW_FIB: 196 if (!res->addr.aid) { 197 struct ibuf *msg; 198 sa_family_t af; 199 200 af = aid2af(res->aid); 201 if ((msg = imsg_create(ibuf, IMSG_CTL_KROUTE, 202 res->rtableid, 0, sizeof(res->flags) + 203 sizeof(af))) == NULL) 204 errx(1, "imsg_create failure"); 205 if (imsg_add(msg, &res->flags, sizeof(res->flags)) == 206 -1 || 207 imsg_add(msg, &af, sizeof(af)) == -1) 208 errx(1, "imsg_add failure"); 209 imsg_close(ibuf, msg); 210 } else 211 imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, res->rtableid, 212 0, -1, &res->addr, sizeof(res->addr)); 213 show_fib_head(); 214 break; 215 case SHOW_FIB_TABLES: 216 imsg_compose(ibuf, IMSG_CTL_SHOW_FIB_TABLES, 0, 0, -1, NULL, 0); 217 show_fib_tables_head(); 218 break; 219 case SHOW_NEXTHOP: 220 imsg_compose(ibuf, IMSG_CTL_SHOW_NEXTHOP, res->rtableid, 0, -1, 221 NULL, 0); 222 show_nexthop_head(); 223 break; 224 case SHOW_INTERFACE: 225 imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, NULL, 0); 226 show_interface_head(); 227 break; 228 case SHOW_NEIGHBOR: 229 case SHOW_NEIGHBOR_TIMERS: 230 case SHOW_NEIGHBOR_TERSE: 231 neighbor.show_timers = (res->action == SHOW_NEIGHBOR_TIMERS); 232 if (res->peeraddr.aid || res->peerdesc[0]) 233 imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 234 &neighbor, sizeof(neighbor)); 235 else 236 imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 237 NULL, 0); 238 break; 239 case SHOW_RIB: 240 bzero(&ribreq, sizeof(ribreq)); 241 type = IMSG_CTL_SHOW_RIB; 242 if (res->as.type != AS_NONE) { 243 memcpy(&ribreq.as, &res->as, sizeof(res->as)); 244 type = IMSG_CTL_SHOW_RIB_AS; 245 } 246 if (res->addr.aid) { 247 memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr)); 248 ribreq.prefixlen = res->prefixlen; 249 type = IMSG_CTL_SHOW_RIB_PREFIX; 250 } 251 if (res->community.as != COMMUNITY_UNSET && 252 res->community.type != COMMUNITY_UNSET) { 253 memcpy(&ribreq.community, &res->community, 254 sizeof(res->community)); 255 type = IMSG_CTL_SHOW_RIB_COMMUNITY; 256 } 257 memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); 258 strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); 259 ribreq.aid = res->aid; 260 ribreq.flags = res->flags; 261 imsg_compose(ibuf, type, 0, 0, -1, &ribreq, sizeof(ribreq)); 262 if (!(res->flags & F_CTL_DETAIL)) 263 show_rib_summary_head(); 264 break; 265 case SHOW_MRT: 266 close(fd); 267 bzero(&ribreq, sizeof(ribreq)); 268 if (res->as.type != AS_NONE) 269 memcpy(&ribreq.as, &res->as, sizeof(res->as)); 270 if (res->addr.aid) { 271 memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr)); 272 ribreq.prefixlen = res->prefixlen; 273 } 274 if (res->community.as != COMMUNITY_UNSET && 275 res->community.type != COMMUNITY_UNSET) 276 memcpy(&ribreq.community, &res->community, 277 sizeof(res->community)); 278 memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); 279 ribreq.aid = res->aid; 280 ribreq.flags = res->flags; 281 show_mrt.arg = &ribreq; 282 if (!(res->flags & F_CTL_DETAIL)) 283 show_rib_summary_head(); 284 mrt_parse(res->mrtfd, &show_mrt, 1); 285 exit(0); 286 case SHOW_RIB_MEM: 287 imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); 288 break; 289 case RELOAD: 290 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 291 printf("reload request sent.\n"); 292 break; 293 case FIB: 294 errx(1, "action==FIB"); 295 break; 296 case FIB_COUPLE: 297 imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, res->rtableid, 0, -1, 298 NULL, 0); 299 printf("couple request sent.\n"); 300 done = 1; 301 break; 302 case FIB_DECOUPLE: 303 imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, res->rtableid, 0, -1, 304 NULL, 0); 305 printf("decouple request sent.\n"); 306 done = 1; 307 break; 308 case NEIGHBOR: 309 errx(1, "action==NEIGHBOR"); 310 break; 311 case NEIGHBOR_UP: 312 imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_UP, 0, 0, -1, 313 &neighbor, sizeof(neighbor)); 314 break; 315 case NEIGHBOR_DOWN: 316 imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_DOWN, 0, 0, -1, 317 &neighbor, sizeof(neighbor)); 318 break; 319 case NEIGHBOR_CLEAR: 320 imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_CLEAR, 0, 0, -1, 321 &neighbor, sizeof(neighbor)); 322 break; 323 case NEIGHBOR_RREFRESH: 324 imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_RREFRESH, 0, 0, -1, 325 &neighbor, sizeof(neighbor)); 326 break; 327 case NEIGHBOR_DESTROY: 328 imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_DESTROY, 0, 0, -1, 329 &neighbor, sizeof(neighbor)); 330 break; 331 case NETWORK_BULK_ADD: 332 case NETWORK_BULK_REMOVE: 333 network_bulk(res); 334 printf("requests sent.\n"); 335 done = 1; 336 break; 337 case NETWORK_ADD: 338 case NETWORK_REMOVE: 339 bzero(&net, sizeof(net)); 340 memcpy(&net.prefix, &res->addr, sizeof(res->addr)); 341 net.prefixlen = res->prefixlen; 342 /* attribute sets are not supported */ 343 if (res->action == NETWORK_ADD) { 344 imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1, 345 &net, sizeof(net)); 346 send_filterset(ibuf, &res->set); 347 imsg_compose(ibuf, IMSG_NETWORK_DONE, 0, 0, -1, 348 NULL, 0); 349 } else 350 imsg_compose(ibuf, IMSG_NETWORK_REMOVE, 0, 0, -1, 351 &net, sizeof(net)); 352 printf("request sent.\n"); 353 done = 1; 354 break; 355 case NETWORK_FLUSH: 356 imsg_compose(ibuf, IMSG_NETWORK_FLUSH, 0, 0, -1, NULL, 0); 357 printf("request sent.\n"); 358 done = 1; 359 break; 360 case NETWORK_SHOW: 361 bzero(&ribreq, sizeof(ribreq)); 362 ribreq.aid = res->aid; 363 strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); 364 imsg_compose(ibuf, IMSG_CTL_SHOW_NETWORK, 0, 0, -1, 365 &ribreq, sizeof(ribreq)); 366 show_network_head(); 367 break; 368 case NETWORK_MRT: 369 bzero(&ribreq, sizeof(ribreq)); 370 if (res->as.type != AS_NONE) 371 memcpy(&ribreq.as, &res->as, sizeof(res->as)); 372 if (res->addr.aid) { 373 memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr)); 374 ribreq.prefixlen = res->prefixlen; 375 } 376 if (res->community.as != COMMUNITY_UNSET && 377 res->community.type != COMMUNITY_UNSET) 378 memcpy(&ribreq.community, &res->community, 379 sizeof(res->community)); 380 memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); 381 ribreq.aid = res->aid; 382 ribreq.flags = res->flags; 383 net_mrt.arg = &ribreq; 384 mrt_parse(res->mrtfd, &net_mrt, 1); 385 done = 1; 386 break; 387 case LOG_VERBOSE: 388 verbose = 1; 389 /* FALLTHROUGH */ 390 case LOG_BRIEF: 391 imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 392 &verbose, sizeof(verbose)); 393 printf("logging request sent.\n"); 394 done = 1; 395 break; 396 } 397 398 while (ibuf->w.queued) 399 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 400 err(1, "write error"); 401 402 while (!done) { 403 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 404 err(1, "imsg_read error"); 405 if (n == 0) 406 errx(1, "pipe closed"); 407 408 while (!done) { 409 if ((n = imsg_get(ibuf, &imsg)) == -1) 410 err(1, "imsg_get error"); 411 if (n == 0) 412 break; 413 414 if (imsg.hdr.type == IMSG_CTL_RESULT) { 415 done = show_result(&imsg); 416 imsg_free(&imsg); 417 continue; 418 } 419 420 switch (res->action) { 421 case SHOW: 422 case SHOW_SUMMARY: 423 done = show_summary_msg(&imsg, nodescr); 424 break; 425 case SHOW_SUMMARY_TERSE: 426 done = show_summary_terse_msg(&imsg, nodescr); 427 break; 428 case SHOW_FIB: 429 case SHOW_FIB_TABLES: 430 case NETWORK_SHOW: 431 done = show_fib_msg(&imsg); 432 break; 433 case SHOW_NEXTHOP: 434 done = show_nexthop_msg(&imsg); 435 break; 436 case SHOW_INTERFACE: 437 done = show_interface_msg(&imsg); 438 break; 439 case SHOW_NEIGHBOR: 440 done = show_neighbor_msg(&imsg, NV_DEFAULT); 441 break; 442 case SHOW_NEIGHBOR_TIMERS: 443 done = show_neighbor_msg(&imsg, NV_TIMERS); 444 break; 445 case SHOW_NEIGHBOR_TERSE: 446 done = show_neighbor_terse(&imsg); 447 break; 448 case SHOW_RIB: 449 if (res->flags & F_CTL_DETAIL) 450 done = show_rib_detail_msg(&imsg, 451 nodescr); 452 else 453 done = show_rib_summary_msg(&imsg); 454 break; 455 case SHOW_RIB_MEM: 456 done = show_rib_memory_msg(&imsg); 457 break; 458 case NEIGHBOR: 459 case NEIGHBOR_UP: 460 case NEIGHBOR_DOWN: 461 case NEIGHBOR_CLEAR: 462 case NEIGHBOR_RREFRESH: 463 case NEIGHBOR_DESTROY: 464 case NONE: 465 case RELOAD: 466 case FIB: 467 case FIB_COUPLE: 468 case FIB_DECOUPLE: 469 case NETWORK_ADD: 470 case NETWORK_REMOVE: 471 case NETWORK_FLUSH: 472 case NETWORK_BULK_ADD: 473 case NETWORK_BULK_REMOVE: 474 case IRRFILTER: 475 case LOG_VERBOSE: 476 case LOG_BRIEF: 477 case SHOW_MRT: 478 case NETWORK_MRT: 479 break; 480 } 481 imsg_free(&imsg); 482 } 483 } 484 close(fd); 485 free(ibuf); 486 487 exit(0); 488 } 489 490 char * 491 fmt_peer(const char *descr, const struct bgpd_addr *remote_addr, 492 int masklen, int nodescr) 493 { 494 const char *ip; 495 char *p; 496 497 if (descr[0] && !nodescr) { 498 if ((p = strdup(descr)) == NULL) 499 err(1, NULL); 500 return (p); 501 } 502 503 ip = log_addr(remote_addr); 504 if (masklen != -1 && ((remote_addr->aid == AID_INET && masklen != 32) || 505 (remote_addr->aid == AID_INET6 && masklen != 128))) { 506 if (asprintf(&p, "%s/%u", ip, masklen) == -1) 507 err(1, NULL); 508 } else { 509 if ((p = strdup(ip)) == NULL) 510 err(1, NULL); 511 } 512 513 return (p); 514 } 515 516 void 517 show_summary_head(void) 518 { 519 printf("%-20s %8s %10s %10s %5s %-8s %s\n", "Neighbor", "AS", 520 "MsgRcvd", "MsgSent", "OutQ", "Up/Down", "State/PrfRcvd"); 521 } 522 523 int 524 show_summary_msg(struct imsg *imsg, int nodescr) 525 { 526 struct peer *p; 527 char *s; 528 const char *a; 529 size_t alen; 530 531 switch (imsg->hdr.type) { 532 case IMSG_CTL_SHOW_NEIGHBOR: 533 p = imsg->data; 534 s = fmt_peer(p->conf.descr, &p->conf.remote_addr, 535 p->conf.remote_masklen, nodescr); 536 537 a = log_as(p->conf.remote_as); 538 alen = strlen(a); 539 /* max displayed lenght of the peers name is 28 */ 540 if (alen < 28) { 541 if (strlen(s) > 28 - alen) 542 s[28 - alen] = 0; 543 } else 544 alen = 0; 545 546 printf("%-*s %s %10llu %10llu %5u %-8s ", 547 (28 - (int)alen), s, a, 548 p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + 549 p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + 550 p->stats.msg_rcvd_rrefresh, 551 p->stats.msg_sent_open + p->stats.msg_sent_notification + 552 p->stats.msg_sent_update + p->stats.msg_sent_keepalive + 553 p->stats.msg_sent_rrefresh, 554 p->wbuf.queued, 555 fmt_timeframe(p->stats.last_updown)); 556 if (p->state == STATE_ESTABLISHED) { 557 printf("%6u", p->stats.prefix_cnt); 558 if (p->conf.max_prefix != 0) 559 printf("/%u", p->conf.max_prefix); 560 } else if (p->conf.template) 561 printf("Template"); 562 else 563 printf("%s", statenames[p->state]); 564 printf("\n"); 565 free(s); 566 break; 567 case IMSG_CTL_END: 568 return (1); 569 default: 570 break; 571 } 572 573 return (0); 574 } 575 576 int 577 show_summary_terse_msg(struct imsg *imsg, int nodescr) 578 { 579 struct peer *p; 580 char *s; 581 582 switch (imsg->hdr.type) { 583 case IMSG_CTL_SHOW_NEIGHBOR: 584 p = imsg->data; 585 s = fmt_peer(p->conf.descr, &p->conf.remote_addr, 586 p->conf.remote_masklen, nodescr); 587 printf("%s %s %s\n", s, log_as(p->conf.remote_as), 588 p->conf.template ? "Template" : statenames[p->state]); 589 free(s); 590 break; 591 case IMSG_CTL_END: 592 return (1); 593 default: 594 break; 595 } 596 597 return (0); 598 } 599 600 int 601 show_neighbor_terse(struct imsg *imsg) 602 { 603 struct peer *p; 604 605 switch (imsg->hdr.type) { 606 case IMSG_CTL_SHOW_NEIGHBOR: 607 p = imsg->data; 608 printf("%llu %llu %llu %llu %llu %llu %llu " 609 "%llu %llu %llu %u %u %llu %llu %llu %llu\n", 610 p->stats.msg_sent_open, p->stats.msg_rcvd_open, 611 p->stats.msg_sent_notification, 612 p->stats.msg_rcvd_notification, 613 p->stats.msg_sent_update, p->stats.msg_rcvd_update, 614 p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive, 615 p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh, 616 p->stats.prefix_cnt, p->conf.max_prefix, 617 p->stats.prefix_sent_update, p->stats.prefix_rcvd_update, 618 p->stats.prefix_sent_withdraw, 619 p->stats.prefix_rcvd_withdraw); 620 break; 621 case IMSG_CTL_END: 622 return (1); 623 default: 624 break; 625 } 626 627 return (0); 628 } 629 630 const char * 631 print_auth_method(enum auth_method method) 632 { 633 switch (method) { 634 case AUTH_MD5SIG: 635 return ", using md5sig"; 636 case AUTH_IPSEC_MANUAL_ESP: 637 return ", using ipsec manual esp"; 638 case AUTH_IPSEC_MANUAL_AH: 639 return ", using ipsec manual ah"; 640 case AUTH_IPSEC_IKE_ESP: 641 return ", using ipsec ike esp"; 642 case AUTH_IPSEC_IKE_AH: 643 return ", using ipsec ike ah"; 644 case AUTH_NONE: /* FALLTHROUGH */ 645 default: 646 return ""; 647 } 648 } 649 650 int 651 show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv) 652 { 653 struct peer *p; 654 struct ctl_timer *t; 655 struct in_addr ina; 656 char buf[NI_MAXHOST], pbuf[NI_MAXSERV], *s; 657 int hascapamp = 0; 658 u_int8_t i; 659 660 switch (imsg->hdr.type) { 661 case IMSG_CTL_SHOW_NEIGHBOR: 662 p = imsg->data; 663 if ((p->conf.remote_addr.aid == AID_INET && 664 p->conf.remote_masklen != 32) || 665 (p->conf.remote_addr.aid == AID_INET6 && 666 p->conf.remote_masklen != 128)) { 667 if (asprintf(&s, "%s/%u", 668 log_addr(&p->conf.remote_addr), 669 p->conf.remote_masklen) == -1) 670 err(1, NULL); 671 } else 672 if ((s = strdup(log_addr(&p->conf.remote_addr))) == 673 NULL) 674 err(1, "strdup"); 675 676 ina.s_addr = p->remote_bgpid; 677 printf("BGP neighbor is %s, ", s); 678 free(s); 679 if (p->conf.remote_as == 0 && p->conf.template) 680 printf("remote AS: accept any"); 681 else 682 printf("remote AS %s", log_as(p->conf.remote_as)); 683 if (p->conf.template) 684 printf(", Template"); 685 if (p->template) 686 printf(", Cloned"); 687 if (p->conf.passive) 688 printf(", Passive"); 689 if (p->conf.ebgp && p->conf.distance > 1) 690 printf(", Multihop (%u)", (int)p->conf.distance); 691 printf("\n"); 692 if (p->conf.descr[0]) 693 printf(" Description: %s\n", p->conf.descr); 694 if (p->conf.max_prefix) { 695 printf(" Max-prefix: %u", p->conf.max_prefix); 696 if (p->conf.max_prefix_restart) 697 printf(" (restart %u)", 698 p->conf.max_prefix_restart); 699 printf("\n"); 700 } 701 printf(" BGP version 4, remote router-id %s", 702 inet_ntoa(ina)); 703 printf("%s\n", print_auth_method(p->auth.method)); 704 printf(" BGP state = %s", statenames[p->state]); 705 if (p->stats.last_updown != 0) 706 printf(", %s for %s", 707 p->state == STATE_ESTABLISHED ? "up" : "down", 708 fmt_timeframe(p->stats.last_updown)); 709 printf("\n"); 710 printf(" Last read %s, holdtime %us, keepalive interval %us\n", 711 fmt_timeframe(p->stats.last_read), 712 p->holdtime, p->holdtime/3); 713 for (i = 0; i < AID_MAX; i++) 714 if (p->capa.peer.mp[i]) 715 hascapamp = 1; 716 if (hascapamp || p->capa.peer.refresh || 717 p->capa.peer.grestart.restart || p->capa.peer.as4byte) { 718 printf(" Neighbor capabilities:\n"); 719 if (hascapamp) { 720 printf(" Multiprotocol extensions: "); 721 print_neighbor_capa_mp(p); 722 printf("\n"); 723 } 724 if (p->capa.peer.refresh) 725 printf(" Route Refresh\n"); 726 if (p->capa.peer.grestart.restart) { 727 printf(" Graceful Restart"); 728 print_neighbor_capa_restart(p); 729 printf("\n"); 730 } 731 if (p->capa.peer.as4byte) 732 printf(" 4-byte AS numbers\n"); 733 } 734 printf("\n"); 735 if (nv == NV_TIMERS) 736 break; 737 print_neighbor_msgstats(p); 738 printf("\n"); 739 if (p->state == STATE_IDLE) { 740 static const char *errstr; 741 742 errstr = get_errstr(p->stats.last_sent_errcode, 743 p->stats.last_sent_suberr); 744 if (errstr) 745 printf(" Last error: %s\n\n", errstr); 746 } else { 747 if (getnameinfo((struct sockaddr *)&p->sa_local, 748 (socklen_t)p->sa_local.ss_len, 749 buf, sizeof(buf), pbuf, sizeof(pbuf), 750 NI_NUMERICHOST | NI_NUMERICSERV)) { 751 strlcpy(buf, "(unknown)", sizeof(buf)); 752 strlcpy(pbuf, "", sizeof(pbuf)); 753 } 754 printf(" Local host: %20s, Local port: %5s\n", buf, 755 pbuf); 756 757 if (getnameinfo((struct sockaddr *)&p->sa_remote, 758 (socklen_t)p->sa_remote.ss_len, 759 buf, sizeof(buf), pbuf, sizeof(pbuf), 760 NI_NUMERICHOST | NI_NUMERICSERV)) { 761 strlcpy(buf, "(unknown)", sizeof(buf)); 762 strlcpy(pbuf, "", sizeof(pbuf)); 763 } 764 printf(" Remote host: %20s, Remote port: %5s\n", buf, 765 pbuf); 766 printf("\n"); 767 } 768 break; 769 case IMSG_CTL_SHOW_TIMER: 770 t = imsg->data; 771 if (t->type > 0 && t->type < Timer_Max) 772 print_timer(timernames[t->type], t->val); 773 break; 774 case IMSG_CTL_END: 775 return (1); 776 break; 777 default: 778 break; 779 } 780 781 return (0); 782 } 783 784 void 785 print_neighbor_capa_mp(struct peer *p) 786 { 787 int comma; 788 u_int8_t i; 789 790 for (i = 0, comma = 0; i < AID_MAX; i++) 791 if (p->capa.peer.mp[i]) { 792 printf("%s%s", comma ? ", " : "", aid2str(i)); 793 comma = 1; 794 } 795 } 796 797 void 798 print_neighbor_capa_restart(struct peer *p) 799 { 800 int comma; 801 u_int8_t i; 802 803 if (p->capa.peer.grestart.timeout) 804 printf(": Timeout: %d, ", p->capa.peer.grestart.timeout); 805 for (i = 0, comma = 0; i < AID_MAX; i++) 806 if (p->capa.peer.grestart.flags[i] & CAPA_GR_PRESENT) { 807 if (!comma && 808 p->capa.peer.grestart.flags[i] & CAPA_GR_RESTART) 809 printf("restarted, "); 810 if (comma) 811 printf(", "); 812 printf("%s", aid2str(i)); 813 if (p->capa.peer.grestart.flags[i] & CAPA_GR_FORWARD) 814 printf(" (preserved)"); 815 comma = 1; 816 } 817 } 818 819 void 820 print_neighbor_msgstats(struct peer *p) 821 { 822 printf(" Message statistics:\n"); 823 printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); 824 printf(" %-15s %10llu %10llu\n", "Opens", 825 p->stats.msg_sent_open, p->stats.msg_rcvd_open); 826 printf(" %-15s %10llu %10llu\n", "Notifications", 827 p->stats.msg_sent_notification, p->stats.msg_rcvd_notification); 828 printf(" %-15s %10llu %10llu\n", "Updates", 829 p->stats.msg_sent_update, p->stats.msg_rcvd_update); 830 printf(" %-15s %10llu %10llu\n", "Keepalives", 831 p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive); 832 printf(" %-15s %10llu %10llu\n", "Route Refresh", 833 p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh); 834 printf(" %-15s %10llu %10llu\n\n", "Total", 835 p->stats.msg_sent_open + p->stats.msg_sent_notification + 836 p->stats.msg_sent_update + p->stats.msg_sent_keepalive + 837 p->stats.msg_sent_rrefresh, 838 p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + 839 p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + 840 p->stats.msg_rcvd_rrefresh); 841 printf(" Update statistics:\n"); 842 printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); 843 printf(" %-15s %10llu %10llu\n", "Updates", 844 p->stats.prefix_sent_update, p->stats.prefix_rcvd_update); 845 printf(" %-15s %10llu %10llu\n", "Withdraws", 846 p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw); 847 printf(" %-15s %10llu %10llu\n", "End-of-Rib", 848 p->stats.prefix_sent_eor, p->stats.prefix_rcvd_eor); 849 } 850 851 void 852 print_timer(const char *name, time_t d) 853 { 854 printf(" %-20s ", name); 855 856 if (d <= 0) 857 printf("%-20s\n", "due"); 858 else 859 printf("due in %-13s\n", fmt_timeframe_core(d)); 860 } 861 862 #define TF_BUFS 8 863 #define TF_LEN 9 864 865 static char * 866 fmt_timeframe(time_t t) 867 { 868 if (t == 0) 869 return ("Never"); 870 else 871 return (fmt_timeframe_core(time(NULL) - t)); 872 } 873 874 static char * 875 fmt_timeframe_core(time_t t) 876 { 877 char *buf; 878 static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ 879 static int idx = 0; 880 unsigned int sec, min, hrs, day; 881 unsigned long long week; 882 883 buf = tfbuf[idx++]; 884 if (idx == TF_BUFS) 885 idx = 0; 886 887 week = t; 888 889 sec = week % 60; 890 week /= 60; 891 min = week % 60; 892 week /= 60; 893 hrs = week % 24; 894 week /= 24; 895 day = week % 7; 896 week /= 7; 897 898 if (week > 0) 899 snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); 900 else if (day > 0) 901 snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); 902 else 903 snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); 904 905 return (buf); 906 } 907 908 void 909 show_fib_head(void) 910 { 911 printf("flags: " 912 "* = valid, B = BGP, C = Connected, S = Static, D = Dynamic\n"); 913 printf(" " 914 "N = BGP Nexthop reachable via this route R = redistributed\n"); 915 printf(" r = reject route, b = blackhole route\n\n"); 916 printf("flags prio destination gateway\n"); 917 } 918 919 void 920 show_fib_tables_head(void) 921 { 922 printf("%-5s %-20s %-8s\n", "Table", "Description", "State"); 923 } 924 925 void 926 show_network_head(void) 927 { 928 printf("flags: S = Static\n"); 929 printf("flags destination\n"); 930 } 931 932 void 933 show_fib_flags(u_int16_t flags) 934 { 935 if (flags & F_DOWN) 936 printf(" "); 937 else 938 printf("*"); 939 940 if (flags & F_BGPD_INSERTED) 941 printf("B"); 942 else if (flags & F_CONNECTED) 943 printf("C"); 944 else if (flags & F_STATIC) 945 printf("S"); 946 else if (flags & F_DYNAMIC) 947 printf("D"); 948 else 949 printf(" "); 950 951 if (flags & F_NEXTHOP) 952 printf("N"); 953 else 954 printf(" "); 955 956 if (flags & F_REDISTRIBUTED) 957 printf("R"); 958 else 959 printf(" "); 960 961 if (flags & F_REJECT && flags & F_BLACKHOLE) 962 printf("f"); 963 else if (flags & F_REJECT) 964 printf("r"); 965 else if (flags & F_BLACKHOLE) 966 printf("b"); 967 else 968 printf(" "); 969 970 printf(" "); 971 } 972 973 int 974 show_fib_msg(struct imsg *imsg) 975 { 976 struct kroute_full *kf; 977 struct ktable *kt; 978 char *p; 979 980 switch (imsg->hdr.type) { 981 case IMSG_CTL_KROUTE: 982 case IMSG_CTL_SHOW_NETWORK: 983 if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf)) 984 errx(1, "wrong imsg len"); 985 kf = imsg->data; 986 987 show_fib_flags(kf->flags); 988 989 if (asprintf(&p, "%s/%u", log_addr(&kf->prefix), 990 kf->prefixlen) == -1) 991 err(1, NULL); 992 printf("%4i %-20s ", kf->priority, p); 993 free(p); 994 995 if (kf->flags & F_CONNECTED) 996 printf("link#%u", kf->ifindex); 997 else 998 printf("%s", log_addr(&kf->nexthop)); 999 printf("\n"); 1000 1001 break; 1002 case IMSG_CTL_SHOW_FIB_TABLES: 1003 if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt)) 1004 errx(1, "wrong imsg len"); 1005 kt = imsg->data; 1006 1007 printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr, 1008 kt->fib_sync ? "coupled" : "decoupled", 1009 kt->fib_sync != kt->fib_conf ? "*" : ""); 1010 1011 break; 1012 case IMSG_CTL_END: 1013 return (1); 1014 default: 1015 break; 1016 } 1017 1018 return (0); 1019 } 1020 1021 void 1022 show_nexthop_head(void) 1023 { 1024 printf("Flags: * = nexthop valid\n"); 1025 printf("\n %-15s %-19s%-4s %-15s %-20s\n", "Nexthop", "Route", 1026 "Prio", "Gateway", "Iface"); 1027 } 1028 1029 int 1030 show_nexthop_msg(struct imsg *imsg) 1031 { 1032 struct ctl_show_nexthop *p; 1033 struct kroute *k; 1034 struct kroute6 *k6; 1035 char *s; 1036 1037 switch (imsg->hdr.type) { 1038 case IMSG_CTL_SHOW_NEXTHOP: 1039 p = imsg->data; 1040 printf("%s %-15s ", p->valid ? "*" : " ", log_addr(&p->addr)); 1041 if (!p->krvalid) { 1042 printf("\n"); 1043 return (0); 1044 } 1045 switch (p->addr.aid) { 1046 case AID_INET: 1047 k = &p->kr.kr4; 1048 if (asprintf(&s, "%s/%u", inet_ntoa(k->prefix), 1049 k->prefixlen) == -1) 1050 err(1, NULL); 1051 printf("%-20s", s); 1052 free(s); 1053 printf("%3i %-15s ", k->priority, 1054 k->flags & F_CONNECTED ? "connected" : 1055 inet_ntoa(k->nexthop)); 1056 break; 1057 case AID_INET6: 1058 k6 = &p->kr.kr6; 1059 if (asprintf(&s, "%s/%u", log_in6addr(&k6->prefix), 1060 k6->prefixlen) == -1) 1061 err(1, NULL); 1062 printf("%-20s", s); 1063 free(s); 1064 printf("%3i %-15s ", k6->priority, 1065 k6->flags & F_CONNECTED ? "connected" : 1066 log_in6addr(&k6->nexthop)); 1067 break; 1068 default: 1069 printf("unknown address family\n"); 1070 return (0); 1071 } 1072 if (p->kif.ifname[0]) { 1073 char *s1; 1074 if (p->kif.baudrate) { 1075 if (asprintf(&s1, ", %s", 1076 get_baudrate(p->kif.baudrate, 1077 "bps")) == -1) 1078 err(1, NULL); 1079 } else if (asprintf(&s1, ", %s", get_linkstate( 1080 p->kif.if_type, p->kif.link_state)) == -1) 1081 err(1, NULL); 1082 if (asprintf(&s, "%s (%s%s)", p->kif.ifname, 1083 p->kif.flags & IFF_UP ? "UP" : "DOWN", s1) == -1) 1084 err(1, NULL); 1085 printf("%-15s", s); 1086 free(s1); 1087 free(s); 1088 } 1089 printf("\n"); 1090 break; 1091 case IMSG_CTL_END: 1092 return (1); 1093 break; 1094 default: 1095 break; 1096 } 1097 1098 return (0); 1099 } 1100 1101 1102 void 1103 show_interface_head(void) 1104 { 1105 printf("%-15s%-15s%-15s%s\n", "Interface", "Nexthop state", "Flags", 1106 "Link state"); 1107 } 1108 1109 const struct if_status_description 1110 if_status_descriptions[] = LINK_STATE_DESCRIPTIONS; 1111 const struct ifmedia_description 1112 ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS; 1113 1114 uint64_t 1115 ift2ifm(uint8_t if_type) 1116 { 1117 switch (if_type) { 1118 case IFT_ETHER: 1119 return (IFM_ETHER); 1120 case IFT_FDDI: 1121 return (IFM_FDDI); 1122 case IFT_CARP: 1123 return (IFM_CARP); 1124 case IFT_IEEE80211: 1125 return (IFM_IEEE80211); 1126 default: 1127 return (0); 1128 } 1129 } 1130 1131 const char * 1132 get_media_descr(uint64_t media_type) 1133 { 1134 const struct ifmedia_description *p; 1135 1136 for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++) 1137 if (media_type == p->ifmt_word) 1138 return (p->ifmt_string); 1139 1140 return ("unknown media"); 1141 } 1142 1143 const char * 1144 get_linkstate(uint8_t if_type, int link_state) 1145 { 1146 const struct if_status_description *p; 1147 static char buf[8]; 1148 1149 for (p = if_status_descriptions; p->ifs_string != NULL; p++) { 1150 if (LINK_STATE_DESC_MATCH(p, if_type, link_state)) 1151 return (p->ifs_string); 1152 } 1153 snprintf(buf, sizeof(buf), "[#%d]", link_state); 1154 return (buf); 1155 } 1156 1157 const char * 1158 get_baudrate(u_int64_t baudrate, char *unit) 1159 { 1160 static char bbuf[16]; 1161 1162 if (baudrate > IF_Gbps(1)) 1163 snprintf(bbuf, sizeof(bbuf), "%llu G%s", 1164 baudrate / IF_Gbps(1), unit); 1165 else if (baudrate > IF_Mbps(1)) 1166 snprintf(bbuf, sizeof(bbuf), "%llu M%s", 1167 baudrate / IF_Mbps(1), unit); 1168 else if (baudrate > IF_Kbps(1)) 1169 snprintf(bbuf, sizeof(bbuf), "%llu K%s", 1170 baudrate / IF_Kbps(1), unit); 1171 else 1172 snprintf(bbuf, sizeof(bbuf), "%llu %s", 1173 baudrate, unit); 1174 1175 return (bbuf); 1176 } 1177 1178 int 1179 show_interface_msg(struct imsg *imsg) 1180 { 1181 struct kif *k; 1182 uint64_t ifms_type; 1183 1184 switch (imsg->hdr.type) { 1185 case IMSG_CTL_SHOW_INTERFACE: 1186 k = imsg->data; 1187 printf("%-15s", k->ifname); 1188 printf("%-15s", k->nh_reachable ? "ok" : "invalid"); 1189 printf("%-15s", k->flags & IFF_UP ? "UP" : ""); 1190 1191 if ((ifms_type = ift2ifm(k->if_type)) != 0) 1192 printf("%s, ", get_media_descr(ifms_type)); 1193 1194 printf("%s", get_linkstate(k->if_type, k->link_state)); 1195 1196 if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0) 1197 printf(", %s", get_baudrate(k->baudrate, "Bit/s")); 1198 printf("\n"); 1199 break; 1200 case IMSG_CTL_END: 1201 return (1); 1202 break; 1203 default: 1204 break; 1205 } 1206 1207 return (0); 1208 } 1209 1210 void 1211 show_rib_summary_head(void) 1212 { 1213 printf("flags: * = Valid, > = Selected, I = via IBGP, A = Announced, " 1214 "S = Stale\n"); 1215 printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n"); 1216 printf("%-5s %-20s %-15s %5s %5s %s\n", "flags", "destination", 1217 "gateway", "lpref", "med", "aspath origin"); 1218 } 1219 1220 void 1221 print_prefix(struct bgpd_addr *prefix, u_int8_t prefixlen, u_int8_t flags) 1222 { 1223 char *p; 1224 1225 print_flags(flags, 1); 1226 if (asprintf(&p, "%s/%u", log_addr(prefix), prefixlen) == -1) 1227 err(1, NULL); 1228 printf("%-20s", p); 1229 free(p); 1230 } 1231 1232 const char * 1233 print_origin(u_int8_t origin, int sum) 1234 { 1235 switch (origin) { 1236 case ORIGIN_IGP: 1237 return (sum ? "i" : "IGP"); 1238 case ORIGIN_EGP: 1239 return (sum ? "e" : "EGP"); 1240 case ORIGIN_INCOMPLETE: 1241 return (sum ? "?" : "incomplete"); 1242 default: 1243 return (sum ? "X" : "bad origin"); 1244 } 1245 } 1246 1247 void 1248 print_flags(u_int8_t flags, int sum) 1249 { 1250 char flagstr[5]; 1251 char *p = flagstr; 1252 1253 if (sum) { 1254 if (flags & F_PREF_ANNOUNCE) 1255 *p++ = 'A'; 1256 if (flags & F_PREF_INTERNAL) 1257 *p++ = 'I'; 1258 if (flags & F_PREF_STALE) 1259 *p++ = 'S'; 1260 if (flags & F_PREF_ELIGIBLE) 1261 *p++ = '*'; 1262 if (flags & F_PREF_ACTIVE) 1263 *p++ = '>'; 1264 *p = '\0'; 1265 printf("%-5s ", flagstr); 1266 } else { 1267 if (flags & F_PREF_INTERNAL) 1268 printf("internal"); 1269 else 1270 printf("external"); 1271 if (flags & F_PREF_STALE) 1272 printf(", stale"); 1273 if (flags & F_PREF_ELIGIBLE) 1274 printf(", valid"); 1275 if (flags & F_PREF_ACTIVE) 1276 printf(", best"); 1277 if (flags & F_PREF_ANNOUNCE) 1278 printf(", announced"); 1279 } 1280 } 1281 1282 int 1283 show_rib_summary_msg(struct imsg *imsg) 1284 { 1285 struct ctl_show_rib rib; 1286 u_char *asdata; 1287 1288 switch (imsg->hdr.type) { 1289 case IMSG_CTL_SHOW_RIB: 1290 memcpy(&rib, imsg->data, sizeof(rib)); 1291 asdata = imsg->data; 1292 asdata += sizeof(struct ctl_show_rib); 1293 show_rib_brief(&rib, asdata); 1294 break; 1295 case IMSG_CTL_END: 1296 return (1); 1297 default: 1298 break; 1299 } 1300 1301 return (0); 1302 } 1303 1304 int 1305 show_rib_detail_msg(struct imsg *imsg, int nodescr) 1306 { 1307 struct ctl_show_rib rib; 1308 u_char *asdata; 1309 u_int16_t ilen; 1310 1311 switch (imsg->hdr.type) { 1312 case IMSG_CTL_SHOW_RIB: 1313 memcpy(&rib, imsg->data, sizeof(rib)); 1314 asdata = imsg->data; 1315 asdata += sizeof(struct ctl_show_rib); 1316 show_rib_detail(&rib, asdata, nodescr); 1317 break; 1318 case IMSG_CTL_SHOW_RIB_ATTR: 1319 ilen = imsg->hdr.len - IMSG_HEADER_SIZE; 1320 if (ilen < 3) 1321 errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received"); 1322 show_attr(imsg->data, ilen); 1323 break; 1324 case IMSG_CTL_END: 1325 printf("\n"); 1326 return (1); 1327 default: 1328 break; 1329 } 1330 1331 return (0); 1332 } 1333 1334 void 1335 show_rib_brief(struct ctl_show_rib *r, u_char *asdata) 1336 { 1337 char *aspath; 1338 1339 print_prefix(&r->prefix, r->prefixlen, r->flags); 1340 printf(" %-15s ", log_addr(&r->exit_nexthop)); 1341 printf(" %5u %5u ", r->local_pref, r->med); 1342 1343 if (aspath_asprint(&aspath, asdata, r->aspath_len) == -1) 1344 err(1, NULL); 1345 if (strlen(aspath) > 0) 1346 printf("%s ", aspath); 1347 free(aspath); 1348 1349 printf("%s\n", print_origin(r->origin, 1)); 1350 } 1351 1352 void 1353 show_rib_detail(struct ctl_show_rib *r, u_char *asdata, int nodescr) 1354 { 1355 struct in_addr id; 1356 char *aspath, *s; 1357 time_t now; 1358 1359 printf("\nBGP routing table entry for %s/%u\n", 1360 log_addr(&r->prefix), r->prefixlen); 1361 1362 if (aspath_asprint(&aspath, asdata, r->aspath_len) == -1) 1363 err(1, NULL); 1364 if (strlen(aspath) > 0) 1365 printf(" %s\n", aspath); 1366 free(aspath); 1367 1368 s = fmt_peer(r->descr, &r->remote_addr, -1, nodescr); 1369 printf(" Nexthop %s ", log_addr(&r->exit_nexthop)); 1370 printf("(via %s) from %s (", log_addr(&r->true_nexthop), s); 1371 free(s); 1372 id.s_addr = htonl(r->remote_id); 1373 printf("%s)\n", inet_ntoa(id)); 1374 1375 printf(" Origin %s, metric %u, localpref %u, weight %u, ", 1376 print_origin(r->origin, 0), r->med, r->local_pref, r->weight); 1377 print_flags(r->flags, 0); 1378 1379 now = time(NULL); 1380 if (now > r->lastchange) 1381 now -= r->lastchange; 1382 else 1383 now = 0; 1384 1385 printf("\n Last update: %s ago\n", fmt_timeframe_core(now)); 1386 } 1387 1388 void 1389 show_attr(void *b, u_int16_t len) 1390 { 1391 char *data = b; 1392 struct in_addr id; 1393 u_int32_t as; 1394 u_int16_t alen, ioff; 1395 u_int8_t flags, type; 1396 1397 if (len < 3) 1398 errx(1, "show_attr: too short bgp attr"); 1399 1400 flags = data[0]; 1401 type = data[1]; 1402 1403 /* get the attribute length */ 1404 if (flags & ATTR_EXTLEN) { 1405 if (len < 4) 1406 errx(1, "show_attr: too short bgp attr"); 1407 memcpy(&alen, data+2, sizeof(u_int16_t)); 1408 alen = ntohs(alen); 1409 data += 4; 1410 len -= 4; 1411 } else { 1412 alen = (u_char)data[2]; 1413 data += 3; 1414 len -= 3; 1415 } 1416 1417 /* bad imsg len how can that happen!? */ 1418 if (alen > len) 1419 errx(1, "show_attr: bad length"); 1420 1421 switch (type) { 1422 case ATTR_COMMUNITIES: 1423 printf(" Communities: "); 1424 show_community(data, alen); 1425 printf("\n"); 1426 break; 1427 case ATTR_AGGREGATOR: 1428 memcpy(&as, data, sizeof(as)); 1429 memcpy(&id, data + sizeof(as), sizeof(id)); 1430 printf(" Aggregator: %s [%s]\n", 1431 log_as(ntohl(as)), inet_ntoa(id)); 1432 break; 1433 case ATTR_ORIGINATOR_ID: 1434 memcpy(&id, data, sizeof(id)); 1435 printf(" Originator Id: %s\n", inet_ntoa(id)); 1436 break; 1437 case ATTR_CLUSTER_LIST: 1438 printf(" Cluster ID List:"); 1439 for (ioff = 0; ioff + sizeof(id) <= alen; 1440 ioff += sizeof(id)) { 1441 memcpy(&id, data + ioff, sizeof(id)); 1442 printf(" %s", inet_ntoa(id)); 1443 } 1444 printf("\n"); 1445 break; 1446 case ATTR_EXT_COMMUNITIES: 1447 printf(" Ext. communities: "); 1448 show_ext_community(data, alen); 1449 printf("\n"); 1450 break; 1451 default: 1452 /* ignore unknown attributes */ 1453 break; 1454 } 1455 } 1456 1457 void 1458 show_community(u_char *data, u_int16_t len) 1459 { 1460 u_int16_t a, v; 1461 u_int16_t i; 1462 1463 if (len & 0x3) 1464 return; 1465 1466 for (i = 0; i < len; i += 4) { 1467 memcpy(&a, data + i, sizeof(a)); 1468 memcpy(&v, data + i + 2, sizeof(v)); 1469 a = ntohs(a); 1470 v = ntohs(v); 1471 if (a == COMMUNITY_WELLKNOWN) 1472 switch (v) { 1473 case COMMUNITY_NO_EXPORT: 1474 printf("NO_EXPORT"); 1475 break; 1476 case COMMUNITY_NO_ADVERTISE: 1477 printf("NO_ADVERTISE"); 1478 break; 1479 case COMMUNITY_NO_EXPSUBCONFED: 1480 printf("NO_EXPORT_SUBCONFED"); 1481 break; 1482 case COMMUNITY_NO_PEER: 1483 printf("NO_PEER"); 1484 break; 1485 case COMMUNITY_BLACKHOLE: 1486 printf("BLACKHOLE"); 1487 break; 1488 default: 1489 printf("WELLKNOWN:%hu", v); 1490 break; 1491 } 1492 else 1493 printf("%hu:%hu", a, v); 1494 1495 if (i + 4 < len) 1496 printf(" "); 1497 } 1498 } 1499 1500 void 1501 show_ext_community(u_char *data, u_int16_t len) 1502 { 1503 u_int64_t ext; 1504 struct in_addr ip; 1505 u_int32_t as4, u32; 1506 u_int16_t i, as2, u16; 1507 u_int8_t type, subtype; 1508 1509 if (len & 0x7) 1510 return; 1511 1512 for (i = 0; i < len; i += 8) { 1513 type = data[i]; 1514 subtype = data[i + 1]; 1515 1516 switch (type & EXT_COMMUNITY_VALUE) { 1517 case EXT_COMMUNITY_TWO_AS: 1518 memcpy(&as2, data + i + 2, sizeof(as2)); 1519 memcpy(&u32, data + i + 4, sizeof(u32)); 1520 printf("%s %s:%u", log_ext_subtype(subtype), 1521 log_as(ntohs(as2)), ntohl(u32)); 1522 break; 1523 case EXT_COMMUNITY_IPV4: 1524 memcpy(&ip, data + i + 2, sizeof(ip)); 1525 memcpy(&u16, data + i + 6, sizeof(u16)); 1526 printf("%s %s:%hu", log_ext_subtype(subtype), 1527 inet_ntoa(ip), ntohs(u16)); 1528 break; 1529 case EXT_COMMUNITY_FOUR_AS: 1530 memcpy(&as4, data + i + 2, sizeof(as4)); 1531 memcpy(&u16, data + i + 6, sizeof(u16)); 1532 printf("%s %s:%hu", log_ext_subtype(subtype), 1533 log_as(ntohl(as4)), ntohs(u16)); 1534 break; 1535 case EXT_COMMUNITY_OPAQUE: 1536 memcpy(&ext, data + i, sizeof(ext)); 1537 ext = betoh64(ext) & 0xffffffffffffLL; 1538 printf("%s 0x%llx", log_ext_subtype(subtype), ext); 1539 break; 1540 default: 1541 memcpy(&ext, data + i, sizeof(ext)); 1542 printf("0x%llx", betoh64(ext)); 1543 } 1544 if (i + 8 < len) 1545 printf(", "); 1546 } 1547 } 1548 1549 char * 1550 fmt_mem(int64_t num) 1551 { 1552 static char buf[16]; 1553 1554 if (fmt_scaled(num, buf) == -1) 1555 snprintf(buf, sizeof(buf), "%lldB", (long long)num); 1556 1557 return (buf); 1558 } 1559 1560 size_t pt_sizes[AID_MAX] = AID_PTSIZE; 1561 1562 int 1563 show_rib_memory_msg(struct imsg *imsg) 1564 { 1565 struct rde_memstats stats; 1566 size_t pts = 0; 1567 int i; 1568 1569 switch (imsg->hdr.type) { 1570 case IMSG_CTL_SHOW_RIB_MEM: 1571 memcpy(&stats, imsg->data, sizeof(stats)); 1572 printf("RDE memory statistics\n"); 1573 for (i = 0; i < AID_MAX; i++) { 1574 if (stats.pt_cnt[i] == 0) 1575 continue; 1576 pts += stats.pt_cnt[i] * pt_sizes[i]; 1577 printf("%10lld %s network entries using %s of memory\n", 1578 (long long)stats.pt_cnt[i], aid_vals[i].name, 1579 fmt_mem(stats.pt_cnt[i] * pt_sizes[i])); 1580 } 1581 printf("%10lld rib entries using %s of memory\n", 1582 (long long)stats.rib_cnt, fmt_mem(stats.rib_cnt * 1583 sizeof(struct rib_entry))); 1584 printf("%10lld prefix entries using %s of memory\n", 1585 (long long)stats.prefix_cnt, fmt_mem(stats.prefix_cnt * 1586 sizeof(struct prefix))); 1587 printf("%10lld BGP path attribute entries using %s of memory\n", 1588 (long long)stats.path_cnt, fmt_mem(stats.path_cnt * 1589 sizeof(struct rde_aspath))); 1590 printf("%10lld BGP AS-PATH attribute entries using " 1591 "%s of memory,\n\t and holding %lld references\n", 1592 (long long)stats.aspath_cnt, fmt_mem(stats.aspath_size), 1593 (long long)stats.aspath_refs); 1594 printf("%10lld BGP attributes entries using %s of memory\n", 1595 (long long)stats.attr_cnt, fmt_mem(stats.attr_cnt * 1596 sizeof(struct attr))); 1597 printf("\t and holding %lld references\n", 1598 (long long)stats.attr_refs); 1599 printf("%10lld BGP attributes using %s of memory\n", 1600 (long long)stats.attr_dcnt, fmt_mem(stats.attr_data)); 1601 printf("RIB using %s of memory\n", fmt_mem(pts + 1602 stats.prefix_cnt * sizeof(struct prefix) + 1603 stats.rib_cnt * sizeof(struct rib_entry) + 1604 stats.path_cnt * sizeof(struct rde_aspath) + 1605 stats.aspath_size + stats.attr_cnt * sizeof(struct attr) + 1606 stats.attr_data)); 1607 break; 1608 default: 1609 break; 1610 } 1611 1612 return (1); 1613 } 1614 1615 void 1616 send_filterset(struct imsgbuf *i, struct filter_set_head *set) 1617 { 1618 struct filter_set *s; 1619 1620 while ((s = TAILQ_FIRST(set)) != NULL) { 1621 imsg_compose(i, IMSG_FILTER_SET, 0, 0, -1, s, 1622 sizeof(struct filter_set)); 1623 TAILQ_REMOVE(set, s, entry); 1624 free(s); 1625 } 1626 } 1627 1628 const char * 1629 get_errstr(u_int8_t errcode, u_int8_t subcode) 1630 { 1631 static const char *errstr = NULL; 1632 1633 if (errcode && errcode < sizeof(errnames)/sizeof(char *)) 1634 errstr = errnames[errcode]; 1635 1636 switch (errcode) { 1637 case ERR_HEADER: 1638 if (subcode && 1639 subcode < sizeof(suberr_header_names)/sizeof(char *)) 1640 errstr = suberr_header_names[subcode]; 1641 break; 1642 case ERR_OPEN: 1643 if (subcode && 1644 subcode < sizeof(suberr_open_names)/sizeof(char *)) 1645 errstr = suberr_open_names[subcode]; 1646 break; 1647 case ERR_UPDATE: 1648 if (subcode && 1649 subcode < sizeof(suberr_update_names)/sizeof(char *)) 1650 errstr = suberr_update_names[subcode]; 1651 break; 1652 case ERR_HOLDTIMEREXPIRED: 1653 case ERR_FSM: 1654 case ERR_CEASE: 1655 break; 1656 default: 1657 return ("unknown error code"); 1658 } 1659 1660 return (errstr); 1661 } 1662 1663 int 1664 show_result(struct imsg *imsg) 1665 { 1666 u_int rescode; 1667 1668 if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(rescode)) 1669 errx(1, "got IMSG_CTL_RESULT with wrong len"); 1670 memcpy(&rescode, imsg->data, sizeof(rescode)); 1671 1672 if (rescode == 0) 1673 printf("request processed\n"); 1674 else { 1675 if (rescode > 1676 sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) 1677 printf("unknown result error code %u\n", rescode); 1678 else 1679 printf("%s\n", ctl_res_strerror[rescode]); 1680 } 1681 1682 return (1); 1683 } 1684 1685 void 1686 network_bulk(struct parse_result *res) 1687 { 1688 struct network_config net; 1689 struct filter_set *s = NULL; 1690 struct bgpd_addr h; 1691 char *b, *buf, *lbuf; 1692 size_t slen; 1693 u_int8_t len; 1694 FILE *f; 1695 1696 if ((f = fdopen(STDIN_FILENO, "r")) != NULL) { 1697 while ((buf = fgetln(f, &slen))) { 1698 lbuf = NULL; 1699 if (buf[slen - 1] == '\n') 1700 buf[slen - 1] = '\0'; 1701 else { 1702 if ((lbuf = malloc(slen + 1)) == NULL) 1703 err(1, NULL); 1704 memcpy(lbuf, buf, slen); 1705 lbuf[slen] = '\0'; 1706 buf = lbuf; 1707 } 1708 1709 while ((b = strsep(&buf, " \t")) != NULL) { 1710 /* Don't process commented entries */ 1711 if (strchr(b, '#') != NULL) 1712 break; 1713 bzero(&net, sizeof(net)); 1714 parse_prefix(b, strlen(b), &h, &len); 1715 memcpy(&net.prefix, &h, sizeof(h)); 1716 net.prefixlen = len; 1717 1718 if (res->action == NETWORK_BULK_ADD) { 1719 imsg_compose(ibuf, IMSG_NETWORK_ADD, 1720 0, 0, -1, &net, sizeof(net)); 1721 TAILQ_FOREACH(s, &res->set, entry) { 1722 imsg_compose(ibuf, 1723 IMSG_FILTER_SET, 1724 0, 0, -1, s, sizeof(*s)); 1725 } 1726 imsg_compose(ibuf, IMSG_NETWORK_DONE, 1727 0, 0, -1, NULL, 0); 1728 } else 1729 imsg_compose(ibuf, IMSG_NETWORK_REMOVE, 1730 0, 0, -1, &net, sizeof(net)); 1731 } 1732 free(lbuf); 1733 } 1734 fclose(f); 1735 } else { 1736 err(1, "Failed to open stdin\n"); 1737 } 1738 } 1739 1740 void 1741 show_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg) 1742 { 1743 struct ctl_show_rib ctl; 1744 struct ctl_show_rib_request *req = arg; 1745 struct mrt_rib_entry *mre; 1746 u_int16_t i, j; 1747 1748 for (i = 0; i < mr->nentries; i++) { 1749 mre = &mr->entries[i]; 1750 bzero(&ctl, sizeof(ctl)); 1751 mrt_to_bgpd_addr(&mr->prefix, &ctl.prefix); 1752 ctl.prefixlen = mr->prefixlen; 1753 ctl.lastchange = mre->originated; 1754 mrt_to_bgpd_addr(&mre->nexthop, &ctl.true_nexthop); 1755 mrt_to_bgpd_addr(&mre->nexthop, &ctl.exit_nexthop); 1756 ctl.origin = mre->origin; 1757 ctl.local_pref = mre->local_pref; 1758 ctl.med = mre->med; 1759 /* weight is not part of the mrt dump so it can't be set */ 1760 ctl.aspath_len = mre->aspath_len; 1761 1762 if (mre->peer_idx < mp->npeers) { 1763 mrt_to_bgpd_addr(&mp->peers[mre->peer_idx].addr, 1764 &ctl.remote_addr); 1765 ctl.remote_id = mp->peers[mre->peer_idx].bgp_id; 1766 } 1767 1768 /* filter by neighbor */ 1769 if (req->neighbor.addr.aid != AID_UNSPEC && 1770 memcmp(&req->neighbor.addr, &ctl.remote_addr, 1771 sizeof(ctl.remote_addr)) != 0) 1772 continue; 1773 /* filter by AF */ 1774 if (req->aid && req->aid != ctl.prefix.aid) 1775 return; 1776 /* filter by prefix */ 1777 if (req->prefix.aid != AID_UNSPEC) { 1778 if (!prefix_compare(&req->prefix, &ctl.prefix, 1779 req->prefixlen)) { 1780 if (req->flags & F_LONGER) { 1781 if (req->prefixlen > ctl.prefixlen) 1782 return; 1783 } else if (req->prefixlen != ctl.prefixlen) 1784 return; 1785 } else 1786 return; 1787 } 1788 /* filter by AS */ 1789 if (req->as.type != AS_NONE && 1790 !aspath_match(mre->aspath, mre->aspath_len, 1791 req->as.type, req->as.as)) 1792 continue; 1793 1794 if (req->flags & F_CTL_DETAIL) { 1795 show_rib_detail(&ctl, mre->aspath, 1); 1796 for (j = 0; j < mre->nattrs; j++) 1797 show_attr(mre->attrs[j].attr, 1798 mre->attrs[j].attr_len); 1799 } else 1800 show_rib_brief(&ctl, mre->aspath); 1801 } 1802 } 1803 1804 void 1805 network_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg) 1806 { 1807 struct ctl_show_rib ctl; 1808 struct network_config net; 1809 struct ctl_show_rib_request *req = arg; 1810 struct mrt_rib_entry *mre; 1811 struct ibuf *msg; 1812 u_int16_t i, j; 1813 1814 for (i = 0; i < mr->nentries; i++) { 1815 mre = &mr->entries[i]; 1816 bzero(&ctl, sizeof(ctl)); 1817 mrt_to_bgpd_addr(&mr->prefix, &ctl.prefix); 1818 ctl.prefixlen = mr->prefixlen; 1819 ctl.lastchange = mre->originated; 1820 mrt_to_bgpd_addr(&mre->nexthop, &ctl.true_nexthop); 1821 mrt_to_bgpd_addr(&mre->nexthop, &ctl.exit_nexthop); 1822 ctl.origin = mre->origin; 1823 ctl.local_pref = mre->local_pref; 1824 ctl.med = mre->med; 1825 ctl.aspath_len = mre->aspath_len; 1826 1827 if (mre->peer_idx < mp->npeers) { 1828 mrt_to_bgpd_addr(&mp->peers[mre->peer_idx].addr, 1829 &ctl.remote_addr); 1830 ctl.remote_id = mp->peers[mre->peer_idx].bgp_id; 1831 } 1832 1833 /* filter by neighbor */ 1834 if (req->neighbor.addr.aid != AID_UNSPEC && 1835 memcmp(&req->neighbor.addr, &ctl.remote_addr, 1836 sizeof(ctl.remote_addr)) != 0) 1837 continue; 1838 /* filter by AF */ 1839 if (req->aid && req->aid != ctl.prefix.aid) 1840 return; 1841 /* filter by prefix */ 1842 if (req->prefix.aid != AID_UNSPEC) { 1843 if (!prefix_compare(&req->prefix, &ctl.prefix, 1844 req->prefixlen)) { 1845 if (req->flags & F_LONGER) { 1846 if (req->prefixlen > ctl.prefixlen) 1847 return; 1848 } else if (req->prefixlen != ctl.prefixlen) 1849 return; 1850 } else 1851 return; 1852 } 1853 /* filter by AS */ 1854 if (req->as.type != AS_NONE && 1855 !aspath_match(mre->aspath, mre->aspath_len, 1856 req->as.type, req->as.as)) 1857 continue; 1858 1859 bzero(&net, sizeof(net)); 1860 memcpy(&net.prefix, &ctl.prefix, sizeof(net.prefix)); 1861 net.prefixlen = ctl.prefixlen; 1862 net.type = NETWORK_MRTCLONE; 1863 /* XXX rtableid */ 1864 1865 imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1, 1866 &net, sizeof(net)); 1867 if ((msg = imsg_create(ibuf, IMSG_NETWORK_ASPATH, 1868 0, 0, sizeof(ctl) + mre->aspath_len)) == NULL) 1869 errx(1, "imsg_create failure"); 1870 if (imsg_add(msg, &ctl, sizeof(ctl)) == -1 || 1871 imsg_add(msg, mre->aspath, mre->aspath_len) == -1) 1872 errx(1, "imsg_add failure"); 1873 imsg_close(ibuf, msg); 1874 for (j = 0; j < mre->nattrs; j++) 1875 imsg_compose(ibuf, IMSG_NETWORK_ATTR, 0, 0, -1, 1876 mre->attrs[j].attr, mre->attrs[j].attr_len); 1877 imsg_compose(ibuf, IMSG_NETWORK_DONE, 0, 0, -1, NULL, 0); 1878 1879 while (ibuf->w.queued) { 1880 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 1881 err(1, "write error"); 1882 } 1883 } 1884 } 1885 1886 void 1887 show_mrt_state(struct mrt_bgp_state *ms, void *arg) 1888 { 1889 struct bgpd_addr src, dst; 1890 1891 mrt_to_bgpd_addr(&ms->src, &src); 1892 mrt_to_bgpd_addr(&ms->dst, &dst); 1893 printf("%s[%u] -> ", log_addr(&src), ms->src_as); 1894 printf("%s[%u]: %s -> %s\n", log_addr(&dst), ms->dst_as, 1895 statenames[ms->old_state], statenames[ms->new_state]); 1896 } 1897 1898 void 1899 show_mrt_msg(struct mrt_bgp_msg *mm, void *arg) 1900 { 1901 struct bgpd_addr src, dst; 1902 1903 mrt_to_bgpd_addr(&mm->src, &src); 1904 mrt_to_bgpd_addr(&mm->dst, &dst); 1905 printf("%s[%u] -> ", log_addr(&src), mm->src_as); 1906 printf("%s[%u]: size %u\n", log_addr(&dst), mm->dst_as, mm->msg_len); 1907 } 1908 1909 void 1910 mrt_to_bgpd_addr(union mrt_addr *ma, struct bgpd_addr *ba) 1911 { 1912 switch (ma->sa.sa_family) { 1913 case AF_INET: 1914 case AF_INET6: 1915 sa2addr(&ma->sa, ba); 1916 break; 1917 case AF_VPNv4: 1918 bzero(ba, sizeof(*ba)); 1919 ba->aid = AID_VPN_IPv4; 1920 ba->vpn4.rd = ma->svpn4.sv_rd; 1921 ba->vpn4.addr.s_addr = ma->svpn4.sv_addr.s_addr; 1922 memcpy(ba->vpn4.labelstack, ma->svpn4.sv_label, 1923 sizeof(ba->vpn4.labelstack)); 1924 break; 1925 } 1926 } 1927 1928 const char * 1929 msg_type(u_int8_t type) 1930 { 1931 if (type >= sizeof(msgtypenames)/sizeof(msgtypenames[0])) 1932 return "BAD"; 1933 return (msgtypenames[type]); 1934 } 1935 1936 /* following functions are necessary for the imsg framework */ 1937 void 1938 log_warnx(const char *emsg, ...) 1939 { 1940 va_list ap; 1941 1942 va_start(ap, emsg); 1943 vwarnx(emsg, ap); 1944 va_end(ap); 1945 } 1946 1947 void 1948 log_warn(const char *emsg, ...) 1949 { 1950 va_list ap; 1951 1952 va_start(ap, emsg); 1953 vwarn(emsg, ap); 1954 va_end(ap); 1955 } 1956 1957 void 1958 fatal(const char *emsg, ...) 1959 { 1960 va_list ap; 1961 1962 va_start(ap, emsg); 1963 verr(1, emsg, ap); 1964 va_end(ap); 1965 } 1966 1967 void 1968 fatalx(const char *emsg) 1969 { 1970 errx(1, "%s", emsg); 1971 } 1972