1*586b5f8aSreyk /* $OpenBSD: hce.c,v 1.74 2016/09/02 14:45:51 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 19f04ff968Sreyk #include <sys/types.h> 200ca734d7Sreyk #include <sys/queue.h> 21feb9ff76Sreyk #include <sys/time.h> 22f04ff968Sreyk #include <sys/uio.h> 230ca734d7Sreyk 24feb9ff76Sreyk #include <event.h> 25feb9ff76Sreyk #include <stdlib.h> 26feb9ff76Sreyk #include <string.h> 27feb9ff76Sreyk #include <unistd.h> 28f04ff968Sreyk #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 51f910ac11Sreyk void 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 59f910ac11Sreyk 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); 72cf9e83c3Sbenno 73cf9e83c3Sbenno if (pledge("stdio inet", NULL) == -1) 74cf9e83c3Sbenno fatal("hce: pledge"); 75780e8f79Spyr } 76780e8f79Spyr 77780e8f79Spyr void 78641b8bafSpyr hce_setup_events(void) 79780e8f79Spyr { 80780e8f79Spyr struct timeval tv; 81780e8f79Spyr struct table *table; 82780e8f79Spyr 83a2195becSreyk if (!(TAILQ_EMPTY(env->sc_tables) || 84a2195becSreyk event_initialized(&env->sc_ev))) { 8535d10c30Sreyk evtimer_set(&env->sc_ev, hce_launch_checks, env); 8601d85ec5Sreyk bzero(&tv, sizeof(tv)); 8735d10c30Sreyk evtimer_add(&env->sc_ev, &tv); 882edd718bSreyk } 89feb9ff76Sreyk 90*586b5f8aSreyk if (env->sc_conf.flags & F_TLS) { 9135d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 927bb52228Sreyk if (!(table->conf.flags & F_TLS) || 93a2195becSreyk table->ssl_ctx != NULL) 94e8fb3979Spyr continue; 95e8fb3979Spyr table->ssl_ctx = ssl_ctx_create(env); 96e8fb3979Spyr } 97e8fb3979Spyr } 98641b8bafSpyr } 99641b8bafSpyr 100641b8bafSpyr void 101641b8bafSpyr hce_disable_events(void) 102641b8bafSpyr { 103641b8bafSpyr struct table *table; 104641b8bafSpyr struct host *host; 105641b8bafSpyr 10635d10c30Sreyk evtimer_del(&env->sc_ev); 10735d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 108641b8bafSpyr TAILQ_FOREACH(host, &table->hosts, entry) { 109c0dc99f6Sreyk host->he = HCE_ABORT; 110a2195becSreyk if (event_initialized(&host->cte.ev)) { 111641b8bafSpyr event_del(&host->cte.ev); 112641b8bafSpyr close(host->cte.s); 113641b8bafSpyr } 114641b8bafSpyr } 115a2195becSreyk } 11635d10c30Sreyk if (env->sc_has_icmp) { 11735d10c30Sreyk event_del(&env->sc_icmp_send.ev); 11835d10c30Sreyk event_del(&env->sc_icmp_recv.ev); 119641b8bafSpyr } 12035d10c30Sreyk if (env->sc_has_icmp6) { 12135d10c30Sreyk event_del(&env->sc_icmp6_send.ev); 12235d10c30Sreyk event_del(&env->sc_icmp6_recv.ev); 123641b8bafSpyr } 124feb9ff76Sreyk } 125feb9ff76Sreyk 126feb9ff76Sreyk void 127feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg) 128feb9ff76Sreyk { 129feb9ff76Sreyk struct host *host; 130feb9ff76Sreyk struct table *table; 13101d85ec5Sreyk struct timeval tv; 13201d85ec5Sreyk 13301d85ec5Sreyk /* 13401d85ec5Sreyk * notify pfe checks are done and schedule next check 13501d85ec5Sreyk */ 136c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0); 13735d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 13801d85ec5Sreyk TAILQ_FOREACH(host, &table->hosts, entry) { 139c0dc99f6Sreyk if ((host->flags & F_CHECK_DONE) == 0) 140c0dc99f6Sreyk host->he = HCE_INTERVAL_TIMEOUT; 14101d85ec5Sreyk host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 142a2195becSreyk if (event_initialized(&host->cte.ev)) { 14301d85ec5Sreyk event_del(&host->cte.ev); 144a2195becSreyk close(host->cte.s); 145a2195becSreyk } 146a2195becSreyk host->cte.s = -1; 14701d85ec5Sreyk } 14801d85ec5Sreyk } 14901d85ec5Sreyk 150fd1841a3Sreyk getmonotime(&tv); 151feb9ff76Sreyk 15235d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) { 15368b79041Spyr if (table->conf.flags & F_DISABLE) 1547e351ffdSreyk continue; 15518de159aSpyr if (table->conf.skip_cnt) { 15618de159aSpyr if (table->skipped++ > table->conf.skip_cnt) 15718de159aSpyr table->skipped = 0; 15818de159aSpyr if (table->skipped != 1) 15918de159aSpyr continue; 16018de159aSpyr } 16168b79041Spyr if (table->conf.check == CHECK_NOCHECK) 1627e351ffdSreyk fatalx("hce_launch_checks: unknown check type"); 16301d85ec5Sreyk 1647e351ffdSreyk TAILQ_FOREACH(host, &table->hosts, entry) { 165c723f8edSreyk if (host->flags & F_DISABLE || host->conf.parentid) 1667e351ffdSreyk continue; 167a7da800aSsthen bcopy(&tv, &host->cte.tv_start, 168a7da800aSsthen sizeof(host->cte.tv_start)); 1694156152fSreyk switch (table->conf.check) { 1704156152fSreyk case CHECK_ICMP: 17101d85ec5Sreyk schedule_icmp(env, host); 1724156152fSreyk break; 1734156152fSreyk case CHECK_SCRIPT: 1740325c666Sreyk check_script(env, host); 1754156152fSreyk break; 1764156152fSreyk default: 17701d85ec5Sreyk /* Any other TCP-style checks */ 1787e351ffdSreyk host->last_up = host->up; 1797e351ffdSreyk host->cte.host = host; 1807e351ffdSreyk host->cte.table = table; 1817e351ffdSreyk check_tcp(&host->cte); 1824156152fSreyk break; 1834156152fSreyk } 1847e351ffdSreyk } 1857e351ffdSreyk } 18601d85ec5Sreyk check_icmp(env, &tv); 18701d85ec5Sreyk 188*586b5f8aSreyk bcopy(&env->sc_conf.interval, &tv, sizeof(tv)); 18935d10c30Sreyk evtimer_add(&env->sc_ev, &tv); 1907e351ffdSreyk } 1917e351ffdSreyk 1927e351ffdSreyk void 193c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he) 1947e351ffdSreyk { 195609cf3a7Sreyk struct table *table; 1967e351ffdSreyk struct ctl_status st; 197609cf3a7Sreyk struct timeval tv_now, tv_dur; 198609cf3a7Sreyk u_long duration; 199609cf3a7Sreyk u_int logopt; 20074a0367eSgiovanni struct host *h, *hostnst; 201c723f8edSreyk int hostup; 202c0dc99f6Sreyk const char *msg; 203c48fdb1dSbenno char *codemsg = NULL; 204c723f8edSreyk 20574a0367eSgiovanni if ((hostnst = host_find(env, host->conf.id)) == NULL) 20674a0367eSgiovanni fatalx("hce_notify_done: desynchronized"); 20774a0367eSgiovanni 20874a0367eSgiovanni if ((table = table_find(env, host->conf.tableid)) == NULL) 20974a0367eSgiovanni fatalx("hce_notify_done: invalid table id"); 21074a0367eSgiovanni 21174a0367eSgiovanni if (hostnst->flags & F_DISABLE) { 212*586b5f8aSreyk if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) { 21374a0367eSgiovanni log_info("host %s, check %s%s (ignoring result, " 21474a0367eSgiovanni "host disabled)", 21574a0367eSgiovanni host->conf.name, table_check(table->conf.check), 2167bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : ""); 21774a0367eSgiovanni } 21874a0367eSgiovanni host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 21974a0367eSgiovanni return; 22074a0367eSgiovanni } 22174a0367eSgiovanni 222c723f8edSreyk hostup = host->up; 223c0dc99f6Sreyk host->he = he; 2247e351ffdSreyk 2252edd718bSreyk if (host->up == HOST_DOWN && host->retry_cnt) { 22685a8c65fSreyk log_debug("%s: host %s retry %d", __func__, 22768b79041Spyr host->conf.name, host->retry_cnt); 228bd2508bcSreyk host->up = host->last_up; 2292edd718bSreyk host->retry_cnt--; 2302edd718bSreyk } else 23168b79041Spyr host->retry_cnt = host->conf.retry; 2322edd718bSreyk if (host->up != HOST_UNKNOWN) { 2332edd718bSreyk host->check_cnt++; 2342edd718bSreyk if (host->up == HOST_UP) 2352edd718bSreyk host->up_cnt++; 2362edd718bSreyk } 23768b79041Spyr st.id = host->conf.id; 238feb9ff76Sreyk st.up = host->up; 2392edd718bSreyk st.check_cnt = host->check_cnt; 2402edd718bSreyk st.retry_cnt = host->retry_cnt; 241c0dc99f6Sreyk st.he = he; 24201d85ec5Sreyk host->flags |= (F_CHECK_SENT|F_CHECK_DONE); 243c0dc99f6Sreyk msg = host_error(he); 2447e351ffdSreyk if (msg) 24585a8c65fSreyk log_debug("%s: %s (%s)", __func__, host->conf.name, msg); 246609cf3a7Sreyk 247c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st)); 2482edd718bSreyk if (host->up != host->last_up) 249748ceb64Sreyk logopt = RELAYD_OPT_LOGUPDATE; 2502edd718bSreyk else 251748ceb64Sreyk logopt = RELAYD_OPT_LOGNOTIFY; 252609cf3a7Sreyk 253fd1841a3Sreyk getmonotime(&tv_now); 254609cf3a7Sreyk timersub(&tv_now, &host->cte.tv_start, &tv_dur); 255609cf3a7Sreyk if (timercmp(&host->cte.tv_start, &tv_dur, >)) 256609cf3a7Sreyk duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0); 257609cf3a7Sreyk else 258609cf3a7Sreyk duration = 0; 259609cf3a7Sreyk 260*586b5f8aSreyk if (env->sc_conf.opts & logopt) { 261c48fdb1dSbenno if (host->code > 0) 262c48fdb1dSbenno asprintf(&codemsg, ",%d", host->code); 263c48fdb1dSbenno log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, " 2642edd718bSreyk "availability %s", 26568b79041Spyr host->conf.name, table_check(table->conf.check), 2667bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : "", duration, 267c48fdb1dSbenno msg, (codemsg != NULL) ? codemsg : "", 2682edd718bSreyk host_status(host->last_up), host_status(host->up), 2692edd718bSreyk print_availability(host->check_cnt, host->up_cnt)); 270c48fdb1dSbenno free(codemsg); 271feb9ff76Sreyk } 272fe250497Sreyk 273609cf3a7Sreyk host->last_up = host->up; 274c723f8edSreyk 2751584e35dSreyk if (SLIST_EMPTY(&host->children)) 276c723f8edSreyk return; 277c723f8edSreyk 278c723f8edSreyk /* Notify for all other hosts that inherit the state from this one */ 2791584e35dSreyk SLIST_FOREACH(h, &host->children, child) { 280c723f8edSreyk h->up = hostup; 281c0dc99f6Sreyk hce_notify_done(h, he); 282c723f8edSreyk } 283feb9ff76Sreyk } 284feb9ff76Sreyk 2850325c666Sreyk int 2860325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg) 287feb9ff76Sreyk { 288feb9ff76Sreyk objid_t id; 289feb9ff76Sreyk struct host *host; 290feb9ff76Sreyk struct table *table; 291feb9ff76Sreyk 2920325c666Sreyk switch (imsg->hdr.type) { 293feb9ff76Sreyk case IMSG_HOST_DISABLE: 2940325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 295feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 2964f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 297feb9ff76Sreyk host->flags |= F_DISABLE; 298feb9ff76Sreyk host->up = HOST_UNKNOWN; 2992edd718bSreyk host->check_cnt = 0; 3002edd718bSreyk host->up_cnt = 0; 301c0dc99f6Sreyk host->he = HCE_NONE; 302feb9ff76Sreyk break; 303feb9ff76Sreyk case IMSG_HOST_ENABLE: 3040325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 305feb9ff76Sreyk if ((host = host_find(env, id)) == NULL) 3064f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 307feb9ff76Sreyk host->flags &= ~(F_DISABLE); 308feb9ff76Sreyk host->up = HOST_UNKNOWN; 309c0dc99f6Sreyk host->he = HCE_NONE; 310feb9ff76Sreyk break; 311feb9ff76Sreyk case IMSG_TABLE_DISABLE: 3120325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 313feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 3144f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 31568b79041Spyr table->conf.flags |= F_DISABLE; 316feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 317feb9ff76Sreyk host->up = HOST_UNKNOWN; 318feb9ff76Sreyk break; 319feb9ff76Sreyk case IMSG_TABLE_ENABLE: 3200325c666Sreyk memcpy(&id, imsg->data, sizeof(id)); 321feb9ff76Sreyk if ((table = table_find(env, id)) == NULL) 3224f416e82Scamield fatalx("hce_dispatch_pfe: desynchronized"); 32368b79041Spyr table->conf.flags &= ~(F_DISABLE); 324feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry) 325feb9ff76Sreyk host->up = HOST_UNKNOWN; 326feb9ff76Sreyk break; 327cd65ce7bSpyr case IMSG_CTL_POLL: 32835d10c30Sreyk evtimer_del(&env->sc_ev); 32935d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) 330f98352d8Spyr table->skipped = 0; 331cd65ce7bSpyr hce_launch_checks(-1, EV_TIMEOUT, env); 332cd65ce7bSpyr break; 333feb9ff76Sreyk default: 3340325c666Sreyk return (-1); 335feb9ff76Sreyk } 336feb9ff76Sreyk 3370325c666Sreyk return (0); 3380325c666Sreyk } 3390325c666Sreyk 3400325c666Sreyk int 3410325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 342feb9ff76Sreyk { 3434156152fSreyk struct ctl_script scr; 344feb9ff76Sreyk 3450325c666Sreyk switch (imsg->hdr.type) { 3464156152fSreyk case IMSG_SCRIPT: 3470325c666Sreyk IMSG_SIZE_CHECK(imsg, &scr); 3480325c666Sreyk bcopy(imsg->data, &scr, sizeof(scr)); 3494156152fSreyk script_done(env, &scr); 3504156152fSreyk break; 351a2195becSreyk case IMSG_CFG_TABLE: 352a2195becSreyk config_gettable(env, imsg); 353adf98c11Spyr break; 354a2195becSreyk case IMSG_CFG_HOST: 355a2195becSreyk config_gethost(env, imsg); 356adf98c11Spyr break; 357a2195becSreyk case IMSG_CFG_DONE: 358a2195becSreyk config_getcfg(env, imsg); 3590dcba380Scamield break; 3600dcba380Scamield case IMSG_CTL_START: 361adf98c11Spyr hce_setup_events(); 362adf98c11Spyr break; 363a2195becSreyk case IMSG_CTL_RESET: 364a2195becSreyk config_getreset(env, imsg); 365a2195becSreyk break; 366feb9ff76Sreyk default: 3670325c666Sreyk return (-1); 368feb9ff76Sreyk } 3690325c666Sreyk 3700325c666Sreyk return (0); 371feb9ff76Sreyk } 3726e07057bSblambert 3736e07057bSblambert int 3746e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg) 3756e07057bSblambert { 3766e07057bSblambert switch (imsg->hdr.type) { 3776e07057bSblambert default: 3786e07057bSblambert break; 3796e07057bSblambert } 3806e07057bSblambert 3816e07057bSblambert return (-1); 3826e07057bSblambert } 383