1 /* $OpenBSD: relayctl.c,v 1.40 2009/09/01 08:51:34 claudio 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 int error = 0; 87 88 __dead void 89 usage(void) 90 { 91 extern char *__progname; 92 93 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 94 exit(1); 95 } 96 97 int 98 main(int argc, char *argv[]) 99 { 100 struct sockaddr_un sun; 101 struct parse_result *res; 102 struct imsg imsg; 103 int ctl_sock; 104 int done = 0; 105 int n; 106 107 /* parse options */ 108 if ((res = parse(argc - 1, argv + 1)) == NULL) 109 exit(1); 110 111 /* connect to relayd control socket */ 112 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 113 err(1, "socket"); 114 115 bzero(&sun, sizeof(sun)); 116 sun.sun_family = AF_UNIX; 117 strlcpy(sun.sun_path, RELAYD_SOCKET, sizeof(sun.sun_path)); 118 reconnect: 119 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 120 /* Keep retrying if running in monitor mode */ 121 if (res->action == MONITOR && 122 (errno == ENOENT || errno == ECONNREFUSED)) { 123 usleep(100); 124 goto reconnect; 125 } 126 err(1, "connect: %s", RELAYD_SOCKET); 127 } 128 129 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 130 err(1, NULL); 131 imsg_init(ibuf, ctl_sock); 132 done = 0; 133 134 /* process user request */ 135 switch (res->action) { 136 case NONE: 137 usage(); 138 /* not reached */ 139 case SHOW_SUM: 140 case SHOW_HOSTS: 141 case SHOW_RDRS: 142 case SHOW_RELAYS: 143 case SHOW_ROUTERS: 144 imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0); 145 printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n", 146 "Id", "Type", "Name", "Avlblty"); 147 break; 148 case SHOW_SESSIONS: 149 imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); 150 break; 151 case RDR_ENABLE: 152 imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1, 153 &res->id, sizeof(res->id)); 154 break; 155 case RDR_DISABLE: 156 imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1, 157 &res->id, sizeof(res->id)); 158 break; 159 case TABLE_ENABLE: 160 imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1, 161 &res->id, sizeof(res->id)); 162 break; 163 case TABLE_DISABLE: 164 imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1, 165 &res->id, sizeof(res->id)); 166 break; 167 case HOST_ENABLE: 168 imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1, 169 &res->id, sizeof(res->id)); 170 break; 171 case HOST_DISABLE: 172 imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1, 173 &res->id, sizeof(res->id)); 174 break; 175 case SHUTDOWN: 176 imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); 177 break; 178 case POLL: 179 imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0); 180 break; 181 case RELOAD: 182 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 183 break; 184 case MONITOR: 185 imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); 186 break; 187 } 188 189 while (ibuf->w.queued) 190 if (msgbuf_write(&ibuf->w) < 0) 191 err(1, "write error"); 192 193 while (!done) { 194 if ((n = imsg_read(ibuf)) == -1) 195 errx(1, "imsg_read error"); 196 if (n == 0) 197 errx(1, "pipe closed"); 198 199 while (!done) { 200 if ((n = imsg_get(ibuf, &imsg)) == -1) 201 errx(1, "imsg_get error"); 202 if (n == 0) 203 break; 204 switch (res->action) { 205 case SHOW_SUM: 206 case SHOW_HOSTS: 207 case SHOW_RDRS: 208 case SHOW_RELAYS: 209 case SHOW_ROUTERS: 210 done = show_summary_msg(&imsg, res->action); 211 break; 212 case SHOW_SESSIONS: 213 done = show_session_msg(&imsg); 214 break; 215 case RDR_DISABLE: 216 case RDR_ENABLE: 217 case TABLE_DISABLE: 218 case TABLE_ENABLE: 219 case HOST_DISABLE: 220 case HOST_ENABLE: 221 case POLL: 222 case RELOAD: 223 case SHUTDOWN: 224 done = show_command_output(&imsg); 225 break; 226 case NONE: 227 break; 228 case MONITOR: 229 done = monitor(&imsg); 230 break; 231 } 232 imsg_free(&imsg); 233 } 234 } 235 close(ctl_sock); 236 free(ibuf); 237 238 return (error ? 1 : 0); 239 } 240 241 struct imsgname * 242 monitor_lookup(u_int8_t type) 243 { 244 int i; 245 246 for (i = 0; imsgs[i].name != NULL; i++) 247 if (imsgs[i].type == type) 248 return (&imsgs[i]); 249 return (&imsgunknown); 250 } 251 252 void 253 monitor_host_status(struct imsg *imsg) 254 { 255 struct ctl_status cs; 256 257 memcpy(&cs, imsg->data, sizeof(cs)); 258 printf("\tid: %u\n", cs.id); 259 printf("\tstate: "); 260 switch (cs.up) { 261 case HOST_UP: 262 printf("up\n"); 263 break; 264 case HOST_DOWN: 265 printf("down\n"); 266 break; 267 default: 268 printf("unknown\n"); 269 break; 270 } 271 } 272 273 void 274 monitor_id(struct imsg *imsg) 275 { 276 struct ctl_id id; 277 278 memcpy(&id, imsg->data, sizeof(id)); 279 printf("\tid: %u\n", id.id); 280 if (strlen(id.name)) 281 printf("\tname: %s\n", id.name); 282 } 283 284 int 285 monitor(struct imsg *imsg) 286 { 287 time_t now; 288 int done = 0; 289 struct imsgname *imn; 290 291 now = time(NULL); 292 293 imn = monitor_lookup(imsg->hdr.type); 294 printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name, 295 imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid); 296 printf("\ttimestamp: %u, %s", now, ctime(&now)); 297 if (imn->type == -1) 298 done = 1; 299 if (imn->func != NULL) 300 (*imn->func)(imsg); 301 302 return (done); 303 } 304 305 int 306 show_summary_msg(struct imsg *imsg, int type) 307 { 308 struct rdr *rdr; 309 struct table *table; 310 struct host *host; 311 struct relay *rlay; 312 struct router *rt; 313 struct netroute *nr; 314 struct ctl_stats stats[RELAY_MAXPROC]; 315 char name[MAXHOSTNAMELEN]; 316 317 switch (imsg->hdr.type) { 318 case IMSG_CTL_RDR: 319 if (!(type == SHOW_SUM || type == SHOW_RDRS)) 320 break; 321 rdr = imsg->data; 322 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 323 rdr->conf.id, "redirect", rdr->conf.name, "", 324 print_rdr_status(rdr->conf.flags)); 325 break; 326 case IMSG_CTL_TABLE: 327 if (!(type == SHOW_SUM || type == SHOW_HOSTS)) 328 break; 329 table = imsg->data; 330 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 331 table->conf.id, "table", table->conf.name, "", 332 print_table_status(table->up, table->conf.flags)); 333 break; 334 case IMSG_CTL_HOST: 335 if (!(type == SHOW_SUM || type == SHOW_HOSTS)) 336 break; 337 host = imsg->data; 338 if (host->conf.parentid) 339 snprintf(name, sizeof(name), "%s parent %u", 340 host->conf.name, host->conf.parentid); 341 else 342 strlcpy(name, host->conf.name, sizeof(name)); 343 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 344 host->conf.id, "host", name, 345 print_availability(host->check_cnt, host->up_cnt), 346 print_host_status(host->up, host->flags)); 347 if (type == SHOW_HOSTS && host->check_cnt) { 348 printf("\t%8s\ttotal: %lu/%lu checks", 349 "", host->up_cnt, host->check_cnt); 350 if (host->retry_cnt) 351 printf(", %d retries", host->retry_cnt); 352 if (host->he && host->up == HOST_DOWN) 353 printf(", error: %s", host_error(host->he)); 354 printf("\n"); 355 } 356 break; 357 case IMSG_CTL_RELAY: 358 if (!(type == SHOW_SUM || type == SHOW_RELAYS)) 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_ROUTER: 379 if (!(type == SHOW_SUM || type == SHOW_ROUTERS)) 380 break; 381 rt = imsg->data; 382 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 383 rt->rt_conf.id, "router", rt->rt_conf.name, "", 384 print_relay_status(rt->rt_conf.flags)); 385 if (type != SHOW_ROUTERS) 386 break; 387 if (rt->rt_conf.rtable) 388 printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable); 389 if (strlen(rt->rt_conf.label)) 390 printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label); 391 break; 392 case IMSG_CTL_NETROUTE: 393 if (type != SHOW_ROUTERS) 394 break; 395 nr = imsg->data; 396 (void)print_host(&nr->nr_conf.ss, name, sizeof(name)); 397 printf("\t%8s\troute: %s/%d\n", 398 "", name, nr->nr_conf.prefixlen); 399 break; 400 case IMSG_CTL_END: 401 return (1); 402 default: 403 errx(1, "wrong message in summary: %u", imsg->hdr.type); 404 break; 405 } 406 return (0); 407 } 408 409 int 410 show_session_msg(struct imsg *imsg) 411 { 412 struct rsession *con; 413 char a[128], b[128]; 414 struct timeval tv_now; 415 416 switch (imsg->hdr.type) { 417 case IMSG_CTL_SESSION: 418 con = imsg->data; 419 420 (void)print_host(&con->se_in.ss, a, sizeof(a)); 421 (void)print_host(&con->se_out.ss, b, sizeof(b)); 422 printf("session %u:%u %s:%u -> %s:%u\t%s\n", 423 imsg->hdr.peerid, con->se_id, 424 a, ntohs(con->se_in.port), b, ntohs(con->se_out.port), 425 con->se_done ? "DONE" : "RUNNING"); 426 427 if (gettimeofday(&tv_now, NULL)) 428 fatal("show_session_msg: gettimeofday"); 429 print_time(&tv_now, &con->se_tv_start, a, sizeof(a)); 430 print_time(&tv_now, &con->se_tv_last, b, sizeof(b)); 431 printf("\tage %s, idle %s, relay %u", a, b, con->se_relayid); 432 if (con->se_mark) 433 printf(", mark %u", con->se_mark); 434 printf("\n"); 435 break; 436 case IMSG_CTL_END: 437 return (1); 438 default: 439 errx(1, "wrong message in session: %u", imsg->hdr.type); 440 break; 441 } 442 return (0); 443 } 444 445 int 446 show_command_output(struct imsg *imsg) 447 { 448 switch (imsg->hdr.type) { 449 case IMSG_CTL_OK: 450 printf("command succeeded\n"); 451 break; 452 case IMSG_CTL_FAIL: 453 printf("command failed\n"); 454 error++; 455 break; 456 default: 457 errx(1, "wrong message in summary: %u", imsg->hdr.type); 458 } 459 return (1); 460 } 461 462 char * 463 print_rdr_status(int flags) 464 { 465 if (flags & F_DISABLE) { 466 return ("disabled"); 467 } else if (flags & F_DOWN) { 468 return ("down"); 469 } else if (flags & F_BACKUP) { 470 return ("active (using backup table)"); 471 } else 472 return ("active"); 473 } 474 475 char * 476 print_table_status(int up, int fl) 477 { 478 static char buf[1024]; 479 480 bzero(buf, sizeof(buf)); 481 482 if (fl & F_DISABLE) { 483 snprintf(buf, sizeof(buf) - 1, "disabled"); 484 } else if (!up) { 485 snprintf(buf, sizeof(buf) - 1, "empty"); 486 } else 487 snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up); 488 return (buf); 489 } 490 491 char * 492 print_host_status(int status, int fl) 493 { 494 if (fl & F_DISABLE) 495 return ("disabled"); 496 497 switch (status) { 498 case HOST_DOWN: 499 return ("down"); 500 case HOST_UNKNOWN: 501 return ("unknown"); 502 case HOST_UP: 503 return ("up"); 504 default: 505 errx(1, "invalid status: %d", status); 506 } 507 } 508 509 char * 510 print_relay_status(int flags) 511 { 512 if (flags & F_DISABLE) { 513 return ("disabled"); 514 } else 515 return ("active"); 516 } 517 518 void 519 print_statistics(struct ctl_stats stats[RELAY_MAXPROC + 1]) 520 { 521 struct ctl_stats crs; 522 int i; 523 524 bzero(&crs, sizeof(crs)); 525 crs.interval = stats[0].interval; 526 for (i = 0; stats[i].id != EMPTY_ID; i++) { 527 crs.cnt += stats[i].cnt; 528 crs.last += stats[i].last; 529 crs.avg += stats[i].avg; 530 crs.last_hour += stats[i].last_hour; 531 crs.avg_hour += stats[i].avg_hour; 532 crs.last_day += stats[i].last_day; 533 crs.avg_day += stats[i].avg_day; 534 } 535 if (crs.cnt == 0) 536 return; 537 printf("\t%8s\ttotal: %llu sessions\n" 538 "\t%8s\tlast: %u/%us %u/h %u/d sessions\n" 539 "\t%8s\taverage: %u/%us %u/h %u/d sessions\n", 540 "", crs.cnt, 541 "", crs.last, crs.interval, 542 crs.last_hour, crs.last_day, 543 "", crs.avg, crs.interval, 544 crs.avg_hour, crs.avg_day); 545 } 546