1 /* $OpenBSD: relayctl.c,v 1.34 2008/07/19 12:10:07 reyk 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> [arg [...]]\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 printf("\n"); 355 } 356 break; 357 case IMSG_CTL_RELAY: 358 if (type == SHOW_HOSTS || type == SHOW_RDRS) 359 break; 360 rlay = imsg->data; 361 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 362 rlay->rl_conf.id, "relay", rlay->rl_conf.name, "", 363 print_relay_status(rlay->rl_conf.flags)); 364 break; 365 case IMSG_CTL_RDR_STATS: 366 if (type != SHOW_RDRS) 367 break; 368 bcopy(imsg->data, &stats[0], sizeof(stats[0])); 369 stats[1].id = EMPTY_ID; 370 print_statistics(stats); 371 break; 372 case IMSG_CTL_RELAY_STATS: 373 if (type != SHOW_RELAYS) 374 break; 375 bcopy(imsg->data, &stats, sizeof(stats)); 376 print_statistics(stats); 377 break; 378 case IMSG_CTL_END: 379 return (1); 380 default: 381 errx(1, "wrong message in summary: %u", imsg->hdr.type); 382 break; 383 } 384 return (0); 385 } 386 387 int 388 show_session_msg(struct imsg *imsg) 389 { 390 struct session *con; 391 char a[128], b[128]; 392 struct timeval tv_now; 393 394 switch (imsg->hdr.type) { 395 case IMSG_CTL_SESSION: 396 con = imsg->data; 397 398 (void)print_host(&con->se_in.ss, a, sizeof(a)); 399 (void)print_host(&con->se_out.ss, b, sizeof(b)); 400 printf("session %u:%u %s:%u -> %s:%u\t%s\n", 401 imsg->hdr.peerid, con->se_id, 402 a, ntohs(con->se_in.port), b, ntohs(con->se_out.port), 403 con->se_done ? "DONE" : "RUNNING"); 404 405 if (gettimeofday(&tv_now, NULL)) 406 fatal("show_session_msg: gettimeofday"); 407 print_time(&tv_now, &con->se_tv_start, a, sizeof(a)); 408 print_time(&tv_now, &con->se_tv_last, b, sizeof(b)); 409 printf("\tage %s, idle %s, relay %u", a, b, con->se_relayid); 410 if (con->se_mark) 411 printf(", mark %u", con->se_mark); 412 printf("\n"); 413 break; 414 case IMSG_CTL_END: 415 return (1); 416 default: 417 errx(1, "wrong message in session: %u", imsg->hdr.type); 418 break; 419 } 420 return (0); 421 } 422 423 int 424 show_command_output(struct imsg *imsg) 425 { 426 switch (imsg->hdr.type) { 427 case IMSG_CTL_OK: 428 printf("command succeeded\n"); 429 break; 430 case IMSG_CTL_FAIL: 431 printf("command failed\n"); 432 break; 433 default: 434 errx(1, "wrong message in summary: %u", imsg->hdr.type); 435 } 436 return (1); 437 } 438 439 char * 440 print_rdr_status(int flags) 441 { 442 if (flags & F_DISABLE) { 443 return ("disabled"); 444 } else if (flags & F_DOWN) { 445 return ("down"); 446 } else if (flags & F_BACKUP) { 447 return ("active (using backup table)"); 448 } else 449 return ("active"); 450 } 451 452 char * 453 print_table_status(int up, int fl) 454 { 455 static char buf[1024]; 456 457 bzero(buf, sizeof(buf)); 458 459 if (fl & F_DISABLE) { 460 snprintf(buf, sizeof(buf) - 1, "disabled"); 461 } else if (!up) { 462 snprintf(buf, sizeof(buf) - 1, "empty"); 463 } else 464 snprintf(buf, sizeof(buf) - 1, "active (%d hosts up)", up); 465 return (buf); 466 } 467 468 char * 469 print_host_status(int status, int fl) 470 { 471 if (fl & F_DISABLE) 472 return ("disabled"); 473 474 switch (status) { 475 case HOST_DOWN: 476 return ("down"); 477 case HOST_UNKNOWN: 478 return ("unknown"); 479 case HOST_UP: 480 return ("up"); 481 default: 482 errx(1, "invalid status: %d", status); 483 } 484 } 485 486 char * 487 print_relay_status(int flags) 488 { 489 if (flags & F_DISABLE) { 490 return ("disabled"); 491 } else 492 return ("active"); 493 } 494 495 void 496 print_statistics(struct ctl_stats stats[RELAY_MAXPROC + 1]) 497 { 498 struct ctl_stats crs; 499 int i; 500 501 bzero(&crs, sizeof(crs)); 502 crs.interval = stats[0].interval; 503 for (i = 0; stats[i].id != EMPTY_ID; i++) { 504 crs.cnt += stats[i].cnt; 505 crs.last += stats[i].last; 506 crs.avg += stats[i].avg; 507 crs.last_hour += stats[i].last_hour; 508 crs.avg_hour += stats[i].avg_hour; 509 crs.last_day += stats[i].last_day; 510 crs.avg_day += stats[i].avg_day; 511 } 512 if (crs.cnt == 0) 513 return; 514 printf("\t%8s\ttotal: %llu sessions\n" 515 "\t%8s\tlast: %u/%us %u/h %u/d sessions\n" 516 "\t%8s\taverage: %u/%us %u/h %u/d sessions\n", 517 "", crs.cnt, 518 "", crs.last, crs.interval, 519 crs.last_hour, crs.last_day, 520 "", crs.avg, crs.interval, 521 crs.avg_hour, crs.avg_day); 522 } 523