1 /* $OpenBSD: hce.c,v 1.47 2009/04/17 09:47:06 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/stat.h> 23 #include <sys/socket.h> 24 #include <sys/un.h> 25 26 #include <net/if.h> 27 #include <netinet/in_systm.h> 28 #include <netinet/in.h> 29 #include <netinet/ip.h> 30 31 #include <errno.h> 32 #include <event.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <err.h> 38 #include <pwd.h> 39 40 #include <openssl/ssl.h> 41 42 #include "relayd.h" 43 44 __dead void hce_shutdown(void); 45 void hce_sig_handler(int sig, short, void *); 46 void hce_dispatch_imsg(int, short, void *); 47 void hce_dispatch_parent(int, short, void *); 48 void hce_launch_checks(int, short, void *); 49 void hce_setup_events(void); 50 void hce_disable_events(void); 51 52 static struct relayd *env = NULL; 53 struct imsgbuf *ibuf_pfe; 54 struct imsgbuf *ibuf_main; 55 int running = 0; 56 57 void 58 hce_sig_handler(int sig, short event, void *arg) 59 { 60 switch (sig) { 61 case SIGINT: 62 case SIGTERM: 63 hce_shutdown(); 64 break; 65 default: 66 fatalx("hce_sig_handler: unexpected signal"); 67 } 68 } 69 70 pid_t 71 hce(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], 72 int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2], 73 int pipe_pfe2relay[RELAY_MAXPROC][2]) 74 { 75 pid_t pid; 76 struct passwd *pw; 77 int i; 78 struct event ev_sigint; 79 struct event ev_sigterm; 80 81 switch (pid = fork()) { 82 case -1: 83 fatal("hce: cannot fork"); 84 case 0: 85 break; 86 default: 87 return (pid); 88 } 89 90 env = x_env; 91 purge_config(env, PURGE_RDRS|PURGE_RELAYS|PURGE_PROTOS); 92 93 if ((pw = getpwnam(RELAYD_USER)) == NULL) 94 fatal("hce: getpwnam"); 95 96 #ifndef DEBUG 97 if (chroot(pw->pw_dir) == -1) 98 fatal("hce: chroot"); 99 if (chdir("/") == -1) 100 fatal("hce: chdir(\"/\")"); 101 #else 102 #warning disabling privilege revocation and chroot in DEBUG mode 103 #endif 104 105 setproctitle("host check engine"); 106 relayd_process = PROC_HCE; 107 108 /* this is needed for icmp tests */ 109 icmp_init(env); 110 111 #ifndef DEBUG 112 if (setgroups(1, &pw->pw_gid) || 113 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 114 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 115 fatal("hce: can't drop privileges"); 116 #endif 117 118 event_init(); 119 120 if ((ibuf_pfe = calloc(1, sizeof(struct imsgbuf))) == NULL || 121 (ibuf_main = calloc(1, sizeof(struct imsgbuf))) == NULL) 122 fatal("hce"); 123 imsg_init(ibuf_pfe, pipe_pfe2hce[0], hce_dispatch_imsg); 124 imsg_init(ibuf_main, pipe_parent2hce[1], hce_dispatch_parent); 125 126 ibuf_pfe->events = EV_READ; 127 event_set(&ibuf_pfe->ev, ibuf_pfe->fd, ibuf_pfe->events, 128 ibuf_pfe->handler, ibuf_pfe); 129 event_add(&ibuf_pfe->ev, NULL); 130 131 ibuf_main->events = EV_READ; 132 event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events, 133 ibuf_main->handler, ibuf_main); 134 event_add(&ibuf_main->ev, NULL); 135 136 signal_set(&ev_sigint, SIGINT, hce_sig_handler, NULL); 137 signal_set(&ev_sigterm, SIGTERM, hce_sig_handler, NULL); 138 signal_add(&ev_sigint, NULL); 139 signal_add(&ev_sigterm, NULL); 140 signal(SIGPIPE, SIG_IGN); 141 signal(SIGHUP, SIG_IGN); 142 143 /* setup pipes */ 144 close(pipe_pfe2hce[1]); 145 close(pipe_parent2hce[0]); 146 close(pipe_parent2pfe[0]); 147 close(pipe_parent2pfe[1]); 148 for (i = 0; i < env->sc_prefork_relay; i++) { 149 close(pipe_parent2relay[i][0]); 150 close(pipe_parent2relay[i][1]); 151 close(pipe_pfe2relay[i][0]); 152 close(pipe_pfe2relay[i][1]); 153 } 154 155 hce_setup_events(); 156 event_dispatch(); 157 hce_shutdown(); 158 159 return (0); 160 } 161 162 void 163 hce_setup_events(void) 164 { 165 struct timeval tv; 166 struct table *table; 167 168 snmp_init(env, ibuf_main); 169 170 if (!TAILQ_EMPTY(env->sc_tables)) { 171 evtimer_set(&env->sc_ev, hce_launch_checks, env); 172 bzero(&tv, sizeof(tv)); 173 evtimer_add(&env->sc_ev, &tv); 174 } 175 176 if (env->sc_flags & F_SSL) { 177 ssl_init(env); 178 TAILQ_FOREACH(table, env->sc_tables, entry) { 179 if (!(table->conf.flags & F_SSL)) 180 continue; 181 table->ssl_ctx = ssl_ctx_create(env); 182 } 183 } 184 } 185 186 void 187 hce_disable_events(void) 188 { 189 struct table *table; 190 struct host *host; 191 192 evtimer_del(&env->sc_ev); 193 TAILQ_FOREACH(table, env->sc_tables, entry) { 194 TAILQ_FOREACH(host, &table->hosts, entry) { 195 host->he = HCE_ABORT; 196 event_del(&host->cte.ev); 197 close(host->cte.s); 198 } 199 } 200 if (env->sc_has_icmp) { 201 event_del(&env->sc_icmp_send.ev); 202 event_del(&env->sc_icmp_recv.ev); 203 } 204 if (env->sc_has_icmp6) { 205 event_del(&env->sc_icmp6_send.ev); 206 event_del(&env->sc_icmp6_recv.ev); 207 } 208 } 209 210 void 211 hce_launch_checks(int fd, short event, void *arg) 212 { 213 struct host *host; 214 struct table *table; 215 struct timeval tv; 216 217 /* 218 * notify pfe checks are done and schedule next check 219 */ 220 imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, -1, NULL, 0); 221 TAILQ_FOREACH(table, env->sc_tables, entry) { 222 TAILQ_FOREACH(host, &table->hosts, entry) { 223 if ((host->flags & F_CHECK_DONE) == 0) 224 host->he = HCE_INTERVAL_TIMEOUT; 225 host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 226 event_del(&host->cte.ev); 227 } 228 } 229 230 if (gettimeofday(&tv, NULL) == -1) 231 fatal("hce_launch_checks: gettimeofday"); 232 233 TAILQ_FOREACH(table, env->sc_tables, entry) { 234 if (table->conf.flags & F_DISABLE) 235 continue; 236 if (table->conf.skip_cnt) { 237 if (table->skipped++ > table->conf.skip_cnt) 238 table->skipped = 0; 239 if (table->skipped != 1) 240 continue; 241 } 242 if (table->conf.check == CHECK_NOCHECK) 243 fatalx("hce_launch_checks: unknown check type"); 244 245 TAILQ_FOREACH(host, &table->hosts, entry) { 246 if (host->flags & F_DISABLE || host->conf.parentid) 247 continue; 248 switch (table->conf.check) { 249 case CHECK_ICMP: 250 schedule_icmp(env, host); 251 break; 252 case CHECK_SCRIPT: 253 check_script(host); 254 break; 255 default: 256 /* Any other TCP-style checks */ 257 bzero(&host->cte, sizeof(host->cte)); 258 host->last_up = host->up; 259 host->cte.host = host; 260 host->cte.table = table; 261 bcopy(&tv, &host->cte.tv_start, 262 sizeof(host->cte.tv_start)); 263 check_tcp(&host->cte); 264 break; 265 } 266 } 267 } 268 check_icmp(env, &tv); 269 270 bcopy(&env->sc_interval, &tv, sizeof(tv)); 271 evtimer_add(&env->sc_ev, &tv); 272 } 273 274 void 275 hce_notify_done(struct host *host, enum host_error he) 276 { 277 struct table *table; 278 struct ctl_status st; 279 struct timeval tv_now, tv_dur; 280 u_long duration; 281 u_int logopt; 282 struct host *h; 283 int hostup; 284 const char *msg; 285 286 hostup = host->up; 287 host->he = he; 288 289 if (host->up == HOST_DOWN && host->retry_cnt) { 290 log_debug("hce_notify_done: host %s retry %d", 291 host->conf.name, host->retry_cnt); 292 host->up = host->last_up; 293 host->retry_cnt--; 294 } else 295 host->retry_cnt = host->conf.retry; 296 if (host->up != HOST_UNKNOWN) { 297 host->check_cnt++; 298 if (host->up == HOST_UP) 299 host->up_cnt++; 300 } 301 st.id = host->conf.id; 302 st.up = host->up; 303 st.check_cnt = host->check_cnt; 304 st.retry_cnt = host->retry_cnt; 305 st.he = he; 306 host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 307 msg = host_error(he); 308 if (msg) 309 log_debug("hce_notify_done: %s (%s)", host->conf.name, msg); 310 311 imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0, -1, &st, sizeof(st)); 312 if (host->up != host->last_up) 313 logopt = RELAYD_OPT_LOGUPDATE; 314 else 315 logopt = RELAYD_OPT_LOGNOTIFY; 316 317 if (gettimeofday(&tv_now, NULL) == -1) 318 fatal("hce_notify_done: gettimeofday"); 319 timersub(&tv_now, &host->cte.tv_start, &tv_dur); 320 if (timercmp(&host->cte.tv_start, &tv_dur, >)) 321 duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0); 322 else 323 duration = 0; 324 325 if ((table = table_find(env, host->conf.tableid)) == NULL) 326 fatalx("hce_notify_done: invalid table id"); 327 328 if (env->sc_opts & logopt) { 329 log_info("host %s, check %s%s (%lums), state %s -> %s, " 330 "availability %s", 331 host->conf.name, table_check(table->conf.check), 332 (table->conf.flags & F_SSL) ? " use ssl" : "", duration, 333 host_status(host->last_up), host_status(host->up), 334 print_availability(host->check_cnt, host->up_cnt)); 335 } 336 337 if (host->last_up != host->up) 338 snmp_hosttrap(table, host); 339 340 host->last_up = host->up; 341 342 if (SLIST_EMPTY(&host->children)) 343 return; 344 345 /* Notify for all other hosts that inherit the state from this one */ 346 SLIST_FOREACH(h, &host->children, child) { 347 h->up = hostup; 348 hce_notify_done(h, he); 349 } 350 } 351 352 void 353 hce_shutdown(void) 354 { 355 log_info("host check engine exiting"); 356 _exit(0); 357 } 358 359 void 360 hce_dispatch_imsg(int fd, short event, void *ptr) 361 { 362 struct imsgbuf *ibuf; 363 struct imsg imsg; 364 ssize_t n; 365 objid_t id; 366 struct host *host; 367 struct table *table; 368 369 ibuf = ptr; 370 switch (event) { 371 case EV_READ: 372 if ((n = imsg_read(ibuf)) == -1) 373 fatal("hce_dispatch_imsg: imsg_read_error"); 374 if (n == 0) { 375 /* this pipe is dead, so remove the event handler */ 376 event_del(&ibuf->ev); 377 event_loopexit(NULL); 378 return; 379 } 380 break; 381 case EV_WRITE: 382 if (msgbuf_write(&ibuf->w) == -1) 383 fatal("hce_dispatch_imsg: msgbuf_write"); 384 imsg_event_add(ibuf); 385 return; 386 default: 387 fatalx("hce_dispatch_imsg: unknown event"); 388 } 389 390 for (;;) { 391 if ((n = imsg_get(ibuf, &imsg)) == -1) 392 fatal("hce_dispatch_imsg: imsg_read error"); 393 if (n == 0) 394 break; 395 396 switch (imsg.hdr.type) { 397 case IMSG_HOST_DISABLE: 398 memcpy(&id, imsg.data, sizeof(id)); 399 if ((host = host_find(env, id)) == NULL) 400 fatalx("hce_dispatch_imsg: desynchronized"); 401 host->flags |= F_DISABLE; 402 host->up = HOST_UNKNOWN; 403 host->check_cnt = 0; 404 host->up_cnt = 0; 405 host->he = HCE_NONE; 406 break; 407 case IMSG_HOST_ENABLE: 408 memcpy(&id, imsg.data, sizeof(id)); 409 if ((host = host_find(env, id)) == NULL) 410 fatalx("hce_dispatch_imsg: desynchronized"); 411 host->flags &= ~(F_DISABLE); 412 host->up = HOST_UNKNOWN; 413 host->he = HCE_NONE; 414 break; 415 case IMSG_TABLE_DISABLE: 416 memcpy(&id, imsg.data, sizeof(id)); 417 if ((table = table_find(env, id)) == NULL) 418 fatalx("hce_dispatch_imsg: desynchronized"); 419 table->conf.flags |= F_DISABLE; 420 TAILQ_FOREACH(host, &table->hosts, entry) 421 host->up = HOST_UNKNOWN; 422 break; 423 case IMSG_TABLE_ENABLE: 424 memcpy(&id, imsg.data, sizeof(id)); 425 if ((table = table_find(env, id)) == NULL) 426 fatalx("hce_dispatch_imsg: desynchronized"); 427 table->conf.flags &= ~(F_DISABLE); 428 TAILQ_FOREACH(host, &table->hosts, entry) 429 host->up = HOST_UNKNOWN; 430 break; 431 case IMSG_CTL_POLL: 432 evtimer_del(&env->sc_ev); 433 TAILQ_FOREACH(table, env->sc_tables, entry) 434 table->skipped = 0; 435 hce_launch_checks(-1, EV_TIMEOUT, env); 436 break; 437 default: 438 log_debug("hce_dispatch_msg: unexpected imsg %d", 439 imsg.hdr.type); 440 break; 441 } 442 imsg_free(&imsg); 443 } 444 imsg_event_add(ibuf); 445 } 446 447 void 448 hce_dispatch_parent(int fd, short event, void * ptr) 449 { 450 struct imsgbuf *ibuf; 451 struct imsg imsg; 452 struct ctl_script scr; 453 ssize_t n; 454 size_t len; 455 static struct table *table = NULL; 456 struct host *host, *parent; 457 458 ibuf = ptr; 459 switch (event) { 460 case EV_READ: 461 if ((n = imsg_read(ibuf)) == -1) 462 fatal("hce_dispatch_parent: imsg_read error"); 463 if (n == 0) { 464 /* this pipe is dead, so remove the event handler */ 465 event_del(&ibuf->ev); 466 event_loopexit(NULL); 467 return; 468 } 469 break; 470 case EV_WRITE: 471 if (msgbuf_write(&ibuf->w) == -1) 472 fatal("hce_dispatch_parent: msgbuf_write"); 473 imsg_event_add(ibuf); 474 return; 475 default: 476 fatalx("hce_dispatch_parent: unknown event"); 477 } 478 479 for (;;) { 480 if ((n = imsg_get(ibuf, &imsg)) == -1) 481 fatal("hce_dispatch_parent: imsg_read error"); 482 if (n == 0) 483 break; 484 485 switch (imsg.hdr.type) { 486 case IMSG_SCRIPT: 487 if (imsg.hdr.len - IMSG_HEADER_SIZE != 488 sizeof(scr)) 489 fatalx("hce_dispatch_parent: " 490 "invalid size of script request"); 491 bcopy(imsg.data, &scr, sizeof(scr)); 492 script_done(env, &scr); 493 break; 494 case IMSG_RECONF: 495 log_debug("hce: reloading configuration"); 496 if (imsg.hdr.len != 497 sizeof(struct relayd) + IMSG_HEADER_SIZE) 498 fatalx("corrupted reload data"); 499 hce_disable_events(); 500 purge_config(env, PURGE_TABLES); 501 merge_config(env, (struct relayd *)imsg.data); 502 503 env->sc_tables = calloc(1, sizeof(*env->sc_tables)); 504 if (env->sc_tables == NULL) 505 fatal(NULL); 506 507 TAILQ_INIT(env->sc_tables); 508 break; 509 case IMSG_RECONF_TABLE: 510 if ((table = calloc(1, sizeof(*table))) == NULL) 511 fatal(NULL); 512 memcpy(&table->conf, imsg.data, sizeof(table->conf)); 513 TAILQ_INIT(&table->hosts); 514 TAILQ_INSERT_TAIL(env->sc_tables, table, entry); 515 break; 516 case IMSG_RECONF_SENDBUF: 517 len = imsg.hdr.len - IMSG_HEADER_SIZE; 518 table->sendbuf = calloc(1, len); 519 (void)strlcpy(table->sendbuf, (char *)imsg.data, len); 520 break; 521 case IMSG_RECONF_HOST: 522 if ((host = calloc(1, sizeof(*host))) == NULL) 523 fatal(NULL); 524 memcpy(&host->conf, imsg.data, sizeof(host->conf)); 525 host->tablename = table->conf.name; 526 TAILQ_INSERT_TAIL(&table->hosts, host, entry); 527 if (host->conf.parentid) { 528 parent = host_find(env, host->conf.parentid); 529 SLIST_INSERT_HEAD(&parent->children, 530 host, child); 531 } 532 break; 533 case IMSG_RECONF_END: 534 log_warnx("hce: configuration reloaded"); 535 hce_setup_events(); 536 break; 537 default: 538 log_debug("hce_dispatch_parent: unexpected imsg %d", 539 imsg.hdr.type); 540 break; 541 } 542 imsg_free(&imsg); 543 } 544 imsg_event_add(ibuf); 545 } 546