mirror of
https://github.com/openbsd/src.git
synced 2024-12-22 07:27:59 -08:00
382 lines
9.4 KiB
C
382 lines
9.4 KiB
C
/* $OpenBSD: hce.c,v 1.82 2024/05/18 06:34:46 jsg Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/time.h>
|
|
#include <sys/uio.h>
|
|
|
|
#include <event.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <imsg.h>
|
|
|
|
#include "relayd.h"
|
|
|
|
void hce_init(struct privsep *, struct privsep_proc *p, void *);
|
|
void hce_launch_checks(int, short, void *);
|
|
void hce_setup_events(void);
|
|
void hce_disable_events(void);
|
|
|
|
int hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
|
|
int hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
|
|
int hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
|
|
|
|
static struct relayd *env = NULL;
|
|
int running = 0;
|
|
|
|
static struct privsep_proc procs[] = {
|
|
{ "parent", PROC_PARENT, hce_dispatch_parent },
|
|
{ "pfe", PROC_PFE, hce_dispatch_pfe },
|
|
{ "relay", PROC_RELAY, hce_dispatch_relay },
|
|
};
|
|
|
|
void
|
|
hce(struct privsep *ps, struct privsep_proc *p)
|
|
{
|
|
env = ps->ps_env;
|
|
|
|
/* this is needed for icmp tests */
|
|
icmp_init(env);
|
|
|
|
proc_run(ps, p, procs, nitems(procs), hce_init, NULL);
|
|
}
|
|
|
|
void
|
|
hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
|
|
{
|
|
if (config_init(ps->ps_env) == -1)
|
|
fatal("failed to initialize configuration");
|
|
|
|
env->sc_id = getpid() & 0xffff;
|
|
|
|
/* Allow maximum available sockets for TCP checks */
|
|
socket_rlimit(-1);
|
|
|
|
if (pledge("stdio recvfd inet", NULL) == -1)
|
|
fatal("%s: pledge", __func__);
|
|
}
|
|
|
|
void
|
|
hce_setup_events(void)
|
|
{
|
|
struct timeval tv;
|
|
struct table *table;
|
|
|
|
if (!event_initialized(&env->sc_ev)) {
|
|
evtimer_set(&env->sc_ev, hce_launch_checks, env);
|
|
bzero(&tv, sizeof(tv));
|
|
evtimer_add(&env->sc_ev, &tv);
|
|
}
|
|
|
|
if (env->sc_conf.flags & F_TLS) {
|
|
TAILQ_FOREACH(table, env->sc_tables, entry) {
|
|
if (!(table->conf.flags & F_TLS) ||
|
|
table->tls_cfg != NULL)
|
|
continue;
|
|
table->tls_cfg = tls_config_new();
|
|
if (table->tls_cfg == NULL)
|
|
fatalx("%s: tls_config_new", __func__);
|
|
tls_config_insecure_noverifycert(table->tls_cfg);
|
|
tls_config_insecure_noverifyname(table->tls_cfg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
hce_disable_events(void)
|
|
{
|
|
struct table *table;
|
|
struct host *host;
|
|
|
|
evtimer_del(&env->sc_ev);
|
|
TAILQ_FOREACH(table, env->sc_tables, entry) {
|
|
TAILQ_FOREACH(host, &table->hosts, entry) {
|
|
host->he = HCE_ABORT;
|
|
if (event_initialized(&host->cte.ev)) {
|
|
event_del(&host->cte.ev);
|
|
close(host->cte.s);
|
|
}
|
|
}
|
|
}
|
|
if (env->sc_has_icmp) {
|
|
event_del(&env->sc_icmp_send.ev);
|
|
event_del(&env->sc_icmp_recv.ev);
|
|
}
|
|
if (env->sc_has_icmp6) {
|
|
event_del(&env->sc_icmp6_send.ev);
|
|
event_del(&env->sc_icmp6_recv.ev);
|
|
}
|
|
}
|
|
|
|
void
|
|
hce_launch_checks(int fd, short event, void *arg)
|
|
{
|
|
struct host *host;
|
|
struct table *table;
|
|
struct timeval tv;
|
|
|
|
/*
|
|
* notify pfe checks are done and schedule next check
|
|
*/
|
|
proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
|
|
TAILQ_FOREACH(table, env->sc_tables, entry) {
|
|
TAILQ_FOREACH(host, &table->hosts, entry) {
|
|
if ((host->flags & F_CHECK_DONE) == 0)
|
|
host->he = HCE_INTERVAL_TIMEOUT;
|
|
if (event_initialized(&host->cte.ev)) {
|
|
event_del(&host->cte.ev);
|
|
close(host->cte.s);
|
|
}
|
|
host->cte.s = -1;
|
|
}
|
|
}
|
|
|
|
getmonotime(&tv);
|
|
|
|
TAILQ_FOREACH(table, env->sc_tables, entry) {
|
|
if (table->conf.flags & F_DISABLE)
|
|
continue;
|
|
if (table->conf.skip_cnt) {
|
|
if (table->skipped++ > table->conf.skip_cnt)
|
|
table->skipped = 0;
|
|
if (table->skipped != 1)
|
|
continue;
|
|
}
|
|
if (table->conf.check == CHECK_NOCHECK)
|
|
fatalx("%s: unknown check type", __func__);
|
|
|
|
TAILQ_FOREACH(host, &table->hosts, entry) {
|
|
if (host->flags & F_DISABLE || host->conf.parentid)
|
|
continue;
|
|
bcopy(&tv, &host->cte.tv_start,
|
|
sizeof(host->cte.tv_start));
|
|
switch (table->conf.check) {
|
|
case CHECK_ICMP:
|
|
schedule_icmp(env, host);
|
|
break;
|
|
case CHECK_SCRIPT:
|
|
check_script(env, host);
|
|
break;
|
|
default:
|
|
/* Any other TCP-style checks */
|
|
host->last_up = host->up;
|
|
host->cte.host = host;
|
|
host->cte.table = table;
|
|
check_tcp(&host->cte);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
check_icmp(env, &tv);
|
|
|
|
bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
|
|
evtimer_add(&env->sc_ev, &tv);
|
|
}
|
|
|
|
void
|
|
hce_notify_done(struct host *host, enum host_error he)
|
|
{
|
|
struct table *table;
|
|
struct ctl_status st;
|
|
struct timeval tv_now, tv_dur;
|
|
u_long duration;
|
|
u_int logopt = RELAYD_OPT_LOGHOSTCHECK;
|
|
struct host *h, *hostnst;
|
|
int hostup;
|
|
const char *msg;
|
|
char *codemsg = NULL;
|
|
|
|
if ((hostnst = host_find(env, host->conf.id)) == NULL)
|
|
fatalx("%s: desynchronized", __func__);
|
|
|
|
if ((table = table_find(env, host->conf.tableid)) == NULL)
|
|
fatalx("%s: invalid table id", __func__);
|
|
|
|
if (hostnst->flags & F_DISABLE) {
|
|
if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
|
|
log_info("host %s, check %s%s (ignoring result, "
|
|
"host disabled)",
|
|
host->conf.name, table_check(table->conf.check),
|
|
(table->conf.flags & F_TLS) ? " use tls" : "");
|
|
}
|
|
host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
|
|
return;
|
|
}
|
|
|
|
hostup = host->up;
|
|
host->he = he;
|
|
|
|
if (host->up == HOST_DOWN && host->retry_cnt) {
|
|
log_debug("%s: host %s retry %d", __func__,
|
|
host->conf.name, host->retry_cnt);
|
|
host->up = host->last_up;
|
|
host->retry_cnt--;
|
|
} else
|
|
host->retry_cnt = host->conf.retry;
|
|
if (host->up != HOST_UNKNOWN) {
|
|
host->check_cnt++;
|
|
if (host->up == HOST_UP)
|
|
host->up_cnt++;
|
|
}
|
|
st.id = host->conf.id;
|
|
st.up = host->up;
|
|
st.check_cnt = host->check_cnt;
|
|
st.retry_cnt = host->retry_cnt;
|
|
st.he = he;
|
|
host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
|
|
msg = host_error(he);
|
|
if (msg)
|
|
log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
|
|
|
|
proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
|
|
if (host->up != host->last_up)
|
|
logopt = RELAYD_OPT_LOGUPDATE;
|
|
|
|
getmonotime(&tv_now);
|
|
timersub(&tv_now, &host->cte.tv_start, &tv_dur);
|
|
if (timercmp(&host->cte.tv_start, &tv_dur, >))
|
|
duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
|
|
else
|
|
duration = 0;
|
|
|
|
if (env->sc_conf.opts & logopt) {
|
|
if (host->code > 0)
|
|
asprintf(&codemsg, ",%d", host->code);
|
|
log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, "
|
|
"availability %s",
|
|
host->conf.name, table_check(table->conf.check),
|
|
(table->conf.flags & F_TLS) ? " use tls" : "", duration,
|
|
msg, (codemsg != NULL) ? codemsg : "",
|
|
host_status(host->last_up), host_status(host->up),
|
|
print_availability(host->check_cnt, host->up_cnt));
|
|
free(codemsg);
|
|
}
|
|
|
|
host->last_up = host->up;
|
|
|
|
if (SLIST_EMPTY(&host->children))
|
|
return;
|
|
|
|
/* Notify for all other hosts that inherit the state from this one */
|
|
SLIST_FOREACH(h, &host->children, child) {
|
|
h->up = hostup;
|
|
hce_notify_done(h, he);
|
|
}
|
|
}
|
|
|
|
int
|
|
hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
|
|
{
|
|
objid_t id;
|
|
struct host *host;
|
|
struct table *table;
|
|
|
|
switch (imsg->hdr.type) {
|
|
case IMSG_HOST_DISABLE:
|
|
memcpy(&id, imsg->data, sizeof(id));
|
|
if ((host = host_find(env, id)) == NULL)
|
|
fatalx("%s: desynchronized", __func__);
|
|
host->flags |= F_DISABLE;
|
|
host->up = HOST_UNKNOWN;
|
|
host->check_cnt = 0;
|
|
host->up_cnt = 0;
|
|
host->he = HCE_NONE;
|
|
break;
|
|
case IMSG_HOST_ENABLE:
|
|
memcpy(&id, imsg->data, sizeof(id));
|
|
if ((host = host_find(env, id)) == NULL)
|
|
fatalx("%s: desynchronized", __func__);
|
|
host->flags &= ~(F_DISABLE);
|
|
host->up = HOST_UNKNOWN;
|
|
host->he = HCE_NONE;
|
|
break;
|
|
case IMSG_TABLE_DISABLE:
|
|
memcpy(&id, imsg->data, sizeof(id));
|
|
if ((table = table_find(env, id)) == NULL)
|
|
fatalx("%s: desynchronized", __func__);
|
|
table->conf.flags |= F_DISABLE;
|
|
TAILQ_FOREACH(host, &table->hosts, entry)
|
|
host->up = HOST_UNKNOWN;
|
|
break;
|
|
case IMSG_TABLE_ENABLE:
|
|
memcpy(&id, imsg->data, sizeof(id));
|
|
if ((table = table_find(env, id)) == NULL)
|
|
fatalx("%s: desynchronized", __func__);
|
|
table->conf.flags &= ~(F_DISABLE);
|
|
TAILQ_FOREACH(host, &table->hosts, entry)
|
|
host->up = HOST_UNKNOWN;
|
|
break;
|
|
case IMSG_CTL_POLL:
|
|
evtimer_del(&env->sc_ev);
|
|
TAILQ_FOREACH(table, env->sc_tables, entry)
|
|
table->skipped = 0;
|
|
hce_launch_checks(-1, EV_TIMEOUT, env);
|
|
break;
|
|
default:
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
|
|
{
|
|
struct ctl_script scr;
|
|
|
|
switch (imsg->hdr.type) {
|
|
case IMSG_SCRIPT:
|
|
IMSG_SIZE_CHECK(imsg, &scr);
|
|
bcopy(imsg->data, &scr, sizeof(scr));
|
|
script_done(env, &scr);
|
|
break;
|
|
case IMSG_CFG_TABLE:
|
|
config_gettable(env, imsg);
|
|
break;
|
|
case IMSG_CFG_HOST:
|
|
config_gethost(env, imsg);
|
|
break;
|
|
case IMSG_CFG_DONE:
|
|
config_getcfg(env, imsg);
|
|
break;
|
|
case IMSG_CTL_START:
|
|
hce_setup_events();
|
|
break;
|
|
case IMSG_CTL_RESET:
|
|
config_getreset(env, imsg);
|
|
break;
|
|
default:
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
|
|
{
|
|
switch (imsg->hdr.type) {
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (-1);
|
|
}
|