1 /* $OpenBSD: relayctl.c,v 1.58 2017/11/29 15:24:50 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 7 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 8 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <sys/time.h> 26 #include <sys/queue.h> 27 #include <sys/un.h> 28 29 #include <arpa/inet.h> 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <limits.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <time.h> 39 #include <imsg.h> 40 41 #include "relayd.h" 42 #include "parser.h" 43 44 __dead void usage(void); 45 int show_summary_msg(struct imsg *, int); 46 int show_session_msg(struct imsg *); 47 int show_command_output(struct imsg *); 48 char *print_rdr_status(int); 49 char *print_host_status(int, int); 50 char *print_table_status(int, int); 51 char *print_relay_status(int); 52 void print_statistics(struct ctl_stats[PROC_MAX_INSTANCES + 1]); 53 54 struct imsgname { 55 int type; 56 char *name; 57 void (*func)(struct imsg *); 58 }; 59 60 struct imsgname *monitor_lookup(u_int8_t); 61 void monitor_host_status(struct imsg *); 62 void monitor_id(struct imsg *); 63 int monitor(struct imsg *); 64 65 struct imsgname imsgs[] = { 66 { IMSG_HOST_STATUS, "host_status", monitor_host_status }, 67 { IMSG_CTL_RDR_DISABLE, "ctl_rdr_disable", monitor_id }, 68 { IMSG_CTL_RDR_ENABLE, "ctl_rdr_enable", monitor_id }, 69 { IMSG_CTL_TABLE_DISABLE, "ctl_table_disable", monitor_id }, 70 { IMSG_CTL_TABLE_ENABLE, "ctl_table_enable", monitor_id }, 71 { IMSG_CTL_HOST_DISABLE, "ctl_host_disable", monitor_id }, 72 { IMSG_CTL_HOST_ENABLE, "ctl_host_enable", monitor_id }, 73 { IMSG_CTL_TABLE_CHANGED, "ctl_table_changed", monitor_id }, 74 { IMSG_CTL_PULL_RULESET, "ctl_pull_ruleset", monitor_id }, 75 { IMSG_CTL_PUSH_RULESET, "ctl_push_ruleset", monitor_id }, 76 { IMSG_SYNC, "sync", NULL }, 77 { 0, NULL, NULL } 78 }; 79 struct imsgname imsgunknown = { 80 -1, "<unknown>", NULL 81 }; 82 83 struct imsgbuf *ibuf; 84 int error = 0; 85 86 __dead void 87 usage(void) 88 { 89 extern char *__progname; 90 91 fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n", 92 __progname); 93 exit(1); 94 } 95 96 int 97 main(int argc, char *argv[]) 98 { 99 struct sockaddr_un sun; 100 struct parse_result *res; 101 struct imsg imsg; 102 int ctl_sock; 103 int done = 0; 104 int n, verbose = 0; 105 int ch; 106 const char *sockname; 107 108 sockname = RELAYD_SOCKET; 109 while ((ch = getopt(argc, argv, "s:")) != -1) { 110 switch (ch) { 111 case 's': 112 sockname = optarg; 113 break; 114 default: 115 usage(); 116 /* NOTREACHED */ 117 } 118 } 119 argc -= optind; 120 argv += optind; 121 122 /* parse options */ 123 if ((res = parse(argc, argv)) == NULL) 124 exit(1); 125 126 /* connect to relayd control socket */ 127 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 128 err(1, "socket"); 129 130 bzero(&sun, sizeof(sun)); 131 sun.sun_family = AF_UNIX; 132 if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >= 133 sizeof(sun.sun_path)) 134 errx(1, "socket `%s' too long", sockname); 135 reconnect: 136 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 137 /* Keep retrying if running in monitor mode */ 138 if (res->action == MONITOR && 139 (errno == ENOENT || errno == ECONNREFUSED)) { 140 usleep(100); 141 goto reconnect; 142 } 143 err(1, "connect: %s", sockname); 144 } 145 146 if (pledge("stdio", NULL) == -1) 147 err(1, "pledge"); 148 149 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 150 err(1, NULL); 151 imsg_init(ibuf, ctl_sock); 152 done = 0; 153 154 /* process user request */ 155 switch (res->action) { 156 case NONE: 157 usage(); 158 /* not reached */ 159 case SHOW_SUM: 160 case SHOW_HOSTS: 161 case SHOW_RDRS: 162 case SHOW_RELAYS: 163 case SHOW_ROUTERS: 164 imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0); 165 printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n", 166 "Id", "Type", "Name", "Avlblty"); 167 break; 168 case SHOW_SESSIONS: 169 imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); 170 break; 171 case RDR_ENABLE: 172 imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1, 173 &res->id, sizeof(res->id)); 174 break; 175 case RDR_DISABLE: 176 imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1, 177 &res->id, sizeof(res->id)); 178 break; 179 case TABLE_ENABLE: 180 imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1, 181 &res->id, sizeof(res->id)); 182 break; 183 case TABLE_DISABLE: 184 imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1, 185 &res->id, sizeof(res->id)); 186 break; 187 case HOST_ENABLE: 188 imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1, 189 &res->id, sizeof(res->id)); 190 break; 191 case HOST_DISABLE: 192 imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1, 193 &res->id, sizeof(res->id)); 194 break; 195 case SHUTDOWN: 196 imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); 197 break; 198 case POLL: 199 imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0); 200 break; 201 case LOAD: 202 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, 203 res->path, strlen(res->path)); 204 done = 1; 205 break; 206 case RELOAD: 207 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 208 done = 1; 209 break; 210 case MONITOR: 211 imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); 212 break; 213 case LOG_VERBOSE: 214 verbose = 2; 215 /* FALLTHROUGH */ 216 case LOG_BRIEF: 217 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 218 &verbose, sizeof(verbose)); 219 printf("logging request sent.\n"); 220 done = 1; 221 break; 222 } 223 224 while (ibuf->w.queued) 225 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 226 err(1, "write error"); 227 228 while (!done) { 229 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 230 errx(1, "imsg_read error"); 231 if (n == 0) 232 errx(1, "pipe closed"); 233 234 while (!done) { 235 if ((n = imsg_get(ibuf, &imsg)) == -1) 236 errx(1, "imsg_get error"); 237 if (n == 0) 238 break; 239 switch (res->action) { 240 case SHOW_SUM: 241 case SHOW_HOSTS: 242 case SHOW_RDRS: 243 case SHOW_RELAYS: 244 case SHOW_ROUTERS: 245 done = show_summary_msg(&imsg, res->action); 246 break; 247 case SHOW_SESSIONS: 248 done = show_session_msg(&imsg); 249 break; 250 case RDR_DISABLE: 251 case RDR_ENABLE: 252 case TABLE_DISABLE: 253 case TABLE_ENABLE: 254 case HOST_DISABLE: 255 case HOST_ENABLE: 256 case POLL: 257 case SHUTDOWN: 258 done = show_command_output(&imsg); 259 break; 260 case NONE: 261 case LOG_VERBOSE: 262 case LOG_BRIEF: 263 case RELOAD: 264 case LOAD: 265 break; 266 case MONITOR: 267 done = monitor(&imsg); 268 break; 269 } 270 imsg_free(&imsg); 271 } 272 } 273 close(ctl_sock); 274 free(ibuf); 275 276 return (error ? 1 : 0); 277 } 278 279 struct imsgname * 280 monitor_lookup(u_int8_t type) 281 { 282 int i; 283 284 for (i = 0; imsgs[i].name != NULL; i++) 285 if (imsgs[i].type == type) 286 return (&imsgs[i]); 287 return (&imsgunknown); 288 } 289 290 void 291 monitor_host_status(struct imsg *imsg) 292 { 293 struct ctl_status cs; 294 295 memcpy(&cs, imsg->data, sizeof(cs)); 296 printf("\tid: %u\n", cs.id); 297 printf("\tstate: "); 298 switch (cs.up) { 299 case HOST_UP: 300 printf("up\n"); 301 break; 302 case HOST_DOWN: 303 printf("down\n"); 304 break; 305 default: 306 printf("unknown\n"); 307 break; 308 } 309 } 310 311 void 312 monitor_id(struct imsg *imsg) 313 { 314 struct ctl_id id; 315 316 memcpy(&id, imsg->data, sizeof(id)); 317 printf("\tid: %u\n", id.id); 318 if (strlen(id.name)) 319 printf("\tname: %s\n", id.name); 320 } 321 322 int 323 monitor(struct imsg *imsg) 324 { 325 time_t now; 326 int done = 0; 327 struct imsgname *imn; 328 329 now = time(NULL); 330 331 imn = monitor_lookup(imsg->hdr.type); 332 printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name, 333 imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid); 334 printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now)); 335 if (imn->type == -1) 336 done = 1; 337 if (imn->func != NULL) 338 (*imn->func)(imsg); 339 340 return (done); 341 } 342 343 int 344 show_summary_msg(struct imsg *imsg, int type) 345 { 346 struct rdr *rdr; 347 struct table *table; 348 struct host *host; 349 struct relay *rlay; 350 struct router *rt; 351 struct netroute *nr; 352 struct ctl_stats stats[PROC_MAX_INSTANCES]; 353 char name[HOST_NAME_MAX+1]; 354 355 switch (imsg->hdr.type) { 356 case IMSG_CTL_RDR: 357 if (!(type == SHOW_SUM || type == SHOW_RDRS)) 358 break; 359 rdr = imsg->data; 360 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 361 rdr->conf.id, "redirect", rdr->conf.name, "", 362 print_rdr_status(rdr->conf.flags)); 363 break; 364 case IMSG_CTL_TABLE: 365 if (!(type == SHOW_SUM || type == SHOW_HOSTS)) 366 break; 367 table = imsg->data; 368 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 369 table->conf.id, "table", table->conf.name, "", 370 print_table_status(table->up, table->conf.flags)); 371 break; 372 case IMSG_CTL_HOST: 373 if (!(type == SHOW_SUM || type == SHOW_HOSTS)) 374 break; 375 host = imsg->data; 376 if (host->conf.parentid) 377 snprintf(name, sizeof(name), "%s parent %u", 378 host->conf.name, host->conf.parentid); 379 else 380 strlcpy(name, host->conf.name, sizeof(name)); 381 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 382 host->conf.id, "host", name, 383 print_availability(host->check_cnt, host->up_cnt), 384 print_host_status(host->up, host->flags)); 385 if (type == SHOW_HOSTS && host->check_cnt) { 386 printf("\t%8s\ttotal: %lu/%lu checks", 387 "", host->up_cnt, host->check_cnt); 388 if (host->retry_cnt) 389 printf(", %d retries", host->retry_cnt); 390 if (host->he && host->up == HOST_DOWN) 391 printf(", error: %s", host_error(host->he)); 392 printf("\n"); 393 } 394 break; 395 case IMSG_CTL_RELAY: 396 if (!(type == SHOW_SUM || type == SHOW_RELAYS)) 397 break; 398 rlay = imsg->data; 399 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 400 rlay->rl_conf.id, "relay", rlay->rl_conf.name, "", 401 print_relay_status(rlay->rl_conf.flags)); 402 break; 403 case IMSG_CTL_RDR_STATS: 404 if (type != SHOW_RDRS) 405 break; 406 bcopy(imsg->data, &stats[0], sizeof(stats[0])); 407 stats[1].id = EMPTY_ID; 408 print_statistics(stats); 409 break; 410 case IMSG_CTL_RELAY_STATS: 411 if (type != SHOW_RELAYS) 412 break; 413 bcopy(imsg->data, &stats, sizeof(stats)); 414 print_statistics(stats); 415 break; 416 case IMSG_CTL_ROUTER: 417 if (!(type == SHOW_SUM || type == SHOW_ROUTERS)) 418 break; 419 rt = imsg->data; 420 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 421 rt->rt_conf.id, "router", rt->rt_conf.name, "", 422 print_relay_status(rt->rt_conf.flags)); 423 if (type != SHOW_ROUTERS) 424 break; 425 if (rt->rt_conf.rtable) 426 printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable); 427 if (strlen(rt->rt_conf.label)) 428 printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label); 429 break; 430 case IMSG_CTL_NETROUTE: 431 if (type != SHOW_ROUTERS) 432 break; 433 nr = imsg->data; 434 (void)print_host(&nr->nr_conf.ss, name, sizeof(name)); 435 printf("\t%8s\troute: %s/%d\n", 436 "", name, nr->nr_conf.prefixlen); 437 break; 438 case IMSG_CTL_END: 439 return (1); 440 default: 441 errx(1, "wrong message in summary: %u", imsg->hdr.type); 442 break; 443 } 444 return (0); 445 } 446 447 int 448 show_session_msg(struct imsg *imsg) 449 { 450 struct rsession *con; 451 char a[128], b[128]; 452 struct timeval tv_now; 453 454 switch (imsg->hdr.type) { 455 case IMSG_CTL_SESSION: 456 con = imsg->data; 457 458 (void)print_host(&con->se_in.ss, a, sizeof(a)); 459 (void)print_host(&con->se_out.ss, b, sizeof(b)); 460 printf("session %u:%u %s:%u -> %s:%u\t%s\n", 461 imsg->hdr.peerid, con->se_id, 462 a, ntohs(con->se_in.port), b, ntohs(con->se_out.port), 463 con->se_done ? "DONE" : "RUNNING"); 464 465 getmonotime(&tv_now); 466 print_time(&tv_now, &con->se_tv_start, a, sizeof(a)); 467 print_time(&tv_now, &con->se_tv_last, b, sizeof(b)); 468 printf("\tage %s, idle %s, relay %u, pid %u", 469 a, b, con->se_relayid, con->se_pid); 470 /* XXX grab tagname instead of tag id */ 471 if (con->se_tag) 472 printf(", tag (id) %u", con->se_tag); 473 printf("\n"); 474 break; 475 case IMSG_CTL_END: 476 return (1); 477 default: 478 errx(1, "wrong message in session: %u", imsg->hdr.type); 479 break; 480 } 481 return (0); 482 } 483 484 int 485 show_command_output(struct imsg *imsg) 486 { 487 switch (imsg->hdr.type) { 488 case IMSG_CTL_OK: 489 printf("command succeeded\n"); 490 break; 491 case IMSG_CTL_FAIL: 492 printf("command failed\n"); 493 error++; 494 break; 495 default: 496 errx(1, "wrong message in summary: %u", imsg->hdr.type); 497 } 498 return (1); 499 } 500 501 char * 502 print_rdr_status(int flags) 503 { 504 if (flags & F_DISABLE) { 505 return ("disabled"); 506 } else if (flags & F_DOWN) { 507 return ("down"); 508 } else if (flags & F_BACKUP) { 509 return ("active (using backup table)"); 510 } else 511 return ("active"); 512 } 513 514 char * 515 print_table_status(int up, int fl) 516 { 517 static char buf[1024]; 518 519 bzero(buf, sizeof(buf)); 520 521 if (fl & F_DISABLE) { 522 snprintf(buf, sizeof(buf) - 1, "disabled"); 523 } else if (!up) { 524 snprintf(buf, sizeof(buf) - 1, "empty"); 525 } else 526 snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up); 527 return (buf); 528 } 529 530 char * 531 print_host_status(int status, int fl) 532 { 533 if (fl & F_DISABLE) 534 return ("disabled"); 535 536 switch (status) { 537 case HOST_DOWN: 538 return ("down"); 539 case HOST_UNKNOWN: 540 return ("unknown"); 541 case HOST_UP: 542 return ("up"); 543 default: 544 errx(1, "invalid status: %d", status); 545 } 546 } 547 548 char * 549 print_relay_status(int flags) 550 { 551 if (flags & F_DISABLE) { 552 return ("disabled"); 553 } else 554 return ("active"); 555 } 556 557 void 558 print_statistics(struct ctl_stats stats[PROC_MAX_INSTANCES + 1]) 559 { 560 struct ctl_stats crs; 561 int i; 562 563 bzero(&crs, sizeof(crs)); 564 crs.interval = stats[0].interval; 565 for (i = 0; stats[i].id != EMPTY_ID; i++) { 566 crs.cnt += stats[i].cnt; 567 crs.last += stats[i].last; 568 crs.avg += stats[i].avg; 569 crs.last_hour += stats[i].last_hour; 570 crs.avg_hour += stats[i].avg_hour; 571 crs.last_day += stats[i].last_day; 572 crs.avg_day += stats[i].avg_day; 573 } 574 if (crs.cnt == 0) 575 return; 576 printf("\t%8s\ttotal: %llu sessions\n" 577 "\t%8s\tlast: %u/%llus %u/h %u/d sessions\n" 578 "\t%8s\taverage: %u/%llus %u/h %u/d sessions\n", 579 "", crs.cnt, 580 "", crs.last, crs.interval, 581 crs.last_hour, crs.last_day, 582 "", crs.avg, crs.interval, 583 crs.avg_hour, crs.avg_day); 584 } 585