1 /* $OpenBSD: dvmrpctl.c,v 1.21 2024/11/21 13:38:14 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org> 6 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 #include <netinet/in.h> 25 #include <netinet/ip_mroute.h> 26 #include <arpa/inet.h> 27 #include <net/if_types.h> 28 29 #include <err.h> 30 #include <errno.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "igmp.h" 37 #include "dvmrp.h" 38 #include "dvmrpd.h" 39 #include "dvmrpe.h" 40 #include "parser.h" 41 #include "log.h" 42 43 __dead void usage(void); 44 int show_summary_msg(struct imsg *); 45 int show_interface_msg(struct imsg *); 46 int show_interface_detail_msg(struct imsg *); 47 int show_igmp_msg(struct imsg *); 48 const char *print_if_type(enum iface_type type); 49 const char *print_nbr_state(int); 50 const char *print_link(int); 51 const char *fmt_timeframe(time_t t); 52 const char *fmt_timeframe_core(time_t t); 53 int show_nbr_msg(struct imsg *); 54 const char *print_dvmrp_options(u_int8_t); 55 int show_nbr_detail_msg(struct imsg *); 56 int show_rib_msg(struct imsg *); 57 int show_rib_detail_msg(struct imsg *); 58 int show_mfc_msg(struct imsg *); 59 int show_mfc_detail_msg(struct imsg *); 60 const char * get_linkstate(uint8_t, int); 61 62 struct imsgbuf *ibuf; 63 64 __dead void 65 usage(void) 66 { 67 extern char *__progname; 68 69 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 70 exit(1); 71 } 72 73 int 74 main(int argc, char *argv[]) 75 { 76 struct sockaddr_un sun; 77 struct parse_result *res; 78 struct imsg imsg; 79 unsigned int ifidx = 0; 80 int ctl_sock; 81 int done = 0, verbose = 0; 82 int n; 83 84 /* parse options */ 85 if ((res = parse(argc - 1, argv + 1)) == NULL) 86 exit(1); 87 88 /* connect to dvmrpd control socket */ 89 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 90 err(1, "socket"); 91 92 bzero(&sun, sizeof(sun)); 93 sun.sun_family = AF_UNIX; 94 strlcpy(sun.sun_path, DVMRPD_SOCKET, sizeof(sun.sun_path)); 95 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 96 err(1, "connect: %s", DVMRPD_SOCKET); 97 98 if (pledge("stdio", NULL) == -1) 99 err(1, "pledge"); 100 101 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 102 fatal(NULL); 103 if (imsgbuf_init(ibuf, ctl_sock) == -1) 104 fatal(NULL); 105 done = 0; 106 107 /* process user request */ 108 switch (res->action) { 109 case NONE: 110 usage(); 111 /* NOTREACHED */ 112 case SHOW: 113 case SHOW_SUM: 114 imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0); 115 break; 116 case SHOW_IFACE: 117 printf("%-11s %-18s %-10s %-10s %-10s %-8s %s\n", 118 "Interface", "Address", "State", "ProbeTimer", "Linkstate", 119 "Uptime", "Groups"); 120 /* FALLTHROUGH */ 121 case SHOW_IFACE_DTAIL: 122 if (*res->ifname) { 123 ifidx = if_nametoindex(res->ifname); 124 if (ifidx == 0) 125 errx(1, "no such interface %s", res->ifname); 126 } 127 imsg_compose(ibuf, IMSG_CTL_SHOW_IFACE, 0, 0, -1, &ifidx, 128 sizeof(ifidx)); 129 break; 130 case SHOW_IGMP: 131 if (*res->ifname) { 132 ifidx = if_nametoindex(res->ifname); 133 if (ifidx == 0) 134 errx(1, "no such interface %s", res->ifname); 135 } 136 imsg_compose(ibuf, IMSG_CTL_SHOW_IGMP, 0, 0, -1, &ifidx, 137 sizeof(ifidx)); 138 break; 139 case SHOW_NBR: 140 printf("%-15s %-10s %-9s %-15s %-11s %-8s\n", "ID", "State", 141 "DeadTime", "Address", "Interface", "Uptime"); 142 /* FALLTHROUGH */ 143 case SHOW_NBR_DTAIL: 144 imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0); 145 break; 146 case SHOW_RIB: 147 printf("%-20s %-17s %-7s %-10s %-s\n", "Destination", "Nexthop", 148 "Cost", "Uptime", "Expire"); 149 /* FALLTHROUGH */ 150 case SHOW_RIB_DTAIL: 151 imsg_compose(ibuf, IMSG_CTL_SHOW_RIB, 0, 0, -1, NULL, 0); 152 break; 153 case SHOW_MFC: 154 printf("%-16s %-16s %-9s %-9s %-4s %-10s %-10s\n", "Group", 155 "Origin", "Incoming", "Outgoing", "TTL", "Uptime", 156 "Expire"); 157 /* FALLTHROUGH */ 158 case SHOW_MFC_DTAIL: 159 imsg_compose(ibuf, IMSG_CTL_SHOW_MFC, 0, 0, -1, NULL, 0); 160 break; 161 case LOG_VERBOSE: 162 verbose = 1; 163 /* FALLTHROUGH */ 164 case LOG_BRIEF: 165 imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 166 &verbose, sizeof(verbose)); 167 printf("logging request sent.\n"); 168 done = 1; 169 break; 170 case RELOAD: 171 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 172 printf("reload request sent.\n"); 173 done = 1; 174 break; 175 } 176 177 if (imsgbuf_flush(ibuf) == -1) 178 err(1, "write error"); 179 180 while (!done) { 181 if ((n = imsgbuf_read(ibuf)) == -1) 182 err(1, "read error"); 183 if (n == 0) 184 errx(1, "pipe closed"); 185 186 while (!done) { 187 if ((n = imsg_get(ibuf, &imsg)) == -1) 188 errx(1, "imsg_get error"); 189 if (n == 0) 190 break; 191 switch (res->action) { 192 case SHOW: 193 case SHOW_SUM: 194 done = show_summary_msg(&imsg); 195 break; 196 case SHOW_IFACE: 197 done = show_interface_msg(&imsg); 198 break; 199 case SHOW_IFACE_DTAIL: 200 done = show_interface_detail_msg(&imsg); 201 break; 202 case SHOW_IGMP: 203 done = show_igmp_msg(&imsg); 204 break; 205 case SHOW_NBR: 206 done = show_nbr_msg(&imsg); 207 break; 208 case SHOW_NBR_DTAIL: 209 done = show_nbr_detail_msg(&imsg); 210 break; 211 case SHOW_RIB: 212 done = show_rib_msg(&imsg); 213 break; 214 case SHOW_RIB_DTAIL: 215 done = show_rib_detail_msg(&imsg); 216 break; 217 case SHOW_MFC: 218 done = show_mfc_msg(&imsg); 219 break; 220 case SHOW_MFC_DTAIL: 221 done = show_mfc_detail_msg(&imsg); 222 break; 223 case NONE: 224 case LOG_VERBOSE: 225 case LOG_BRIEF: 226 case RELOAD: 227 break; 228 } 229 imsg_free(&imsg); 230 } 231 } 232 close(ctl_sock); 233 free(ibuf); 234 235 return (0); 236 } 237 238 int 239 show_summary_msg(struct imsg *imsg) 240 { 241 struct ctl_sum *sum; 242 243 switch (imsg->hdr.type) { 244 case IMSG_CTL_SHOW_SUM: 245 sum = imsg->data; 246 printf("Router ID: %s\n", inet_ntoa(sum->rtr_id)); 247 printf("Hold time is %d sec(s)\n", sum->hold_time); 248 break; 249 case IMSG_CTL_END: 250 printf("\n"); 251 return (1); 252 default: 253 break; 254 } 255 256 return (0); 257 } 258 259 int 260 show_interface_msg(struct imsg *imsg) 261 { 262 struct ctl_iface *iface; 263 char *netid; 264 265 switch (imsg->hdr.type) { 266 case IMSG_CTL_SHOW_IFACE: 267 iface = imsg->data; 268 269 if (asprintf(&netid, "%s/%d", inet_ntoa(iface->addr), 270 mask2prefixlen(iface->mask.s_addr)) == -1) 271 err(1, NULL); 272 printf("%-11s %-18s %-10s %-10s %-10s %-8s %5d\n", 273 iface->name, netid, if_state_name(iface->state), 274 iface->probe_timer == 0 ? "00:00:00" : 275 fmt_timeframe_core(iface->probe_timer), 276 get_linkstate(iface->if_type, iface->linkstate), 277 iface->uptime == 0 ? "00:00:00" : 278 fmt_timeframe_core(iface->uptime), iface->group_cnt); 279 free(netid); 280 break; 281 case IMSG_CTL_END: 282 printf("\n"); 283 return (1); 284 default: 285 break; 286 } 287 288 return (0); 289 } 290 291 int 292 show_interface_detail_msg(struct imsg *imsg) 293 { 294 struct ctl_iface *iface; 295 296 switch (imsg->hdr.type) { 297 case IMSG_CTL_SHOW_IFACE: 298 iface = imsg->data; 299 300 printf("\n"); 301 printf("Interface %s, line protocol is %s\n", 302 iface->name, print_link(iface->flags)); 303 printf(" Internet address %s/%d\n", 304 inet_ntoa(iface->addr), 305 mask2prefixlen(iface->mask.s_addr)); 306 printf(" Linkstate %s\n", 307 get_linkstate(iface->if_type, iface->linkstate)); 308 printf(" Network type %s, cost: %d\n", 309 if_type_name(iface->type), iface->metric); 310 printf(" State %s, querier ", if_state_name(iface->state)); 311 if (iface->state == IF_STA_QUERIER) 312 printf("%s\n", inet_ntoa(iface->addr)); 313 else 314 printf("%s\n", inet_ntoa(iface->querier)); 315 printf(" Generation ID %d\n", iface->gen_id); 316 printf(" Timer intervals configured, " 317 "probe %d, dead %d\n", iface->probe_interval, 318 iface->dead_interval); 319 if (iface->passive) 320 printf(" Passive interface (No Hellos)\n"); 321 else if (iface->probe_timer < 0) 322 printf(" Hello timer not running\n"); 323 else 324 printf(" Hello timer due in %s\n", 325 fmt_timeframe_core(iface->probe_timer)); 326 printf(" Uptime %s\n", iface->uptime == 0 ? 327 "00:00:00" : fmt_timeframe_core(iface->uptime)); 328 printf(" Adjacent neighbor count is " 329 "%d\n", iface->adj_cnt); 330 break; 331 case IMSG_CTL_END: 332 printf("\n"); 333 return (1); 334 default: 335 break; 336 } 337 338 return (0); 339 } 340 341 int 342 show_igmp_msg(struct imsg *imsg) 343 { 344 struct ctl_iface *iface; 345 struct ctl_group *group; 346 char *netid; 347 348 switch (imsg->hdr.type) { 349 case IMSG_CTL_SHOW_IFACE: 350 iface = imsg->data; 351 if (asprintf(&netid, "%s/%d", inet_ntoa(iface->addr), 352 mask2prefixlen(iface->mask.s_addr)) == -1) 353 err(1, NULL); 354 printf("\nInterface %s, address %s, state %s, groups %d\n", 355 iface->name, netid, if_state_name(iface->state), 356 iface->group_cnt); 357 free(netid); 358 printf(" %-16s %-10s %-10s %-10s\n", "Group", "State", 359 "DeadTimer", "Uptime"); 360 break; 361 case IMSG_CTL_SHOW_IGMP: 362 group = imsg->data; 363 printf(" %-16s %-10s %-10s %-10s\n", inet_ntoa(group->addr), 364 group_state_name(group->state), 365 group->dead_timer == 0 ? "00:00:00" : 366 fmt_timeframe_core(group->dead_timer), 367 group->uptime == 0 ? "00:00:00" : 368 fmt_timeframe_core(group->uptime)); 369 break; 370 case IMSG_CTL_END: 371 printf("\n"); 372 return (1); 373 default: 374 break; 375 } 376 377 return (0); 378 } 379 380 const char * 381 print_if_type(enum iface_type type) 382 { 383 switch (type) { 384 case IF_TYPE_POINTOPOINT: 385 return ("POINTOPOINT"); 386 case IF_TYPE_BROADCAST: 387 return ("BROADCAST"); 388 default: 389 return ("UNKNOWN"); 390 } 391 } 392 393 const char * 394 print_nbr_state(int state) 395 { 396 switch (state) { 397 case NBR_STA_DOWN: 398 return ("DOWN"); 399 case NBR_STA_1_WAY: 400 return ("1-WAY"); 401 case NBR_STA_2_WAY: 402 return ("2-WAY"); 403 default: 404 return ("UNKNOWN"); 405 } 406 } 407 408 const char * 409 print_link(int state) 410 { 411 if (state & IFF_UP) 412 return ("UP"); 413 else 414 return ("DOWN"); 415 } 416 417 #define TF_BUFS 8 418 #define TF_LEN 9 419 420 const char * 421 fmt_timeframe(time_t t) 422 { 423 if (t == 0) 424 return ("Never"); 425 else 426 return (fmt_timeframe_core(time(NULL) - t)); 427 } 428 429 const char * 430 fmt_timeframe_core(time_t t) 431 { 432 char *buf; 433 static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ 434 static int idx = 0; 435 unsigned int sec, min, hrs, day; 436 unsigned long long week; 437 438 if (t == 0) 439 return ("Stopped"); 440 441 buf = tfbuf[idx++]; 442 if (idx == TF_BUFS) 443 idx = 0; 444 445 week = t; 446 447 sec = week % 60; 448 week /= 60; 449 min = week % 60; 450 week /= 60; 451 hrs = week % 24; 452 week /= 24; 453 day = week % 7; 454 week /= 7; 455 456 if (week > 0) 457 snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); 458 else if (day > 0) 459 snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); 460 else 461 snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); 462 463 return (buf); 464 } 465 466 /* prototype defined in dvmrpd.h and shared with the kroute.c version */ 467 u_int8_t 468 mask2prefixlen(in_addr_t ina) 469 { 470 if (ina == 0) 471 return (0); 472 else 473 return (33 - ffs(ntohl(ina))); 474 } 475 476 int 477 show_nbr_msg(struct imsg *imsg) 478 { 479 struct ctl_nbr *nbr; 480 481 switch (imsg->hdr.type) { 482 case IMSG_CTL_SHOW_NBR: 483 nbr = imsg->data; 484 printf("%-15s %-10s %-10s", inet_ntoa(nbr->id), 485 print_nbr_state(nbr->state), 486 fmt_timeframe_core(nbr->dead_timer)); 487 printf("%-15s %-11s %s\n", inet_ntoa(nbr->addr), 488 nbr->name, fmt_timeframe_core(nbr->uptime)); 489 break; 490 case IMSG_CTL_END: 491 printf("\n"); 492 return (1); 493 default: 494 break; 495 } 496 497 return (0); 498 } 499 500 const char * 501 print_dvmrp_options(u_int8_t opts) 502 { 503 static char optbuf[32]; 504 505 snprintf(optbuf, sizeof(optbuf), "*|*|%s|%s|%s|%s|%s|%s", 506 opts & DVMRP_CAP_NETMASK ? "N" : "-", 507 opts & DVMRP_CAP_SNMP ? "S" : "-", 508 opts & DVMRP_CAP_MTRACE ? "M" : "-", 509 opts & DVMRP_CAP_GENID ? "G" : "-", 510 opts & DVMRP_CAP_PRUNE ? "P" : "-", 511 opts & DVMRP_CAP_LEAF ? "L" : "-"); 512 return (optbuf); 513 } 514 515 int 516 show_nbr_detail_msg(struct imsg *imsg) 517 { 518 switch (imsg->hdr.type) { 519 case IMSG_CTL_SHOW_NBR: 520 break; 521 case IMSG_CTL_END: 522 printf("\n"); 523 return (1); 524 default: 525 break; 526 } 527 528 return (0); 529 } 530 531 int 532 show_rib_msg(struct imsg *imsg) 533 { 534 struct ctl_rt *rt; 535 char *dstnet; 536 537 switch (imsg->hdr.type) { 538 case IMSG_CTL_SHOW_RIB: 539 rt = imsg->data; 540 if (asprintf(&dstnet, "%s/%d", inet_ntoa(rt->prefix), 541 rt->prefixlen) == -1) 542 err(1, NULL); 543 544 printf("%-20s %-17s %-7d %-9s %9s\n", dstnet, 545 inet_ntoa(rt->nexthop), 546 rt->cost, rt->uptime == 0 ? "-" : 547 fmt_timeframe_core(rt->uptime), 548 rt->expire == 0 ? "00:00:00" : 549 fmt_timeframe_core(rt->expire)); 550 free(dstnet); 551 552 break; 553 case IMSG_CTL_END: 554 printf("\n"); 555 return (1); 556 default: 557 break; 558 } 559 560 return (0); 561 } 562 563 int 564 show_rib_detail_msg(struct imsg *imsg) 565 { 566 567 switch (imsg->hdr.type) { 568 case IMSG_CTL_SHOW_RIB: 569 break; 570 case IMSG_CTL_END: 571 printf("\n"); 572 return (1); 573 default: 574 break; 575 } 576 577 return (0); 578 } 579 580 int 581 show_mfc_msg(struct imsg *imsg) 582 { 583 char iname[IF_NAMESIZE]; 584 char oname[IF_NAMESIZE] = "-"; 585 struct ctl_mfc *mfc; 586 int i; 587 588 589 switch (imsg->hdr.type) { 590 case IMSG_CTL_SHOW_MFC: 591 mfc = imsg->data; 592 if_indextoname(mfc->ifindex, iname); 593 594 /* search for first entry with ttl > 0 */ 595 for (i = 0; i < MAXVIFS; i++) { 596 if (mfc->ttls[i] > 0) { 597 if_indextoname(i, oname); 598 i++; 599 break; 600 } 601 } 602 603 /* display first entry with uptime */ 604 printf("%-16s ", inet_ntoa(mfc->group)); 605 printf("%-16s %-9s %-9s %-4d %-10s %-10s\n", 606 inet_ntoa(mfc->origin), iname, oname, mfc->ttls[i - 1], 607 mfc->uptime == 0 ? "-" : fmt_timeframe_core(mfc->uptime), 608 mfc->expire == 0 ? "-" : fmt_timeframe_core(mfc->expire)); 609 610 /* display remaining entries with ttl > 0 */ 611 for (; i < MAXVIFS; i++) { 612 if (mfc->ttls[i] > 0) { 613 if_indextoname(i, oname); 614 printf("%43s %-9s %-4d\n", " ", oname, 615 mfc->ttls[i]); 616 } 617 } 618 break; 619 case IMSG_CTL_END: 620 printf("\n"); 621 return (1); 622 default: 623 break; 624 } 625 626 return (0); 627 } 628 629 int 630 show_mfc_detail_msg(struct imsg *imsg) 631 { 632 633 switch (imsg->hdr.type) { 634 case IMSG_CTL_SHOW_MFC: 635 break; 636 case IMSG_CTL_END: 637 printf("\n"); 638 return (1); 639 default: 640 break; 641 } 642 643 return (0); 644 } 645 646 const struct if_status_description 647 if_status_descriptions[] = LINK_STATE_DESCRIPTIONS; 648 649 const char * 650 get_linkstate(uint8_t if_type, int link_state) 651 { 652 const struct if_status_description *p; 653 static char buf[8]; 654 655 for (p = if_status_descriptions; p->ifs_string != NULL; p++) { 656 if (LINK_STATE_DESC_MATCH(p, if_type, link_state)) 657 return (p->ifs_string); 658 } 659 snprintf(buf, sizeof(buf), "[#%d]", link_state); 660 return (buf); 661 } 662