[BACK]Return to hce.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / relayd

File: [local] / src / usr.sbin / relayd / hce.c (download)

Revision 1.81, Fri Jun 3 13:23:16 2022 UTC (23 months, 4 weeks ago) by tb
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2
Changes since 1.80: +3 -1 lines

Check tls_config_new() for NULL return

This way we don't crash in tls_config_insecure_noverify_cert().

From Mateusz Piotrowski on bugs

ok claudio

/*	$OpenBSD: hce.c,v 1.81 2022/06/03 13:23:16 tb 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_sig_handler(int sig, short, 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);
}