1*e8fb3979Spyr /* $OpenBSD: hce.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ 2feb9ff76Sreyk 3feb9ff76Sreyk /* 4feb9ff76Sreyk * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> 5feb9ff76Sreyk * 6feb9ff76Sreyk * Permission to use, copy, modify, and distribute this software for any 7feb9ff76Sreyk * purpose with or without fee is hereby granted, provided that the above 8feb9ff76Sreyk * copyright notice and this permission notice appear in all copies. 9feb9ff76Sreyk * 10feb9ff76Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11feb9ff76Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12feb9ff76Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13feb9ff76Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14feb9ff76Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15feb9ff76Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16feb9ff76Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17feb9ff76Sreyk */ 18feb9ff76Sreyk 19feb9ff76Sreyk #include <sys/queue.h> 20feb9ff76Sreyk #include <sys/param.h> 21feb9ff76Sreyk #include <sys/types.h> 22feb9ff76Sreyk #include <sys/time.h> 23feb9ff76Sreyk #include <sys/stat.h> 24feb9ff76Sreyk #include <sys/socket.h> 25feb9ff76Sreyk #include <sys/un.h> 26feb9ff76Sreyk #include <netinet/in_systm.h> 27feb9ff76Sreyk #include <netinet/in.h> 28feb9ff76Sreyk #include <netinet/ip.h> 29feb9ff76Sreyk #include <net/if.h> 30feb9ff76Sreyk #include <errno.h> 31feb9ff76Sreyk #include <event.h> 32feb9ff76Sreyk #include <fcntl.h> 33feb9ff76Sreyk #include <stdlib.h> 34feb9ff76Sreyk #include <string.h> 35feb9ff76Sreyk #include <unistd.h> 36feb9ff76Sreyk #include <err.h> 37feb9ff76Sreyk #include <pwd.h> 38feb9ff76Sreyk 39*e8fb3979Spyr #include <openssl/ssl.h> 40*e8fb3979Spyr 41ee9f3836Sderaadt #include "hoststated.h" 42feb9ff76Sreyk 43feb9ff76Sreyk void hce_sig_handler(int sig, short, void *); 44feb9ff76Sreyk void hce_shutdown(void); 45feb9ff76Sreyk void hce_dispatch_imsg(int, short, void *); 46feb9ff76Sreyk void hce_dispatch_parent(int, short, void *); 47feb9ff76Sreyk void hce_launch_checks(int, short, void *); 487e351ffdSreyk int hce_checks_done(void); 49feb9ff76Sreyk 508f79868bSpyr static struct hoststated *env = NULL; 51feb9ff76Sreyk struct imsgbuf *ibuf_pfe; 52feb9ff76Sreyk struct imsgbuf *ibuf_main; 53feb9ff76Sreyk 54feb9ff76Sreyk void 55feb9ff76Sreyk hce_sig_handler(int sig, short event, void *arg) 56feb9ff76Sreyk { 57feb9ff76Sreyk switch (sig) { 58feb9ff76Sreyk case SIGINT: 59feb9ff76Sreyk case SIGTERM: 60feb9ff76Sreyk hce_shutdown(); 61feb9ff76Sreyk default: 62feb9ff76Sreyk fatalx("hce_sig_handler: unexpected signal"); 63feb9ff76Sreyk } 64feb9ff76Sreyk } 65feb9ff76Sreyk 66feb9ff76Sreyk pid_t 678f79868bSpyr hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], 68feb9ff76Sreyk int pipe_pfe2hce[2]) 69feb9ff76Sreyk { 70feb9ff76Sreyk pid_t pid; 71feb9ff76Sreyk struct passwd *pw; 72feb9ff76Sreyk struct timeval tv; 73feb9ff76Sreyk struct event ev_sigint; 74feb9ff76Sreyk struct event ev_sigterm; 75*e8fb3979Spyr struct table *table; 76feb9ff76Sreyk 77feb9ff76Sreyk switch (pid = fork()) { 78feb9ff76Sreyk case -1: 79feb9ff76Sreyk fatal("hce: cannot fork"); 80feb9ff76Sreyk case 0: 81feb9ff76Sreyk break; 82feb9ff76Sreyk default: 83feb9ff76Sreyk return (pid); 84feb9ff76Sreyk } 85feb9ff76Sreyk 86feb9ff76Sreyk env = x_env; 87feb9ff76Sreyk 888f79868bSpyr if ((pw = getpwnam(HOSTSTATED_USER)) == NULL) 89feb9ff76Sreyk fatal("hce: getpwnam"); 90feb9ff76Sreyk 91feb9ff76Sreyk if (chroot(pw->pw_dir) == -1) 92feb9ff76Sreyk fatal("hce: chroot"); 93feb9ff76Sreyk if (chdir("/") == -1) 94feb9ff76Sreyk fatal("hce: chdir(\"/\")"); 95feb9ff76Sreyk 96feb9ff76Sreyk setproctitle("host check engine"); 978f79868bSpyr hoststated_process = PROC_HCE; 98feb9ff76Sreyk 9901d85ec5Sreyk /* this is needed for icmp tests */ 10001d85ec5Sreyk icmp_init(env); 10101d85ec5Sreyk 102feb9ff76Sreyk if (setgroups(1, &pw->pw_gid) || 103feb9ff76Sreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 104feb9ff76Sreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 105feb9ff76Sreyk fatal("hce: can't drop privileges"); 106feb9ff76Sreyk 107feb9ff76Sreyk event_init(); 108feb9ff76Sreyk 109feb9ff76Sreyk signal_set(&ev_sigint, SIGINT, hce_sig_handler, NULL); 110feb9ff76Sreyk signal_set(&ev_sigterm, SIGTERM, hce_sig_handler, NULL); 111feb9ff76Sreyk signal_add(&ev_sigint, NULL); 112feb9ff76Sreyk signal_add(&ev_sigterm, NULL); 113d043a2b1Sclaudio signal(SIGPIPE, SIG_IGN); 114feb9ff76Sreyk 115feb9ff76Sreyk /* setup pipes */ 116feb9ff76Sreyk close(pipe_pfe2hce[1]); 117feb9ff76Sreyk close(pipe_parent2hce[0]); 118feb9ff76Sreyk close(pipe_parent2pfe[0]); 119feb9ff76Sreyk close(pipe_parent2pfe[1]); 120feb9ff76Sreyk 121feb9ff76Sreyk if ((ibuf_pfe = calloc(1, sizeof(struct imsgbuf))) == NULL || 122feb9ff76Sreyk (ibuf_main = calloc(1, sizeof(struct imsgbuf))) == NULL) 123feb9ff76Sreyk fatal("hce"); 124feb9ff76Sreyk imsg_init(ibuf_pfe, pipe_pfe2hce[0], hce_dispatch_imsg); 125feb9ff76Sreyk imsg_init(ibuf_main, pipe_parent2hce[1], hce_dispatch_parent); 126feb9ff76Sreyk 127feb9ff76Sreyk ibuf_pfe->events = EV_READ; 128feb9ff76Sreyk event_set(&ibuf_pfe->ev, ibuf_pfe->fd, ibuf_pfe->events, 129feb9ff76Sreyk ibuf_pfe->handler, ibuf_pfe); 130feb9ff76Sreyk event_add(&ibuf_pfe->ev, NULL); 131feb9ff76Sreyk 132feb9ff76Sreyk ibuf_main->events = EV_READ; 133feb9ff76Sreyk event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events, 134feb9ff76Sreyk ibuf_main->handler, ibuf_main); 135feb9ff76Sreyk event_add(&ibuf_main->ev, NULL); 136feb9ff76Sreyk 13701d85ec5Sreyk evtimer_set(&env->ev, hce_launch_checks, env); 13801d85ec5Sreyk bzero(&tv, sizeof(tv)); 139feb9ff76Sreyk evtimer_add(&env->ev, &tv); 140feb9ff76Sreyk 141*e8fb3979Spyr if (env->flags & F_SSL) { 142*e8fb3979Spyr ssl_init(env); 143*e8fb3979Spyr TAILQ_FOREACH(table, &env->tables, entry) { 144*e8fb3979Spyr if (!(table->flags & F_SSL)) 145*e8fb3979Spyr continue; 146*e8fb3979Spyr table->ssl_ctx = ssl_ctx_create(env); 147*e8fb3979Spyr } 148*e8fb3979Spyr } 149*e8fb3979Spyr 150feb9ff76Sreyk event_dispatch(); 151feb9ff76Sreyk 152feb9ff76Sreyk hce_shutdown(); 153feb9ff76Sreyk 154feb9ff76Sreyk return (0); 155feb9ff76Sreyk } 156feb9ff76Sreyk 157feb9ff76Sreyk void 158feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg) 159feb9ff76Sreyk { 160feb9ff76Sreyk struct host *host; 161feb9ff76Sreyk struct table *table; 16201d85ec5Sreyk struct timeval tv; 16301d85ec5Sreyk 16401d85ec5Sreyk log_debug("hce_launch_checks: scheduled"); 16501d85ec5Sreyk 16601d85ec5Sreyk /* 16701d85ec5Sreyk * notify pfe checks are done and schedule next check 16801d85ec5Sreyk */ 16901d85ec5Sreyk imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0); 17001d85ec5Sreyk TAILQ_FOREACH(table, &env->tables, entry) { 17101d85ec5Sreyk TAILQ_FOREACH(host, &table->hosts, entry) { 17201d85ec5Sreyk host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 17301d85ec5Sreyk event_del(&host->cte.ev); 17401d85ec5Sreyk } 17501d85ec5Sreyk } 17601d85ec5Sreyk 17701d85ec5Sreyk if (gettimeofday(&tv, NULL)) 17801d85ec5Sreyk fatal("hce_launch_checks: gettimeofday"); 179feb9ff76Sreyk 1807e351ffdSreyk TAILQ_FOREACH(table, &env->tables, entry) { 1817e351ffdSreyk if (table->flags & F_DISABLE) 1827e351ffdSreyk continue; 1837e351ffdSreyk if (table->check == CHECK_NOCHECK) 1847e351ffdSreyk fatalx("hce_launch_checks: unknown check type"); 18501d85ec5Sreyk 1867e351ffdSreyk TAILQ_FOREACH(host, &table->hosts, entry) { 1877e351ffdSreyk if (host->flags & F_DISABLE) 1887e351ffdSreyk continue; 18901d85ec5Sreyk if (table->check == CHECK_ICMP) { 19001d85ec5Sreyk schedule_icmp(env, host); 19101d85ec5Sreyk continue; 19201d85ec5Sreyk } 19301d85ec5Sreyk 19401d85ec5Sreyk /* Any other TCP-style checks */ 1957e351ffdSreyk bzero(&host->cte, sizeof(host->cte)); 1967e351ffdSreyk host->last_up = host->up; 1977e351ffdSreyk host->cte.host = host; 1987e351ffdSreyk host->cte.table = table; 19901d85ec5Sreyk bcopy(&tv, &host->cte.tv_start, 20001d85ec5Sreyk sizeof(host->cte.tv_start)); 2017e351ffdSreyk check_tcp(&host->cte); 2027e351ffdSreyk } 2037e351ffdSreyk } 20401d85ec5Sreyk check_icmp(env, &tv); 20501d85ec5Sreyk 20601d85ec5Sreyk bcopy(&env->interval, &tv, sizeof(tv)); 20701d85ec5Sreyk evtimer_add(&env->ev, &tv); 2087e351ffdSreyk } 2097e351ffdSreyk 2107e351ffdSreyk int 2117e351ffdSreyk hce_checks_done() 2127e351ffdSreyk { 2137e351ffdSreyk struct table *table; 2147e351ffdSreyk struct host *host; 2157e351ffdSreyk 216feb9ff76Sreyk TAILQ_FOREACH(table, &env->tables, entry) { 217feb9ff76Sreyk if (table->flags & F_DISABLE) 218feb9ff76Sreyk continue; 219feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) { 220feb9ff76Sreyk if (host->flags & F_DISABLE) 221feb9ff76Sreyk continue; 2227e351ffdSreyk if (!(host->flags & F_CHECK_DONE)) 2237e351ffdSreyk return (0); 224feb9ff76Sreyk } 2257e351ffdSreyk } 2267e351ffdSreyk return (1); 2277e351ffdSreyk } 2287e351ffdSreyk 2297e351ffdSreyk void 2307e351ffdSreyk hce_notify_done(struct host *host, const char *msg) 2317e351ffdSreyk { 2327e351ffdSreyk struct ctl_status st; 2337e351ffdSreyk 234feb9ff76Sreyk st.id = host->id; 235feb9ff76Sreyk st.up = host->up; 23601d85ec5Sreyk host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 2377e351ffdSreyk if (msg) 23801d85ec5Sreyk log_debug("hce_notify_done: %s (%s)", host->name, msg); 2397e351ffdSreyk if (host->up != host->last_up) { 2407e351ffdSreyk imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0, &st, sizeof(st)); 2417e351ffdSreyk host->last_up = host->up; 242feb9ff76Sreyk } 243feb9ff76Sreyk } 244feb9ff76Sreyk 245feb9ff76Sreyk void 246feb9ff76Sreyk hce_shutdown(void) 247feb9ff76Sreyk { 248feb9ff76Sreyk log_info("host check engine exiting"); 249feb9ff76Sreyk _exit(0); 250feb9ff76Sreyk } 251feb9ff76Sreyk 252feb9ff76Sreyk void 253feb9ff76Sreyk hce_dispatch_imsg(int fd, short event, void *ptr) 254feb9ff76Sreyk { 255feb9ff76Sreyk struct imsgbuf *ibuf; 256feb9ff76Sreyk struct imsg imsg; 257feb9ff76Sreyk ssize_t n; 258feb9ff76Sreyk objid_t id; 259feb9ff76Sreyk struct host *host; 260feb9ff76Sreyk struct table *table; 261feb9ff76Sreyk 262feb9ff76Sreyk ibuf = ptr; 263feb9ff76Sreyk switch (event) { 264feb9ff76Sreyk case EV_READ: 265feb9ff76Sreyk if ((n = imsg_read(ibuf)) == -1) 266feb9ff76Sreyk fatal("hce_dispatch_imsg: imsg_read_error"); 267feb9ff76Sreyk if (n == 0) 268feb9ff76Sreyk fatalx("hce_dispatch_imsg: pipe closed"); 269feb9ff76Sreyk break; 270feb9ff76Sreyk case EV_WRITE: 271feb9ff76Sreyk if (msgbuf_write(&ibuf->w) == -1) 272feb9ff76Sreyk fatal("hce_dispatch_imsg: msgbuf_write"); 273feb9ff76Sreyk imsg_event_add(ibuf); 274feb9ff76Sreyk return; 275feb9ff76Sreyk default: 276feb9ff76Sreyk fatalx("hce_dispatch_imsg: unknown event"); 277feb9ff76Sreyk } 278feb9ff76Sreyk 279feb9ff76Sreyk for (;;) { 280feb9ff76Sreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 281feb9ff76Sreyk fatal("hce_dispatch_imsg: imsg_read error"); 282feb9ff76Sreyk if (n == 0) 283feb9ff76Sreyk break; 284feb9ff76Sreyk 285feb9ff76Sreyk switch (imsg.hdr.type) { 286feb9ff76Sreyk case IMSG_HOST_DISABLE: 287feb9ff76Sreyk memcpy(&id, imsg.data, sizeof(id)); 288feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 289feb9ff76Sreyk fatalx("hce_dispatch_imsg: desynchronized"); 290feb9ff76Sreyk host->flags |= F_DISABLE; 291feb9ff76Sreyk host->up = HOST_UNKNOWN; 292feb9ff76Sreyk break; 293feb9ff76Sreyk case IMSG_HOST_ENABLE: 294feb9ff76Sreyk memcpy(&id, imsg.data, sizeof(id)); 295feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 296feb9ff76Sreyk fatalx("hce_dispatch_imsg: desynchronized"); 297feb9ff76Sreyk host->flags &= ~(F_DISABLE); 298feb9ff76Sreyk host->up = HOST_UNKNOWN; 299feb9ff76Sreyk break; 300feb9ff76Sreyk case IMSG_TABLE_DISABLE: 301feb9ff76Sreyk memcpy(&id, imsg.data, sizeof(id)); 302feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 303feb9ff76Sreyk fatalx("hce_dispatch_imsg: desynchronized"); 304feb9ff76Sreyk table->flags |= F_DISABLE; 305feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 306feb9ff76Sreyk host->up = HOST_UNKNOWN; 307feb9ff76Sreyk break; 308feb9ff76Sreyk case IMSG_TABLE_ENABLE: 309feb9ff76Sreyk memcpy(&id, imsg.data, sizeof(id)); 310feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 311feb9ff76Sreyk fatalx("hce_dispatch_imsg: desynchronized"); 312feb9ff76Sreyk table->flags &= ~(F_DISABLE); 313feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 314feb9ff76Sreyk host->up = HOST_UNKNOWN; 315feb9ff76Sreyk break; 316feb9ff76Sreyk default: 317feb9ff76Sreyk log_debug("hce_dispatch_msg: unexpected imsg %d", 318feb9ff76Sreyk imsg.hdr.type); 319feb9ff76Sreyk break; 320feb9ff76Sreyk } 321feb9ff76Sreyk imsg_free(&imsg); 322feb9ff76Sreyk } 323feb9ff76Sreyk imsg_event_add(ibuf); 324feb9ff76Sreyk } 325feb9ff76Sreyk 326feb9ff76Sreyk void 327feb9ff76Sreyk hce_dispatch_parent(int fd, short event, void * ptr) 328feb9ff76Sreyk { 329feb9ff76Sreyk struct imsgbuf *ibuf; 330feb9ff76Sreyk struct imsg imsg; 331feb9ff76Sreyk ssize_t n; 332feb9ff76Sreyk 333feb9ff76Sreyk ibuf = ptr; 334feb9ff76Sreyk switch (event) { 335feb9ff76Sreyk case EV_READ: 336feb9ff76Sreyk if ((n = imsg_read(ibuf)) == -1) 337feb9ff76Sreyk fatal("hce_dispatch_parent: imsg_read error"); 338feb9ff76Sreyk if (n == 0) /* connection closed */ 339feb9ff76Sreyk fatalx("hce_dispatch_parent: pipe closed"); 340feb9ff76Sreyk break; 341feb9ff76Sreyk case EV_WRITE: 342feb9ff76Sreyk if (msgbuf_write(&ibuf->w) == -1) 343feb9ff76Sreyk fatal("hce_dispatch_parent: msgbuf_write"); 344feb9ff76Sreyk imsg_event_add(ibuf); 345feb9ff76Sreyk return; 346feb9ff76Sreyk default: 347feb9ff76Sreyk fatalx("hce_dispatch_parent: unknown event"); 348feb9ff76Sreyk } 349feb9ff76Sreyk 350feb9ff76Sreyk for (;;) { 351feb9ff76Sreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 352feb9ff76Sreyk fatal("hce_dispatch_parent: imsg_read error"); 353feb9ff76Sreyk if (n == 0) 354feb9ff76Sreyk break; 355feb9ff76Sreyk 356feb9ff76Sreyk switch (imsg.hdr.type) { 357feb9ff76Sreyk default: 358feb9ff76Sreyk log_debug("hce_dispatch_parent: unexpected imsg %d", 359feb9ff76Sreyk imsg.hdr.type); 360feb9ff76Sreyk break; 361feb9ff76Sreyk } 362feb9ff76Sreyk imsg_free(&imsg); 363feb9ff76Sreyk } 364feb9ff76Sreyk } 365