1*6e07057bSblambert /* $OpenBSD: hce.c,v 1.66 2014/11/19 10:24:40 blambert Exp $ */ 2feb9ff76Sreyk 3feb9ff76Sreyk /* 436f5dc5eSpyr * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.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/param.h> 200ca734d7Sreyk #include <sys/queue.h> 21feb9ff76Sreyk #include <sys/time.h> 22feb9ff76Sreyk #include <sys/stat.h> 23feb9ff76Sreyk #include <sys/socket.h> 24feb9ff76Sreyk #include <sys/un.h> 250ca734d7Sreyk 260ca734d7Sreyk #include <net/if.h> 27feb9ff76Sreyk #include <netinet/in.h> 28feb9ff76Sreyk #include <netinet/ip.h> 290ca734d7Sreyk 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 39e8fb3979Spyr #include <openssl/ssl.h> 40e8fb3979Spyr 41748ceb64Sreyk #include "relayd.h" 42feb9ff76Sreyk 430325c666Sreyk void hce_init(struct privsep *, struct privsep_proc *p, void *); 44feb9ff76Sreyk void hce_sig_handler(int sig, short, void *); 45feb9ff76Sreyk void hce_launch_checks(int, short, void *); 46641b8bafSpyr void hce_setup_events(void); 47641b8bafSpyr void hce_disable_events(void); 48feb9ff76Sreyk 490325c666Sreyk int hce_dispatch_parent(int, struct privsep_proc *, struct imsg *); 500325c666Sreyk int hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *); 51*6e07057bSblambert int hce_dispatch_relay(int, struct privsep_proc *, struct imsg *); 520325c666Sreyk 53748ceb64Sreyk static struct relayd *env = NULL; 54780e8f79Spyr int running = 0; 55feb9ff76Sreyk 560325c666Sreyk static struct privsep_proc procs[] = { 570325c666Sreyk { "parent", PROC_PARENT, hce_dispatch_parent }, 580325c666Sreyk { "pfe", PROC_PFE, hce_dispatch_pfe }, 59*6e07057bSblambert { "relay", PROC_RELAY, hce_dispatch_relay }, 600325c666Sreyk }; 61feb9ff76Sreyk 62feb9ff76Sreyk pid_t 630325c666Sreyk hce(struct privsep *ps, struct privsep_proc *p) 64feb9ff76Sreyk { 650325c666Sreyk env = ps->ps_env; 66feb9ff76Sreyk 6701d85ec5Sreyk /* this is needed for icmp tests */ 6801d85ec5Sreyk icmp_init(env); 6901d85ec5Sreyk 700325c666Sreyk return (proc_run(ps, p, procs, nitems(procs), hce_init, NULL)); 710325c666Sreyk } 72feb9ff76Sreyk 730325c666Sreyk void 740325c666Sreyk hce_init(struct privsep *ps, struct privsep_proc *p, void *arg) 750325c666Sreyk { 76a2195becSreyk if (config_init(ps->ps_env) == -1) 77a2195becSreyk fatal("failed to initialize configuration"); 780325c666Sreyk 790325c666Sreyk env->sc_id = getpid() & 0xffff; 80feb9ff76Sreyk 81bb8fa3feSreyk /* Allow maximum available sockets for TCP checks */ 82bb8fa3feSreyk socket_rlimit(-1); 83780e8f79Spyr } 84780e8f79Spyr 85780e8f79Spyr void 86641b8bafSpyr hce_setup_events(void) 87780e8f79Spyr { 88780e8f79Spyr struct timeval tv; 89780e8f79Spyr struct table *table; 90780e8f79Spyr 91a2195becSreyk if (!(TAILQ_EMPTY(env->sc_tables) || 92a2195becSreyk event_initialized(&env->sc_ev))) { 9335d10c30Sreyk evtimer_set(&env->sc_ev, hce_launch_checks, env); 9401d85ec5Sreyk bzero(&tv, sizeof(tv)); 9535d10c30Sreyk evtimer_add(&env->sc_ev, &tv); 962edd718bSreyk } 97feb9ff76Sreyk 9835d10c30Sreyk if (env->sc_flags & F_SSL) { 9935d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 100a2195becSreyk if (!(table->conf.flags & F_SSL) || 101a2195becSreyk table->ssl_ctx != NULL) 102e8fb3979Spyr continue; 103e8fb3979Spyr table->ssl_ctx = ssl_ctx_create(env); 104e8fb3979Spyr } 105e8fb3979Spyr } 106641b8bafSpyr } 107641b8bafSpyr 108641b8bafSpyr void 109641b8bafSpyr hce_disable_events(void) 110641b8bafSpyr { 111641b8bafSpyr struct table *table; 112641b8bafSpyr struct host *host; 113641b8bafSpyr 11435d10c30Sreyk evtimer_del(&env->sc_ev); 11535d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 116641b8bafSpyr TAILQ_FOREACH(host, &table->hosts, entry) { 117c0dc99f6Sreyk host->he = HCE_ABORT; 118a2195becSreyk if (event_initialized(&host->cte.ev)) { 119641b8bafSpyr event_del(&host->cte.ev); 120641b8bafSpyr close(host->cte.s); 121641b8bafSpyr } 122641b8bafSpyr } 123a2195becSreyk } 12435d10c30Sreyk if (env->sc_has_icmp) { 12535d10c30Sreyk event_del(&env->sc_icmp_send.ev); 12635d10c30Sreyk event_del(&env->sc_icmp_recv.ev); 127641b8bafSpyr } 12835d10c30Sreyk if (env->sc_has_icmp6) { 12935d10c30Sreyk event_del(&env->sc_icmp6_send.ev); 13035d10c30Sreyk event_del(&env->sc_icmp6_recv.ev); 131641b8bafSpyr } 132feb9ff76Sreyk } 133feb9ff76Sreyk 134feb9ff76Sreyk void 135feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg) 136feb9ff76Sreyk { 137feb9ff76Sreyk struct host *host; 138feb9ff76Sreyk struct table *table; 13901d85ec5Sreyk struct timeval tv; 14001d85ec5Sreyk 14101d85ec5Sreyk /* 14201d85ec5Sreyk * notify pfe checks are done and schedule next check 14301d85ec5Sreyk */ 1440325c666Sreyk proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_SYNC, -1, NULL, 0); 14535d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 14601d85ec5Sreyk TAILQ_FOREACH(host, &table->hosts, entry) { 147c0dc99f6Sreyk if ((host->flags & F_CHECK_DONE) == 0) 148c0dc99f6Sreyk host->he = HCE_INTERVAL_TIMEOUT; 14901d85ec5Sreyk host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 150a2195becSreyk if (event_initialized(&host->cte.ev)) { 15101d85ec5Sreyk event_del(&host->cte.ev); 152a2195becSreyk close(host->cte.s); 153a2195becSreyk } 154a2195becSreyk host->cte.s = -1; 15501d85ec5Sreyk } 15601d85ec5Sreyk } 15701d85ec5Sreyk 158fd1841a3Sreyk getmonotime(&tv); 159feb9ff76Sreyk 16035d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 16168b79041Spyr if (table->conf.flags & F_DISABLE) 1627e351ffdSreyk continue; 16318de159aSpyr if (table->conf.skip_cnt) { 16418de159aSpyr if (table->skipped++ > table->conf.skip_cnt) 16518de159aSpyr table->skipped = 0; 16618de159aSpyr if (table->skipped != 1) 16718de159aSpyr continue; 16818de159aSpyr } 16968b79041Spyr if (table->conf.check == CHECK_NOCHECK) 1707e351ffdSreyk fatalx("hce_launch_checks: unknown check type"); 17101d85ec5Sreyk 1727e351ffdSreyk TAILQ_FOREACH(host, &table->hosts, entry) { 173c723f8edSreyk if (host->flags & F_DISABLE || host->conf.parentid) 1747e351ffdSreyk continue; 175a7da800aSsthen bcopy(&tv, &host->cte.tv_start, 176a7da800aSsthen sizeof(host->cte.tv_start)); 1774156152fSreyk switch (table->conf.check) { 1784156152fSreyk case CHECK_ICMP: 17901d85ec5Sreyk schedule_icmp(env, host); 1804156152fSreyk break; 1814156152fSreyk case CHECK_SCRIPT: 1820325c666Sreyk check_script(env, host); 1834156152fSreyk break; 1844156152fSreyk default: 18501d85ec5Sreyk /* Any other TCP-style checks */ 1867e351ffdSreyk host->last_up = host->up; 1877e351ffdSreyk host->cte.host = host; 1887e351ffdSreyk host->cte.table = table; 1897e351ffdSreyk check_tcp(&host->cte); 1904156152fSreyk break; 1914156152fSreyk } 1927e351ffdSreyk } 1937e351ffdSreyk } 19401d85ec5Sreyk check_icmp(env, &tv); 19501d85ec5Sreyk 19635d10c30Sreyk bcopy(&env->sc_interval, &tv, sizeof(tv)); 19735d10c30Sreyk evtimer_add(&env->sc_ev, &tv); 1987e351ffdSreyk } 1997e351ffdSreyk 2007e351ffdSreyk void 201c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he) 2027e351ffdSreyk { 203609cf3a7Sreyk struct table *table; 2047e351ffdSreyk struct ctl_status st; 205609cf3a7Sreyk struct timeval tv_now, tv_dur; 206609cf3a7Sreyk u_long duration; 207609cf3a7Sreyk u_int logopt; 20874a0367eSgiovanni struct host *h, *hostnst; 209c723f8edSreyk int hostup; 210c0dc99f6Sreyk const char *msg; 211c723f8edSreyk 21274a0367eSgiovanni if ((hostnst = host_find(env, host->conf.id)) == NULL) 21374a0367eSgiovanni fatalx("hce_notify_done: desynchronized"); 21474a0367eSgiovanni 21574a0367eSgiovanni if ((table = table_find(env, host->conf.tableid)) == NULL) 21674a0367eSgiovanni fatalx("hce_notify_done: invalid table id"); 21774a0367eSgiovanni 21874a0367eSgiovanni if (hostnst->flags & F_DISABLE) { 21974a0367eSgiovanni if (env->sc_opts & RELAYD_OPT_LOGUPDATE) { 22074a0367eSgiovanni log_info("host %s, check %s%s (ignoring result, " 22174a0367eSgiovanni "host disabled)", 22274a0367eSgiovanni host->conf.name, table_check(table->conf.check), 22374a0367eSgiovanni (table->conf.flags & F_SSL) ? " use ssl" : ""); 22474a0367eSgiovanni } 22574a0367eSgiovanni host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 22674a0367eSgiovanni return; 22774a0367eSgiovanni } 22874a0367eSgiovanni 229c723f8edSreyk hostup = host->up; 230c0dc99f6Sreyk host->he = he; 2317e351ffdSreyk 2322edd718bSreyk if (host->up == HOST_DOWN && host->retry_cnt) { 23385a8c65fSreyk log_debug("%s: host %s retry %d", __func__, 23468b79041Spyr host->conf.name, host->retry_cnt); 235bd2508bcSreyk host->up = host->last_up; 2362edd718bSreyk host->retry_cnt--; 2372edd718bSreyk } else 23868b79041Spyr host->retry_cnt = host->conf.retry; 2392edd718bSreyk if (host->up != HOST_UNKNOWN) { 2402edd718bSreyk host->check_cnt++; 2412edd718bSreyk if (host->up == HOST_UP) 2422edd718bSreyk host->up_cnt++; 2432edd718bSreyk } 24468b79041Spyr st.id = host->conf.id; 245feb9ff76Sreyk st.up = host->up; 2462edd718bSreyk st.check_cnt = host->check_cnt; 2472edd718bSreyk st.retry_cnt = host->retry_cnt; 248c0dc99f6Sreyk st.he = he; 24901d85ec5Sreyk host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 250c0dc99f6Sreyk msg = host_error(he); 2517e351ffdSreyk if (msg) 25285a8c65fSreyk log_debug("%s: %s (%s)", __func__, host->conf.name, msg); 253609cf3a7Sreyk 2540325c666Sreyk proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS, 2550325c666Sreyk -1, &st, sizeof(st)); 2562edd718bSreyk if (host->up != host->last_up) 257748ceb64Sreyk logopt = RELAYD_OPT_LOGUPDATE; 2582edd718bSreyk else 259748ceb64Sreyk logopt = RELAYD_OPT_LOGNOTIFY; 260609cf3a7Sreyk 261fd1841a3Sreyk getmonotime(&tv_now); 262609cf3a7Sreyk timersub(&tv_now, &host->cte.tv_start, &tv_dur); 263609cf3a7Sreyk if (timercmp(&host->cte.tv_start, &tv_dur, >)) 264609cf3a7Sreyk duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0); 265609cf3a7Sreyk else 266609cf3a7Sreyk duration = 0; 267609cf3a7Sreyk 26835d10c30Sreyk if (env->sc_opts & logopt) { 2692edd718bSreyk log_info("host %s, check %s%s (%lums), state %s -> %s, " 2702edd718bSreyk "availability %s", 27168b79041Spyr host->conf.name, table_check(table->conf.check), 27268b79041Spyr (table->conf.flags & F_SSL) ? " use ssl" : "", duration, 2732edd718bSreyk host_status(host->last_up), host_status(host->up), 2742edd718bSreyk print_availability(host->check_cnt, host->up_cnt)); 275feb9ff76Sreyk } 276fe250497Sreyk 277609cf3a7Sreyk host->last_up = host->up; 278c723f8edSreyk 2791584e35dSreyk if (SLIST_EMPTY(&host->children)) 280c723f8edSreyk return; 281c723f8edSreyk 282c723f8edSreyk /* Notify for all other hosts that inherit the state from this one */ 2831584e35dSreyk SLIST_FOREACH(h, &host->children, child) { 284c723f8edSreyk h->up = hostup; 285c0dc99f6Sreyk hce_notify_done(h, he); 286c723f8edSreyk } 287feb9ff76Sreyk } 288feb9ff76Sreyk 2890325c666Sreyk int 2900325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg) 291feb9ff76Sreyk { 292feb9ff76Sreyk objid_t id; 293feb9ff76Sreyk struct host *host; 294feb9ff76Sreyk struct table *table; 295feb9ff76Sreyk 2960325c666Sreyk switch (imsg->hdr.type) { 297feb9ff76Sreyk case IMSG_HOST_DISABLE: 2980325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 299feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 3004f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 301feb9ff76Sreyk host->flags |= F_DISABLE; 302feb9ff76Sreyk host->up = HOST_UNKNOWN; 3032edd718bSreyk host->check_cnt = 0; 3042edd718bSreyk host->up_cnt = 0; 305c0dc99f6Sreyk host->he = HCE_NONE; 306feb9ff76Sreyk break; 307feb9ff76Sreyk case IMSG_HOST_ENABLE: 3080325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 309feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 3104f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 311feb9ff76Sreyk host->flags &= ~(F_DISABLE); 312feb9ff76Sreyk host->up = HOST_UNKNOWN; 313c0dc99f6Sreyk host->he = HCE_NONE; 314feb9ff76Sreyk break; 315feb9ff76Sreyk case IMSG_TABLE_DISABLE: 3160325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 317feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 3184f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 31968b79041Spyr table->conf.flags |= F_DISABLE; 320feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 321feb9ff76Sreyk host->up = HOST_UNKNOWN; 322feb9ff76Sreyk break; 323feb9ff76Sreyk case IMSG_TABLE_ENABLE: 3240325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 325feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 3264f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 32768b79041Spyr table->conf.flags &= ~(F_DISABLE); 328feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 329feb9ff76Sreyk host->up = HOST_UNKNOWN; 330feb9ff76Sreyk break; 331cd65ce7bSpyr case IMSG_CTL_POLL: 33235d10c30Sreyk evtimer_del(&env->sc_ev); 33335d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) 334f98352d8Spyr table->skipped = 0; 335cd65ce7bSpyr hce_launch_checks(-1, EV_TIMEOUT, env); 336cd65ce7bSpyr break; 337feb9ff76Sreyk default: 3380325c666Sreyk return (-1); 339feb9ff76Sreyk } 340feb9ff76Sreyk 3410325c666Sreyk return (0); 3420325c666Sreyk } 3430325c666Sreyk 3440325c666Sreyk int 3450325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 346feb9ff76Sreyk { 3474156152fSreyk struct ctl_script scr; 348feb9ff76Sreyk 3490325c666Sreyk switch (imsg->hdr.type) { 3504156152fSreyk case IMSG_SCRIPT: 3510325c666Sreyk IMSG_SIZE_CHECK(imsg, &scr); 3520325c666Sreyk bcopy(imsg->data, &scr, sizeof(scr)); 3534156152fSreyk script_done(env, &scr); 3544156152fSreyk break; 355a2195becSreyk case IMSG_CFG_TABLE: 356a2195becSreyk config_gettable(env, imsg); 357adf98c11Spyr break; 358a2195becSreyk case IMSG_CFG_HOST: 359a2195becSreyk config_gethost(env, imsg); 360adf98c11Spyr break; 361a2195becSreyk case IMSG_CFG_DONE: 362a2195becSreyk config_getcfg(env, imsg); 3630dcba380Scamield break; 3640dcba380Scamield case IMSG_CTL_START: 365adf98c11Spyr hce_setup_events(); 366adf98c11Spyr break; 367a2195becSreyk case IMSG_CTL_RESET: 368a2195becSreyk config_getreset(env, imsg); 369a2195becSreyk break; 370feb9ff76Sreyk default: 3710325c666Sreyk return (-1); 372feb9ff76Sreyk } 3730325c666Sreyk 3740325c666Sreyk return (0); 375feb9ff76Sreyk } 376*6e07057bSblambert 377*6e07057bSblambert int 378*6e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg) 379*6e07057bSblambert { 380*6e07057bSblambert switch (imsg->hdr.type) { 381*6e07057bSblambert default: 382*6e07057bSblambert break; 383*6e07057bSblambert } 384*6e07057bSblambert 385*6e07057bSblambert return (-1); 386*6e07057bSblambert } 387