1 /* $OpenBSD: relayctl.c,v 1.36 2008/12/31 15:22:27 sobrado Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2003 Henning Brauer <henning@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 <sys/types.h> 23 #include <sys/socket.h> 24 #include <sys/queue.h> 25 #include <sys/un.h> 26 27 #include <net/if.h> 28 #include <net/if_media.h> 29 #include <net/if_types.h> 30 #include <netinet/in.h> 31 #include <arpa/inet.h> 32 33 #include <err.h> 34 #include <errno.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <event.h> 40 41 #include <openssl/ssl.h> 42 43 #include "relayd.h" 44 #include "parser.h" 45 46 __dead void usage(void); 47 int show_summary_msg(struct imsg *, int); 48 int show_session_msg(struct imsg *); 49 int show_command_output(struct imsg *); 50 char *print_rdr_status(int); 51 char *print_host_status(int, int); 52 char *print_table_status(int, int); 53 char *print_relay_status(int); 54 void print_statistics(struct ctl_stats[RELAY_MAXPROC + 1]); 55 56 struct imsgname { 57 int type; 58 char *name; 59 void (*func)(struct imsg *); 60 }; 61 62 struct imsgname *monitor_lookup(u_int8_t); 63 void monitor_host_status(struct imsg *); 64 void monitor_id(struct imsg *); 65 int monitor(struct imsg *); 66 67 struct imsgname imsgs[] = { 68 { IMSG_HOST_STATUS, "host_status", monitor_host_status }, 69 { IMSG_CTL_RDR_DISABLE, "ctl_rdr_disable", monitor_id }, 70 { IMSG_CTL_RDR_ENABLE, "ctl_rdr_enable", monitor_id }, 71 { IMSG_CTL_TABLE_DISABLE, "ctl_table_disable", monitor_id }, 72 { IMSG_CTL_TABLE_ENABLE, "ctl_table_enable", monitor_id }, 73 { IMSG_CTL_HOST_DISABLE, "ctl_host_disable", monitor_id }, 74 { IMSG_CTL_HOST_ENABLE, "ctl_host_enable", monitor_id }, 75 { IMSG_CTL_TABLE_CHANGED, "ctl_table_changed", monitor_id }, 76 { IMSG_CTL_PULL_RULESET, "ctl_pull_ruleset", monitor_id }, 77 { IMSG_CTL_PUSH_RULESET, "ctl_push_ruleset", monitor_id }, 78 { IMSG_SYNC, "sync", NULL }, 79 { 0, NULL, NULL } 80 }; 81 struct imsgname imsgunknown = { 82 -1, "<unknown>", NULL 83 }; 84 85 struct imsgbuf *ibuf; 86 87 __dead void 88 usage(void) 89 { 90 extern char *__progname; 91 92 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 93 exit(1); 94 } 95 96 /* dummy function so that relayctl does not need libevent */ 97 void 98 imsg_event_add(struct imsgbuf *i) 99 { 100 /* nothing */ 101 } 102 103 int 104 main(int argc, char *argv[]) 105 { 106 struct sockaddr_un sun; 107 struct parse_result *res; 108 struct imsg imsg; 109 int ctl_sock; 110 int done = 0; 111 int n; 112 113 /* parse options */ 114 if ((res = parse(argc - 1, argv + 1)) == NULL) 115 exit(1); 116 117 /* connect to relayd control socket */ 118 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 119 err(1, "socket"); 120 121 bzero(&sun, sizeof(sun)); 122 sun.sun_family = AF_UNIX; 123 strlcpy(sun.sun_path, RELAYD_SOCKET, sizeof(sun.sun_path)); 124 reconnect: 125 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 126 /* Keep retrying if running in monitor mode */ 127 if (res->action == MONITOR && 128 (errno == ENOENT || errno == ECONNREFUSED)) { 129 usleep(100); 130 goto reconnect; 131 } 132 err(1, "connect: %s", RELAYD_SOCKET); 133 } 134 135 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 136 err(1, NULL); 137 imsg_init(ibuf, ctl_sock, NULL); 138 done = 0; 139 140 /* process user request */ 141 switch (res->action) { 142 case NONE: 143 usage(); 144 /* not reached */ 145 case SHOW_SUM: 146 case SHOW_HOSTS: 147 case SHOW_RDRS: 148 case SHOW_RELAYS: 149 imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0); 150 printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n", 151 "Id", "Type", "Name", "Avlblty"); 152 break; 153 case SHOW_SESSIONS: 154 imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); 155 break; 156 case RDR_ENABLE: 157 imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1, 158 &res->id, sizeof(res->id)); 159 break; 160 case RDR_DISABLE: 161 imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1, 162 &res->id, sizeof(res->id)); 163 break; 164 case TABLE_ENABLE: 165 imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1, 166 &res->id, sizeof(res->id)); 167 break; 168 case TABLE_DISABLE: 169 imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1, 170 &res->id, sizeof(res->id)); 171 break; 172 case HOST_ENABLE: 173 imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1, 174 &res->id, sizeof(res->id)); 175 break; 176 case HOST_DISABLE: 177 imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1, 178 &res->id, sizeof(res->id)); 179 break; 180 case SHUTDOWN: 181 imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); 182 break; 183 case POLL: 184 imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0); 185 break; 186 case RELOAD: 187 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 188 break; 189 case MONITOR: 190 imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); 191 break; 192 } 193 194 while (ibuf->w.queued) 195 if (msgbuf_write(&ibuf->w) < 0) 196 err(1, "write error"); 197 198 while (!done) { 199 if ((n = imsg_read(ibuf)) == -1) 200 errx(1, "imsg_read error"); 201 if (n == 0) 202 errx(1, "pipe closed"); 203 204 while (!done) { 205 if ((n = imsg_get(ibuf, &imsg)) == -1) 206 errx(1, "imsg_get error"); 207 if (n == 0) 208 break; 209 switch (res->action) { 210 case SHOW_SUM: 211 case SHOW_HOSTS: 212 case SHOW_RDRS: 213 case SHOW_RELAYS: 214 done = show_summary_msg(&imsg, res->action); 215 break; 216 case SHOW_SESSIONS: 217 done = show_session_msg(&imsg); 218 break; 219 case RDR_DISABLE: 220 case RDR_ENABLE: 221 case TABLE_DISABLE: 222 case TABLE_ENABLE: 223 case HOST_DISABLE: 224 case HOST_ENABLE: 225 case POLL: 226 case RELOAD: 227 case SHUTDOWN: 228 done = show_command_output(&imsg); 229 break; 230 case NONE: 231 break; 232 case MONITOR: 233 done = monitor(&imsg); 234 break; 235 } 236 imsg_free(&imsg); 237 } 238 } 239 close(ctl_sock); 240 free(ibuf); 241 242 return (0); 243 } 244 245 struct imsgname * 246 monitor_lookup(u_int8_t type) 247 { 248 int i; 249 250 for (i = 0; imsgs[i].name != NULL; i++) 251 if (imsgs[i].type == type) 252 return (&imsgs[i]); 253 return (&imsgunknown); 254 } 255 256 void 257 monitor_host_status(struct imsg *imsg) 258 { 259 struct ctl_status cs; 260 261 memcpy(&cs, imsg->data, sizeof(cs)); 262 printf("\tid: %u\n", cs.id); 263 printf("\tstate: "); 264 switch (cs.up) { 265 case HOST_UP: 266 printf("up\n"); 267 break; 268 case HOST_DOWN: 269 printf("down\n"); 270 break; 271 default: 272 printf("unknown\n"); 273 break; 274 } 275 } 276 277 void 278 monitor_id(struct imsg *imsg) 279 { 280 struct ctl_id id; 281 282 memcpy(&id, imsg->data, sizeof(id)); 283 printf("\tid: %u\n", id.id); 284 if (strlen(id.name)) 285 printf("\tname: %s\n", id.name); 286 } 287 288 int 289 monitor(struct imsg *imsg) 290 { 291 time_t now; 292 int done = 0; 293 struct imsgname *imn; 294 295 now = time(NULL); 296 297 imn = monitor_lookup(imsg->hdr.type); 298 printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name, 299 imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid); 300 printf("\ttimestamp: %u, %s", now, ctime(&now)); 301 if (imn->type == -1) 302 done = 1; 303 if (imn->func != NULL) 304 (*imn->func)(imsg); 305 306 return (done); 307 } 308 309 int 310 show_summary_msg(struct imsg *imsg, int type) 311 { 312 struct rdr *rdr; 313 struct table *table; 314 struct host *host; 315 struct relay *rlay; 316 struct ctl_stats stats[RELAY_MAXPROC]; 317 char name[MAXHOSTNAMELEN]; 318 319 switch (imsg->hdr.type) { 320 case IMSG_CTL_RDR: 321 if (type == SHOW_HOSTS || type == SHOW_RELAYS) 322 break; 323 rdr = imsg->data; 324 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 325 rdr->conf.id, "redirect", rdr->conf.name, "", 326 print_rdr_status(rdr->conf.flags)); 327 break; 328 case IMSG_CTL_TABLE: 329 if (type == SHOW_RELAYS || type == SHOW_RDRS) 330 break; 331 table = imsg->data; 332 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 333 table->conf.id, "table", table->conf.name, "", 334 print_table_status(table->up, table->conf.flags)); 335 break; 336 case IMSG_CTL_HOST: 337 if (type == SHOW_RELAYS || type == SHOW_RDRS) 338 break; 339 host = imsg->data; 340 if (host->conf.parentid) 341 snprintf(name, sizeof(name), "%s parent %u", 342 host->conf.name, host->conf.parentid); 343 else 344 strlcpy(name, host->conf.name, sizeof(name)); 345 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 346 host->conf.id, "host", name, 347 print_availability(host->check_cnt, host->up_cnt), 348 print_host_status(host->up, host->flags)); 349 if (type == SHOW_HOSTS && host->check_cnt) { 350 printf("\t%8s\ttotal: %lu/%lu checks", 351 "", host->up_cnt, host->check_cnt); 352 if (host->retry_cnt) 353 printf(", %d retries", host->retry_cnt); 354 if (host->he && host->up == HOST_DOWN) 355 printf(", error: %s", host_error(host->he)); 356 printf("\n"); 357 } 358 break; 359 case IMSG_CTL_RELAY: 360 if (type == SHOW_HOSTS || type == SHOW_RDRS) 361 break; 362 rlay = imsg->data; 363 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 364 rlay->rl_conf.id, "relay", rlay->rl_conf.name, "", 365 print_relay_status(rlay->rl_conf.flags)); 366 break; 367 case IMSG_CTL_RDR_STATS: 368 if (type != SHOW_RDRS) 369 break; 370 bcopy(imsg->data, &stats[0], sizeof(stats[0])); 371 stats[1].id = EMPTY_ID; 372 print_statistics(stats); 373 break; 374 case IMSG_CTL_RELAY_STATS: 375 if (type != SHOW_RELAYS) 376 break; 377 bcopy(imsg->data, &stats, sizeof(stats)); 378 print_statistics(stats); 379 break; 380 case IMSG_CTL_END: 381 return (1); 382 default: 383 errx(1, "wrong message in summary: %u", imsg->hdr.type); 384 break; 385 } 386 return (0); 387 } 388 389 int 390 show_session_msg(struct imsg *imsg) 391 { 392 struct session *con; 393 char a[128], b[128]; 394 struct timeval tv_now; 395 396 switch (imsg->hdr.type) { 397 case IMSG_CTL_SESSION: 398 con = imsg->data; 399 400 (void)print_host(&con->se_in.ss, a, sizeof(a)); 401 (void)print_host(&con->se_out.ss, b, sizeof(b)); 402 printf("session %u:%u %s:%u -> %s:%u\t%s\n", 403 imsg->hdr.peerid, con->se_id, 404 a, ntohs(con->se_in.port), b, ntohs(con->se_out.port), 405 con->se_done ? "DONE" : "RUNNING"); 406 407 if (gettimeofday(&tv_now, NULL)) 408 fatal("show_session_msg: gettimeofday"); 409 print_time(&tv_now, &con->se_tv_start, a, sizeof(a)); 410 print_time(&tv_now, &con->se_tv_last, b, sizeof(b)); 411 printf("\tage %s, idle %s, relay %u", a, b, con->se_relayid); 412 if (con->se_mark) 413 printf(", mark %u", con->se_mark); 414 printf("\n"); 415 break; 416 case IMSG_CTL_END: 417 return (1); 418 default: 419 errx(1, "wrong message in session: %u", imsg->hdr.type); 420 break; 421 } 422 return (0); 423 } 424 425 int 426 show_command_output(struct imsg *imsg) 427 { 428 switch (imsg->hdr.type) { 429 case IMSG_CTL_OK: 430 printf("command succeeded\n"); 431 break; 432 case IMSG_CTL_FAIL: 433 printf("command failed\n"); 434 break; 435 default: 436 errx(1, "wrong message in summary: %u", imsg->hdr.type); 437 } 438 return (1); 439 } 440 441 char * 442 print_rdr_status(int flags) 443 { 444 if (flags & F_DISABLE) { 445 return ("disabled"); 446 } else if (flags & F_DOWN) { 447 return ("down"); 448 } else if (flags & F_BACKUP) { 449 return ("active (using backup table)"); 450 } else 451 return ("active"); 452 } 453 454 char * 455 print_table_status(int up, int fl) 456 { 457 static char buf[1024]; 458 459 bzero(buf, sizeof(buf)); 460 461 if (fl & F_DISABLE) { 462 snprintf(buf, sizeof(buf) - 1, "disabled"); 463 } else if (!up) { 464 snprintf(buf, sizeof(buf) - 1, "empty"); 465 } else 466 snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up); 467 return (buf); 468 } 469 470 char * 471 print_host_status(int status, int fl) 472 { 473 if (fl & F_DISABLE) 474 return ("disabled"); 475 476 switch (status) { 477 case HOST_DOWN: 478 return ("down"); 479 case HOST_UNKNOWN: 480 return ("unknown"); 481 case HOST_UP: 482 return ("up"); 483 default: 484 errx(1, "invalid status: %d", status); 485 } 486 } 487 488 char * 489 print_relay_status(int flags) 490 { 491 if (flags & F_DISABLE) { 492 return ("disabled"); 493 } else 494 return ("active"); 495 } 496 497 void 498 print_statistics(struct ctl_stats stats[RELAY_MAXPROC + 1]) 499 { 500 struct ctl_stats crs; 501 int i; 502 503 bzero(&crs, sizeof(crs)); 504 crs.interval = stats[0].interval; 505 for (i = 0; stats[i].id != EMPTY_ID; i++) { 506 crs.cnt += stats[i].cnt; 507 crs.last += stats[i].last; 508 crs.avg += stats[i].avg; 509 crs.last_hour += stats[i].last_hour; 510 crs.avg_hour += stats[i].avg_hour; 511 crs.last_day += stats[i].last_day; 512 crs.avg_day += stats[i].avg_day; 513 } 514 if (crs.cnt == 0) 515 return; 516 printf("\t%8s\ttotal: %llu sessions\n" 517 "\t%8s\tlast: %u/%us %u/h %u/d sessions\n" 518 "\t%8s\taverage: %u/%us %u/h %u/d sessions\n", 519 "", crs.cnt, 520 "", crs.last, crs.interval, 521 crs.last_hour, crs.last_day, 522 "", crs.avg, crs.interval, 523 crs.avg_hour, crs.avg_day); 524 } 525