1*e87883d9Stb /* $OpenBSD: hce.c,v 1.81 2022/06/03 13:23:16 tb 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
hce(struct privsep * ps,struct privsep_proc * p)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
hce_init(struct privsep * ps,struct privsep_proc * p,void * arg)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
733d6ff6edSreyk if (pledge("stdio recvfd inet", NULL) == -1)
74efc39811Sbenno fatal("%s: pledge", __func__);
75780e8f79Spyr }
76780e8f79Spyr
77780e8f79Spyr void
hce_setup_events(void)78641b8bafSpyr hce_setup_events(void)
79780e8f79Spyr {
80780e8f79Spyr struct timeval tv;
81780e8f79Spyr struct table *table;
82780e8f79Spyr
839ba713feSbenno if (!event_initialized(&env->sc_ev)) {
8435d10c30Sreyk evtimer_set(&env->sc_ev, hce_launch_checks, env);
8501d85ec5Sreyk bzero(&tv, sizeof(tv));
8635d10c30Sreyk evtimer_add(&env->sc_ev, &tv);
872edd718bSreyk }
88feb9ff76Sreyk
89586b5f8aSreyk if (env->sc_conf.flags & F_TLS) {
9035d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
917bb52228Sreyk if (!(table->conf.flags & F_TLS) ||
9285e5f500Sclaudio table->tls_cfg != NULL)
93e8fb3979Spyr continue;
9485e5f500Sclaudio table->tls_cfg = tls_config_new();
95*e87883d9Stb if (table->tls_cfg == NULL)
96*e87883d9Stb fatalx("%s: tls_config_new", __func__);
9785e5f500Sclaudio tls_config_insecure_noverifycert(table->tls_cfg);
9885e5f500Sclaudio tls_config_insecure_noverifyname(table->tls_cfg);
99e8fb3979Spyr }
100e8fb3979Spyr }
101641b8bafSpyr }
102641b8bafSpyr
103641b8bafSpyr void
hce_disable_events(void)104641b8bafSpyr hce_disable_events(void)
105641b8bafSpyr {
106641b8bafSpyr struct table *table;
107641b8bafSpyr struct host *host;
108641b8bafSpyr
10935d10c30Sreyk evtimer_del(&env->sc_ev);
11035d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
111641b8bafSpyr TAILQ_FOREACH(host, &table->hosts, entry) {
112c0dc99f6Sreyk host->he = HCE_ABORT;
113a2195becSreyk if (event_initialized(&host->cte.ev)) {
114641b8bafSpyr event_del(&host->cte.ev);
115641b8bafSpyr close(host->cte.s);
116641b8bafSpyr }
117641b8bafSpyr }
118a2195becSreyk }
11935d10c30Sreyk if (env->sc_has_icmp) {
12035d10c30Sreyk event_del(&env->sc_icmp_send.ev);
12135d10c30Sreyk event_del(&env->sc_icmp_recv.ev);
122641b8bafSpyr }
12335d10c30Sreyk if (env->sc_has_icmp6) {
12435d10c30Sreyk event_del(&env->sc_icmp6_send.ev);
12535d10c30Sreyk event_del(&env->sc_icmp6_recv.ev);
126641b8bafSpyr }
127feb9ff76Sreyk }
128feb9ff76Sreyk
129feb9ff76Sreyk void
hce_launch_checks(int fd,short event,void * arg)130feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
131feb9ff76Sreyk {
132feb9ff76Sreyk struct host *host;
133feb9ff76Sreyk struct table *table;
13401d85ec5Sreyk struct timeval tv;
13501d85ec5Sreyk
13601d85ec5Sreyk /*
13701d85ec5Sreyk * notify pfe checks are done and schedule next check
13801d85ec5Sreyk */
139c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
14035d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
14101d85ec5Sreyk TAILQ_FOREACH(host, &table->hosts, entry) {
142c0dc99f6Sreyk if ((host->flags & F_CHECK_DONE) == 0)
143c0dc99f6Sreyk host->he = HCE_INTERVAL_TIMEOUT;
144a2195becSreyk if (event_initialized(&host->cte.ev)) {
14501d85ec5Sreyk event_del(&host->cte.ev);
146a2195becSreyk close(host->cte.s);
147a2195becSreyk }
148a2195becSreyk host->cte.s = -1;
14901d85ec5Sreyk }
15001d85ec5Sreyk }
15101d85ec5Sreyk
152fd1841a3Sreyk getmonotime(&tv);
153feb9ff76Sreyk
15435d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
15568b79041Spyr if (table->conf.flags & F_DISABLE)
1567e351ffdSreyk continue;
15718de159aSpyr if (table->conf.skip_cnt) {
15818de159aSpyr if (table->skipped++ > table->conf.skip_cnt)
15918de159aSpyr table->skipped = 0;
16018de159aSpyr if (table->skipped != 1)
16118de159aSpyr continue;
16218de159aSpyr }
16368b79041Spyr if (table->conf.check == CHECK_NOCHECK)
164efc39811Sbenno fatalx("%s: unknown check type", __func__);
16501d85ec5Sreyk
1667e351ffdSreyk TAILQ_FOREACH(host, &table->hosts, entry) {
167c723f8edSreyk if (host->flags & F_DISABLE || host->conf.parentid)
1687e351ffdSreyk continue;
169a7da800aSsthen bcopy(&tv, &host->cte.tv_start,
170a7da800aSsthen sizeof(host->cte.tv_start));
1714156152fSreyk switch (table->conf.check) {
1724156152fSreyk case CHECK_ICMP:
17301d85ec5Sreyk schedule_icmp(env, host);
1744156152fSreyk break;
1754156152fSreyk case CHECK_SCRIPT:
1760325c666Sreyk check_script(env, host);
1774156152fSreyk break;
1784156152fSreyk default:
17901d85ec5Sreyk /* Any other TCP-style checks */
1807e351ffdSreyk host->last_up = host->up;
1817e351ffdSreyk host->cte.host = host;
1827e351ffdSreyk host->cte.table = table;
1837e351ffdSreyk check_tcp(&host->cte);
1844156152fSreyk break;
1854156152fSreyk }
1867e351ffdSreyk }
1877e351ffdSreyk }
18801d85ec5Sreyk check_icmp(env, &tv);
18901d85ec5Sreyk
190586b5f8aSreyk bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
19135d10c30Sreyk evtimer_add(&env->sc_ev, &tv);
1927e351ffdSreyk }
1937e351ffdSreyk
1947e351ffdSreyk void
hce_notify_done(struct host * host,enum host_error he)195c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
1967e351ffdSreyk {
197609cf3a7Sreyk struct table *table;
1987e351ffdSreyk struct ctl_status st;
199609cf3a7Sreyk struct timeval tv_now, tv_dur;
200609cf3a7Sreyk u_long duration;
2010be9d00aSbenno u_int logopt = RELAYD_OPT_LOGHOSTCHECK;
20274a0367eSgiovanni struct host *h, *hostnst;
203c723f8edSreyk int hostup;
204c0dc99f6Sreyk const char *msg;
205c48fdb1dSbenno char *codemsg = NULL;
206c723f8edSreyk
20774a0367eSgiovanni if ((hostnst = host_find(env, host->conf.id)) == NULL)
208efc39811Sbenno fatalx("%s: desynchronized", __func__);
20974a0367eSgiovanni
21074a0367eSgiovanni if ((table = table_find(env, host->conf.tableid)) == NULL)
211efc39811Sbenno fatalx("%s: invalid table id", __func__);
21274a0367eSgiovanni
21374a0367eSgiovanni if (hostnst->flags & F_DISABLE) {
214586b5f8aSreyk if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
21574a0367eSgiovanni log_info("host %s, check %s%s (ignoring result, "
21674a0367eSgiovanni "host disabled)",
21774a0367eSgiovanni host->conf.name, table_check(table->conf.check),
2187bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : "");
21974a0367eSgiovanni }
22074a0367eSgiovanni host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
22174a0367eSgiovanni return;
22274a0367eSgiovanni }
22374a0367eSgiovanni
224c723f8edSreyk hostup = host->up;
225c0dc99f6Sreyk host->he = he;
2267e351ffdSreyk
2272edd718bSreyk if (host->up == HOST_DOWN && host->retry_cnt) {
22885a8c65fSreyk log_debug("%s: host %s retry %d", __func__,
22968b79041Spyr host->conf.name, host->retry_cnt);
230bd2508bcSreyk host->up = host->last_up;
2312edd718bSreyk host->retry_cnt--;
2322edd718bSreyk } else
23368b79041Spyr host->retry_cnt = host->conf.retry;
2342edd718bSreyk if (host->up != HOST_UNKNOWN) {
2352edd718bSreyk host->check_cnt++;
2362edd718bSreyk if (host->up == HOST_UP)
2372edd718bSreyk host->up_cnt++;
2382edd718bSreyk }
23968b79041Spyr st.id = host->conf.id;
240feb9ff76Sreyk st.up = host->up;
2412edd718bSreyk st.check_cnt = host->check_cnt;
2422edd718bSreyk st.retry_cnt = host->retry_cnt;
243c0dc99f6Sreyk st.he = he;
24401d85ec5Sreyk host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
245c0dc99f6Sreyk msg = host_error(he);
2467e351ffdSreyk if (msg)
24785a8c65fSreyk log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
248609cf3a7Sreyk
249c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
2502edd718bSreyk if (host->up != host->last_up)
251748ceb64Sreyk logopt = RELAYD_OPT_LOGUPDATE;
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
260586b5f8aSreyk 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
hce_dispatch_pfe(int fd,struct privsep_proc * p,struct imsg * imsg)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)
296efc39811Sbenno fatalx("%s: desynchronized", __func__);
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)
306efc39811Sbenno fatalx("%s: desynchronized", __func__);
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)
314efc39811Sbenno fatalx("%s: desynchronized", __func__);
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)
322efc39811Sbenno fatalx("%s: desynchronized", __func__);
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
hce_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)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
hce_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)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