1*f04ff968Sreyk /* $OpenBSD: hce.c,v 1.69 2015/01/22 17:42:09 reyk 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 19*f04ff968Sreyk #include <sys/types.h> 200ca734d7Sreyk #include <sys/queue.h> 21feb9ff76Sreyk #include <sys/time.h> 22*f04ff968Sreyk #include <sys/uio.h> 230ca734d7Sreyk 24feb9ff76Sreyk #include <event.h> 25feb9ff76Sreyk #include <stdlib.h> 26feb9ff76Sreyk #include <string.h> 27feb9ff76Sreyk #include <unistd.h> 28*f04ff968Sreyk #include <imsg.h> 29e8fb3979Spyr 30748ceb64Sreyk #include "relayd.h" 31feb9ff76Sreyk 320325c666Sreyk void hce_init(struct privsep *, struct privsep_proc *p, void *); 33feb9ff76Sreyk void hce_sig_handler(int sig, short, void *); 34feb9ff76Sreyk void hce_launch_checks(int, short, void *); 35641b8bafSpyr void hce_setup_events(void); 36641b8bafSpyr void hce_disable_events(void); 37feb9ff76Sreyk 380325c666Sreyk int hce_dispatch_parent(int, struct privsep_proc *, struct imsg *); 390325c666Sreyk int hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *); 406e07057bSblambert int hce_dispatch_relay(int, struct privsep_proc *, struct imsg *); 410325c666Sreyk 42748ceb64Sreyk static struct relayd *env = NULL; 43780e8f79Spyr int running = 0; 44feb9ff76Sreyk 450325c666Sreyk static struct privsep_proc procs[] = { 460325c666Sreyk { "parent", PROC_PARENT, hce_dispatch_parent }, 470325c666Sreyk { "pfe", PROC_PFE, hce_dispatch_pfe }, 486e07057bSblambert { "relay", PROC_RELAY, hce_dispatch_relay }, 490325c666Sreyk }; 50feb9ff76Sreyk 51feb9ff76Sreyk pid_t 520325c666Sreyk hce(struct privsep *ps, struct privsep_proc *p) 53feb9ff76Sreyk { 540325c666Sreyk env = ps->ps_env; 55feb9ff76Sreyk 5601d85ec5Sreyk /* this is needed for icmp tests */ 5701d85ec5Sreyk icmp_init(env); 5801d85ec5Sreyk 590325c666Sreyk return (proc_run(ps, p, procs, nitems(procs), hce_init, NULL)); 600325c666Sreyk } 61feb9ff76Sreyk 620325c666Sreyk void 630325c666Sreyk hce_init(struct privsep *ps, struct privsep_proc *p, void *arg) 640325c666Sreyk { 65a2195becSreyk if (config_init(ps->ps_env) == -1) 66a2195becSreyk fatal("failed to initialize configuration"); 670325c666Sreyk 680325c666Sreyk env->sc_id = getpid() & 0xffff; 69feb9ff76Sreyk 70bb8fa3feSreyk /* Allow maximum available sockets for TCP checks */ 71bb8fa3feSreyk socket_rlimit(-1); 72780e8f79Spyr } 73780e8f79Spyr 74780e8f79Spyr void 75641b8bafSpyr hce_setup_events(void) 76780e8f79Spyr { 77780e8f79Spyr struct timeval tv; 78780e8f79Spyr struct table *table; 79780e8f79Spyr 80a2195becSreyk if (!(TAILQ_EMPTY(env->sc_tables) || 81a2195becSreyk event_initialized(&env->sc_ev))) { 8235d10c30Sreyk evtimer_set(&env->sc_ev, hce_launch_checks, env); 8301d85ec5Sreyk bzero(&tv, sizeof(tv)); 8435d10c30Sreyk evtimer_add(&env->sc_ev, &tv); 852edd718bSreyk } 86feb9ff76Sreyk 877bb52228Sreyk if (env->sc_flags & F_TLS) { 8835d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 897bb52228Sreyk if (!(table->conf.flags & F_TLS) || 90a2195becSreyk table->ssl_ctx != NULL) 91e8fb3979Spyr continue; 92e8fb3979Spyr table->ssl_ctx = ssl_ctx_create(env); 93e8fb3979Spyr } 94e8fb3979Spyr } 95641b8bafSpyr } 96641b8bafSpyr 97641b8bafSpyr void 98641b8bafSpyr hce_disable_events(void) 99641b8bafSpyr { 100641b8bafSpyr struct table *table; 101641b8bafSpyr struct host *host; 102641b8bafSpyr 10335d10c30Sreyk evtimer_del(&env->sc_ev); 10435d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 105641b8bafSpyr TAILQ_FOREACH(host, &table->hosts, entry) { 106c0dc99f6Sreyk host->he = HCE_ABORT; 107a2195becSreyk if (event_initialized(&host->cte.ev)) { 108641b8bafSpyr event_del(&host->cte.ev); 109641b8bafSpyr close(host->cte.s); 110641b8bafSpyr } 111641b8bafSpyr } 112a2195becSreyk } 11335d10c30Sreyk if (env->sc_has_icmp) { 11435d10c30Sreyk event_del(&env->sc_icmp_send.ev); 11535d10c30Sreyk event_del(&env->sc_icmp_recv.ev); 116641b8bafSpyr } 11735d10c30Sreyk if (env->sc_has_icmp6) { 11835d10c30Sreyk event_del(&env->sc_icmp6_send.ev); 11935d10c30Sreyk event_del(&env->sc_icmp6_recv.ev); 120641b8bafSpyr } 121feb9ff76Sreyk } 122feb9ff76Sreyk 123feb9ff76Sreyk void 124feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg) 125feb9ff76Sreyk { 126feb9ff76Sreyk struct host *host; 127feb9ff76Sreyk struct table *table; 12801d85ec5Sreyk struct timeval tv; 12901d85ec5Sreyk 13001d85ec5Sreyk /* 13101d85ec5Sreyk * notify pfe checks are done and schedule next check 13201d85ec5Sreyk */ 1330325c666Sreyk proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_SYNC, -1, NULL, 0); 13435d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 13501d85ec5Sreyk TAILQ_FOREACH(host, &table->hosts, entry) { 136c0dc99f6Sreyk if ((host->flags & F_CHECK_DONE) == 0) 137c0dc99f6Sreyk host->he = HCE_INTERVAL_TIMEOUT; 13801d85ec5Sreyk host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 139a2195becSreyk if (event_initialized(&host->cte.ev)) { 14001d85ec5Sreyk event_del(&host->cte.ev); 141a2195becSreyk close(host->cte.s); 142a2195becSreyk } 143a2195becSreyk host->cte.s = -1; 14401d85ec5Sreyk } 14501d85ec5Sreyk } 14601d85ec5Sreyk 147fd1841a3Sreyk getmonotime(&tv); 148feb9ff76Sreyk 14935d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 15068b79041Spyr if (table->conf.flags & F_DISABLE) 1517e351ffdSreyk continue; 15218de159aSpyr if (table->conf.skip_cnt) { 15318de159aSpyr if (table->skipped++ > table->conf.skip_cnt) 15418de159aSpyr table->skipped = 0; 15518de159aSpyr if (table->skipped != 1) 15618de159aSpyr continue; 15718de159aSpyr } 15868b79041Spyr if (table->conf.check == CHECK_NOCHECK) 1597e351ffdSreyk fatalx("hce_launch_checks: unknown check type"); 16001d85ec5Sreyk 1617e351ffdSreyk TAILQ_FOREACH(host, &table->hosts, entry) { 162c723f8edSreyk if (host->flags & F_DISABLE || host->conf.parentid) 1637e351ffdSreyk continue; 164a7da800aSsthen bcopy(&tv, &host->cte.tv_start, 165a7da800aSsthen sizeof(host->cte.tv_start)); 1664156152fSreyk switch (table->conf.check) { 1674156152fSreyk case CHECK_ICMP: 16801d85ec5Sreyk schedule_icmp(env, host); 1694156152fSreyk break; 1704156152fSreyk case CHECK_SCRIPT: 1710325c666Sreyk check_script(env, host); 1724156152fSreyk break; 1734156152fSreyk default: 17401d85ec5Sreyk /* Any other TCP-style checks */ 1757e351ffdSreyk host->last_up = host->up; 1767e351ffdSreyk host->cte.host = host; 1777e351ffdSreyk host->cte.table = table; 1787e351ffdSreyk check_tcp(&host->cte); 1794156152fSreyk break; 1804156152fSreyk } 1817e351ffdSreyk } 1827e351ffdSreyk } 18301d85ec5Sreyk check_icmp(env, &tv); 18401d85ec5Sreyk 18535d10c30Sreyk bcopy(&env->sc_interval, &tv, sizeof(tv)); 18635d10c30Sreyk evtimer_add(&env->sc_ev, &tv); 1877e351ffdSreyk } 1887e351ffdSreyk 1897e351ffdSreyk void 190c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he) 1917e351ffdSreyk { 192609cf3a7Sreyk struct table *table; 1937e351ffdSreyk struct ctl_status st; 194609cf3a7Sreyk struct timeval tv_now, tv_dur; 195609cf3a7Sreyk u_long duration; 196609cf3a7Sreyk u_int logopt; 19774a0367eSgiovanni struct host *h, *hostnst; 198c723f8edSreyk int hostup; 199c0dc99f6Sreyk const char *msg; 200c723f8edSreyk 20174a0367eSgiovanni if ((hostnst = host_find(env, host->conf.id)) == NULL) 20274a0367eSgiovanni fatalx("hce_notify_done: desynchronized"); 20374a0367eSgiovanni 20474a0367eSgiovanni if ((table = table_find(env, host->conf.tableid)) == NULL) 20574a0367eSgiovanni fatalx("hce_notify_done: invalid table id"); 20674a0367eSgiovanni 20774a0367eSgiovanni if (hostnst->flags & F_DISABLE) { 20874a0367eSgiovanni if (env->sc_opts & RELAYD_OPT_LOGUPDATE) { 20974a0367eSgiovanni log_info("host %s, check %s%s (ignoring result, " 21074a0367eSgiovanni "host disabled)", 21174a0367eSgiovanni host->conf.name, table_check(table->conf.check), 2127bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : ""); 21374a0367eSgiovanni } 21474a0367eSgiovanni host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 21574a0367eSgiovanni return; 21674a0367eSgiovanni } 21774a0367eSgiovanni 218c723f8edSreyk hostup = host->up; 219c0dc99f6Sreyk host->he = he; 2207e351ffdSreyk 2212edd718bSreyk if (host->up == HOST_DOWN && host->retry_cnt) { 22285a8c65fSreyk log_debug("%s: host %s retry %d", __func__, 22368b79041Spyr host->conf.name, host->retry_cnt); 224bd2508bcSreyk host->up = host->last_up; 2252edd718bSreyk host->retry_cnt--; 2262edd718bSreyk } else 22768b79041Spyr host->retry_cnt = host->conf.retry; 2282edd718bSreyk if (host->up != HOST_UNKNOWN) { 2292edd718bSreyk host->check_cnt++; 2302edd718bSreyk if (host->up == HOST_UP) 2312edd718bSreyk host->up_cnt++; 2322edd718bSreyk } 23368b79041Spyr st.id = host->conf.id; 234feb9ff76Sreyk st.up = host->up; 2352edd718bSreyk st.check_cnt = host->check_cnt; 2362edd718bSreyk st.retry_cnt = host->retry_cnt; 237c0dc99f6Sreyk st.he = he; 23801d85ec5Sreyk host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 239c0dc99f6Sreyk msg = host_error(he); 2407e351ffdSreyk if (msg) 24185a8c65fSreyk log_debug("%s: %s (%s)", __func__, host->conf.name, msg); 242609cf3a7Sreyk 2430325c666Sreyk proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS, 2440325c666Sreyk -1, &st, sizeof(st)); 2452edd718bSreyk if (host->up != host->last_up) 246748ceb64Sreyk logopt = RELAYD_OPT_LOGUPDATE; 2472edd718bSreyk else 248748ceb64Sreyk logopt = RELAYD_OPT_LOGNOTIFY; 249609cf3a7Sreyk 250fd1841a3Sreyk getmonotime(&tv_now); 251609cf3a7Sreyk timersub(&tv_now, &host->cte.tv_start, &tv_dur); 252609cf3a7Sreyk if (timercmp(&host->cte.tv_start, &tv_dur, >)) 253609cf3a7Sreyk duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0); 254609cf3a7Sreyk else 255609cf3a7Sreyk duration = 0; 256609cf3a7Sreyk 25735d10c30Sreyk if (env->sc_opts & logopt) { 2582edd718bSreyk log_info("host %s, check %s%s (%lums), state %s -> %s, " 2592edd718bSreyk "availability %s", 26068b79041Spyr host->conf.name, table_check(table->conf.check), 2617bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : "", duration, 2622edd718bSreyk host_status(host->last_up), host_status(host->up), 2632edd718bSreyk print_availability(host->check_cnt, host->up_cnt)); 264feb9ff76Sreyk } 265fe250497Sreyk 266609cf3a7Sreyk host->last_up = host->up; 267c723f8edSreyk 2681584e35dSreyk if (SLIST_EMPTY(&host->children)) 269c723f8edSreyk return; 270c723f8edSreyk 271c723f8edSreyk /* Notify for all other hosts that inherit the state from this one */ 2721584e35dSreyk SLIST_FOREACH(h, &host->children, child) { 273c723f8edSreyk h->up = hostup; 274c0dc99f6Sreyk hce_notify_done(h, he); 275c723f8edSreyk } 276feb9ff76Sreyk } 277feb9ff76Sreyk 2780325c666Sreyk int 2790325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg) 280feb9ff76Sreyk { 281feb9ff76Sreyk objid_t id; 282feb9ff76Sreyk struct host *host; 283feb9ff76Sreyk struct table *table; 284feb9ff76Sreyk 2850325c666Sreyk switch (imsg->hdr.type) { 286feb9ff76Sreyk case IMSG_HOST_DISABLE: 2870325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 288feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 2894f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 290feb9ff76Sreyk host->flags |= F_DISABLE; 291feb9ff76Sreyk host->up = HOST_UNKNOWN; 2922edd718bSreyk host->check_cnt = 0; 2932edd718bSreyk host->up_cnt = 0; 294c0dc99f6Sreyk host->he = HCE_NONE; 295feb9ff76Sreyk break; 296feb9ff76Sreyk case IMSG_HOST_ENABLE: 2970325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 298feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 2994f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 300feb9ff76Sreyk host->flags &= ~(F_DISABLE); 301feb9ff76Sreyk host->up = HOST_UNKNOWN; 302c0dc99f6Sreyk host->he = HCE_NONE; 303feb9ff76Sreyk break; 304feb9ff76Sreyk case IMSG_TABLE_DISABLE: 3050325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 306feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 3074f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 30868b79041Spyr table->conf.flags |= F_DISABLE; 309feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 310feb9ff76Sreyk host->up = HOST_UNKNOWN; 311feb9ff76Sreyk break; 312feb9ff76Sreyk case IMSG_TABLE_ENABLE: 3130325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 314feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 3154f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 31668b79041Spyr table->conf.flags &= ~(F_DISABLE); 317feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 318feb9ff76Sreyk host->up = HOST_UNKNOWN; 319feb9ff76Sreyk break; 320cd65ce7bSpyr case IMSG_CTL_POLL: 32135d10c30Sreyk evtimer_del(&env->sc_ev); 32235d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) 323f98352d8Spyr table->skipped = 0; 324cd65ce7bSpyr hce_launch_checks(-1, EV_TIMEOUT, env); 325cd65ce7bSpyr break; 326feb9ff76Sreyk default: 3270325c666Sreyk return (-1); 328feb9ff76Sreyk } 329feb9ff76Sreyk 3300325c666Sreyk return (0); 3310325c666Sreyk } 3320325c666Sreyk 3330325c666Sreyk int 3340325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 335feb9ff76Sreyk { 3364156152fSreyk struct ctl_script scr; 337feb9ff76Sreyk 3380325c666Sreyk switch (imsg->hdr.type) { 3394156152fSreyk case IMSG_SCRIPT: 3400325c666Sreyk IMSG_SIZE_CHECK(imsg, &scr); 3410325c666Sreyk bcopy(imsg->data, &scr, sizeof(scr)); 3424156152fSreyk script_done(env, &scr); 3434156152fSreyk break; 344a2195becSreyk case IMSG_CFG_TABLE: 345a2195becSreyk config_gettable(env, imsg); 346adf98c11Spyr break; 347a2195becSreyk case IMSG_CFG_HOST: 348a2195becSreyk config_gethost(env, imsg); 349adf98c11Spyr break; 350a2195becSreyk case IMSG_CFG_DONE: 351a2195becSreyk config_getcfg(env, imsg); 3520dcba380Scamield break; 3530dcba380Scamield case IMSG_CTL_START: 354adf98c11Spyr hce_setup_events(); 355adf98c11Spyr break; 356a2195becSreyk case IMSG_CTL_RESET: 357a2195becSreyk config_getreset(env, imsg); 358a2195becSreyk break; 359feb9ff76Sreyk default: 3600325c666Sreyk return (-1); 361feb9ff76Sreyk } 3620325c666Sreyk 3630325c666Sreyk return (0); 364feb9ff76Sreyk } 3656e07057bSblambert 3666e07057bSblambert int 3676e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg) 3686e07057bSblambert { 3696e07057bSblambert switch (imsg->hdr.type) { 3706e07057bSblambert default: 3716e07057bSblambert break; 3726e07057bSblambert } 3736e07057bSblambert 3746e07057bSblambert return (-1); 3756e07057bSblambert } 376