1*e15601b9Sjsg /* $OpenBSD: hce.c,v 1.82 2024/05/18 06:34:46 jsg 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_launch_checks(int, short, void *);
34641b8bafSpyr void hce_setup_events(void);
35641b8bafSpyr void hce_disable_events(void);
36feb9ff76Sreyk
370325c666Sreyk int hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
380325c666Sreyk int hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
396e07057bSblambert int hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
400325c666Sreyk
41748ceb64Sreyk static struct relayd *env = NULL;
42780e8f79Spyr int running = 0;
43feb9ff76Sreyk
440325c666Sreyk static struct privsep_proc procs[] = {
450325c666Sreyk { "parent", PROC_PARENT, hce_dispatch_parent },
460325c666Sreyk { "pfe", PROC_PFE, hce_dispatch_pfe },
476e07057bSblambert { "relay", PROC_RELAY, hce_dispatch_relay },
480325c666Sreyk };
49feb9ff76Sreyk
50f910ac11Sreyk void
hce(struct privsep * ps,struct privsep_proc * p)510325c666Sreyk hce(struct privsep *ps, struct privsep_proc *p)
52feb9ff76Sreyk {
530325c666Sreyk env = ps->ps_env;
54feb9ff76Sreyk
5501d85ec5Sreyk /* this is needed for icmp tests */
5601d85ec5Sreyk icmp_init(env);
5701d85ec5Sreyk
58f910ac11Sreyk proc_run(ps, p, procs, nitems(procs), hce_init, NULL);
590325c666Sreyk }
60feb9ff76Sreyk
610325c666Sreyk void
hce_init(struct privsep * ps,struct privsep_proc * p,void * arg)620325c666Sreyk hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
630325c666Sreyk {
64a2195becSreyk if (config_init(ps->ps_env) == -1)
65a2195becSreyk fatal("failed to initialize configuration");
660325c666Sreyk
670325c666Sreyk env->sc_id = getpid() & 0xffff;
68feb9ff76Sreyk
69bb8fa3feSreyk /* Allow maximum available sockets for TCP checks */
70bb8fa3feSreyk socket_rlimit(-1);
71cf9e83c3Sbenno
723d6ff6edSreyk if (pledge("stdio recvfd inet", NULL) == -1)
73efc39811Sbenno fatal("%s: pledge", __func__);
74780e8f79Spyr }
75780e8f79Spyr
76780e8f79Spyr void
hce_setup_events(void)77641b8bafSpyr hce_setup_events(void)
78780e8f79Spyr {
79780e8f79Spyr struct timeval tv;
80780e8f79Spyr struct table *table;
81780e8f79Spyr
829ba713feSbenno if (!event_initialized(&env->sc_ev)) {
8335d10c30Sreyk evtimer_set(&env->sc_ev, hce_launch_checks, env);
8401d85ec5Sreyk bzero(&tv, sizeof(tv));
8535d10c30Sreyk evtimer_add(&env->sc_ev, &tv);
862edd718bSreyk }
87feb9ff76Sreyk
88586b5f8aSreyk if (env->sc_conf.flags & F_TLS) {
8935d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
907bb52228Sreyk if (!(table->conf.flags & F_TLS) ||
9185e5f500Sclaudio table->tls_cfg != NULL)
92e8fb3979Spyr continue;
9385e5f500Sclaudio table->tls_cfg = tls_config_new();
94e87883d9Stb if (table->tls_cfg == NULL)
95e87883d9Stb fatalx("%s: tls_config_new", __func__);
9685e5f500Sclaudio tls_config_insecure_noverifycert(table->tls_cfg);
9785e5f500Sclaudio tls_config_insecure_noverifyname(table->tls_cfg);
98e8fb3979Spyr }
99e8fb3979Spyr }
100641b8bafSpyr }
101641b8bafSpyr
102641b8bafSpyr void
hce_disable_events(void)103641b8bafSpyr hce_disable_events(void)
104641b8bafSpyr {
105641b8bafSpyr struct table *table;
106641b8bafSpyr struct host *host;
107641b8bafSpyr
10835d10c30Sreyk evtimer_del(&env->sc_ev);
10935d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
110641b8bafSpyr TAILQ_FOREACH(host, &table->hosts, entry) {
111c0dc99f6Sreyk host->he = HCE_ABORT;
112a2195becSreyk if (event_initialized(&host->cte.ev)) {
113641b8bafSpyr event_del(&host->cte.ev);
114641b8bafSpyr close(host->cte.s);
115641b8bafSpyr }
116641b8bafSpyr }
117a2195becSreyk }
11835d10c30Sreyk if (env->sc_has_icmp) {
11935d10c30Sreyk event_del(&env->sc_icmp_send.ev);
12035d10c30Sreyk event_del(&env->sc_icmp_recv.ev);
121641b8bafSpyr }
12235d10c30Sreyk if (env->sc_has_icmp6) {
12335d10c30Sreyk event_del(&env->sc_icmp6_send.ev);
12435d10c30Sreyk event_del(&env->sc_icmp6_recv.ev);
125641b8bafSpyr }
126feb9ff76Sreyk }
127feb9ff76Sreyk
128feb9ff76Sreyk void
hce_launch_checks(int fd,short event,void * arg)129feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
130feb9ff76Sreyk {
131feb9ff76Sreyk struct host *host;
132feb9ff76Sreyk struct table *table;
13301d85ec5Sreyk struct timeval tv;
13401d85ec5Sreyk
13501d85ec5Sreyk /*
13601d85ec5Sreyk * notify pfe checks are done and schedule next check
13701d85ec5Sreyk */
138c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
13935d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
14001d85ec5Sreyk TAILQ_FOREACH(host, &table->hosts, entry) {
141c0dc99f6Sreyk if ((host->flags & F_CHECK_DONE) == 0)
142c0dc99f6Sreyk host->he = HCE_INTERVAL_TIMEOUT;
143a2195becSreyk if (event_initialized(&host->cte.ev)) {
14401d85ec5Sreyk event_del(&host->cte.ev);
145a2195becSreyk close(host->cte.s);
146a2195becSreyk }
147a2195becSreyk host->cte.s = -1;
14801d85ec5Sreyk }
14901d85ec5Sreyk }
15001d85ec5Sreyk
151fd1841a3Sreyk getmonotime(&tv);
152feb9ff76Sreyk
15335d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry) {
15468b79041Spyr if (table->conf.flags & F_DISABLE)
1557e351ffdSreyk continue;
15618de159aSpyr if (table->conf.skip_cnt) {
15718de159aSpyr if (table->skipped++ > table->conf.skip_cnt)
15818de159aSpyr table->skipped = 0;
15918de159aSpyr if (table->skipped != 1)
16018de159aSpyr continue;
16118de159aSpyr }
16268b79041Spyr if (table->conf.check == CHECK_NOCHECK)
163efc39811Sbenno fatalx("%s: unknown check type", __func__);
16401d85ec5Sreyk
1657e351ffdSreyk TAILQ_FOREACH(host, &table->hosts, entry) {
166c723f8edSreyk if (host->flags & F_DISABLE || host->conf.parentid)
1677e351ffdSreyk continue;
168a7da800aSsthen bcopy(&tv, &host->cte.tv_start,
169a7da800aSsthen sizeof(host->cte.tv_start));
1704156152fSreyk switch (table->conf.check) {
1714156152fSreyk case CHECK_ICMP:
17201d85ec5Sreyk schedule_icmp(env, host);
1734156152fSreyk break;
1744156152fSreyk case CHECK_SCRIPT:
1750325c666Sreyk check_script(env, host);
1764156152fSreyk break;
1774156152fSreyk default:
17801d85ec5Sreyk /* Any other TCP-style checks */
1797e351ffdSreyk host->last_up = host->up;
1807e351ffdSreyk host->cte.host = host;
1817e351ffdSreyk host->cte.table = table;
1827e351ffdSreyk check_tcp(&host->cte);
1834156152fSreyk break;
1844156152fSreyk }
1857e351ffdSreyk }
1867e351ffdSreyk }
18701d85ec5Sreyk check_icmp(env, &tv);
18801d85ec5Sreyk
189586b5f8aSreyk bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
19035d10c30Sreyk evtimer_add(&env->sc_ev, &tv);
1917e351ffdSreyk }
1927e351ffdSreyk
1937e351ffdSreyk void
hce_notify_done(struct host * host,enum host_error he)194c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
1957e351ffdSreyk {
196609cf3a7Sreyk struct table *table;
1977e351ffdSreyk struct ctl_status st;
198609cf3a7Sreyk struct timeval tv_now, tv_dur;
199609cf3a7Sreyk u_long duration;
2000be9d00aSbenno u_int logopt = RELAYD_OPT_LOGHOSTCHECK;
20174a0367eSgiovanni struct host *h, *hostnst;
202c723f8edSreyk int hostup;
203c0dc99f6Sreyk const char *msg;
204c48fdb1dSbenno char *codemsg = NULL;
205c723f8edSreyk
20674a0367eSgiovanni if ((hostnst = host_find(env, host->conf.id)) == NULL)
207efc39811Sbenno fatalx("%s: desynchronized", __func__);
20874a0367eSgiovanni
20974a0367eSgiovanni if ((table = table_find(env, host->conf.tableid)) == NULL)
210efc39811Sbenno fatalx("%s: invalid table id", __func__);
21174a0367eSgiovanni
21274a0367eSgiovanni if (hostnst->flags & F_DISABLE) {
213586b5f8aSreyk if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
21474a0367eSgiovanni log_info("host %s, check %s%s (ignoring result, "
21574a0367eSgiovanni "host disabled)",
21674a0367eSgiovanni host->conf.name, table_check(table->conf.check),
2177bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : "");
21874a0367eSgiovanni }
21974a0367eSgiovanni host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
22074a0367eSgiovanni return;
22174a0367eSgiovanni }
22274a0367eSgiovanni
223c723f8edSreyk hostup = host->up;
224c0dc99f6Sreyk host->he = he;
2257e351ffdSreyk
2262edd718bSreyk if (host->up == HOST_DOWN && host->retry_cnt) {
22785a8c65fSreyk log_debug("%s: host %s retry %d", __func__,
22868b79041Spyr host->conf.name, host->retry_cnt);
229bd2508bcSreyk host->up = host->last_up;
2302edd718bSreyk host->retry_cnt--;
2312edd718bSreyk } else
23268b79041Spyr host->retry_cnt = host->conf.retry;
2332edd718bSreyk if (host->up != HOST_UNKNOWN) {
2342edd718bSreyk host->check_cnt++;
2352edd718bSreyk if (host->up == HOST_UP)
2362edd718bSreyk host->up_cnt++;
2372edd718bSreyk }
23868b79041Spyr st.id = host->conf.id;
239feb9ff76Sreyk st.up = host->up;
2402edd718bSreyk st.check_cnt = host->check_cnt;
2412edd718bSreyk st.retry_cnt = host->retry_cnt;
242c0dc99f6Sreyk st.he = he;
24301d85ec5Sreyk host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
244c0dc99f6Sreyk msg = host_error(he);
2457e351ffdSreyk if (msg)
24685a8c65fSreyk log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
247609cf3a7Sreyk
248c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
2492edd718bSreyk if (host->up != host->last_up)
250748ceb64Sreyk logopt = RELAYD_OPT_LOGUPDATE;
251609cf3a7Sreyk
252fd1841a3Sreyk getmonotime(&tv_now);
253609cf3a7Sreyk timersub(&tv_now, &host->cte.tv_start, &tv_dur);
254609cf3a7Sreyk if (timercmp(&host->cte.tv_start, &tv_dur, >))
255609cf3a7Sreyk duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
256609cf3a7Sreyk else
257609cf3a7Sreyk duration = 0;
258609cf3a7Sreyk
259586b5f8aSreyk if (env->sc_conf.opts & logopt) {
260c48fdb1dSbenno if (host->code > 0)
261c48fdb1dSbenno asprintf(&codemsg, ",%d", host->code);
262c48fdb1dSbenno log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, "
2632edd718bSreyk "availability %s",
26468b79041Spyr host->conf.name, table_check(table->conf.check),
2657bb52228Sreyk (table->conf.flags & F_TLS) ? " use tls" : "", duration,
266c48fdb1dSbenno msg, (codemsg != NULL) ? codemsg : "",
2672edd718bSreyk host_status(host->last_up), host_status(host->up),
2682edd718bSreyk print_availability(host->check_cnt, host->up_cnt));
269c48fdb1dSbenno free(codemsg);
270feb9ff76Sreyk }
271fe250497Sreyk
272609cf3a7Sreyk host->last_up = host->up;
273c723f8edSreyk
2741584e35dSreyk if (SLIST_EMPTY(&host->children))
275c723f8edSreyk return;
276c723f8edSreyk
277c723f8edSreyk /* Notify for all other hosts that inherit the state from this one */
2781584e35dSreyk SLIST_FOREACH(h, &host->children, child) {
279c723f8edSreyk h->up = hostup;
280c0dc99f6Sreyk hce_notify_done(h, he);
281c723f8edSreyk }
282feb9ff76Sreyk }
283feb9ff76Sreyk
2840325c666Sreyk int
hce_dispatch_pfe(int fd,struct privsep_proc * p,struct imsg * imsg)2850325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
286feb9ff76Sreyk {
287feb9ff76Sreyk objid_t id;
288feb9ff76Sreyk struct host *host;
289feb9ff76Sreyk struct table *table;
290feb9ff76Sreyk
2910325c666Sreyk switch (imsg->hdr.type) {
292feb9ff76Sreyk case IMSG_HOST_DISABLE:
2930325c666Sreyk memcpy(&id, imsg->data, sizeof(id));
294feb9ff76Sreyk if ((host = host_find(env, id)) == NULL)
295efc39811Sbenno fatalx("%s: desynchronized", __func__);
296feb9ff76Sreyk host->flags |= F_DISABLE;
297feb9ff76Sreyk host->up = HOST_UNKNOWN;
2982edd718bSreyk host->check_cnt = 0;
2992edd718bSreyk host->up_cnt = 0;
300c0dc99f6Sreyk host->he = HCE_NONE;
301feb9ff76Sreyk break;
302feb9ff76Sreyk case IMSG_HOST_ENABLE:
3030325c666Sreyk memcpy(&id, imsg->data, sizeof(id));
304feb9ff76Sreyk if ((host = host_find(env, id)) == NULL)
305efc39811Sbenno fatalx("%s: desynchronized", __func__);
306feb9ff76Sreyk host->flags &= ~(F_DISABLE);
307feb9ff76Sreyk host->up = HOST_UNKNOWN;
308c0dc99f6Sreyk host->he = HCE_NONE;
309feb9ff76Sreyk break;
310feb9ff76Sreyk case IMSG_TABLE_DISABLE:
3110325c666Sreyk memcpy(&id, imsg->data, sizeof(id));
312feb9ff76Sreyk if ((table = table_find(env, id)) == NULL)
313efc39811Sbenno fatalx("%s: desynchronized", __func__);
31468b79041Spyr table->conf.flags |= F_DISABLE;
315feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry)
316feb9ff76Sreyk host->up = HOST_UNKNOWN;
317feb9ff76Sreyk break;
318feb9ff76Sreyk case IMSG_TABLE_ENABLE:
3190325c666Sreyk memcpy(&id, imsg->data, sizeof(id));
320feb9ff76Sreyk if ((table = table_find(env, id)) == NULL)
321efc39811Sbenno fatalx("%s: desynchronized", __func__);
32268b79041Spyr table->conf.flags &= ~(F_DISABLE);
323feb9ff76Sreyk TAILQ_FOREACH(host, &table->hosts, entry)
324feb9ff76Sreyk host->up = HOST_UNKNOWN;
325feb9ff76Sreyk break;
326cd65ce7bSpyr case IMSG_CTL_POLL:
32735d10c30Sreyk evtimer_del(&env->sc_ev);
32835d10c30Sreyk TAILQ_FOREACH(table, env->sc_tables, entry)
329f98352d8Spyr table->skipped = 0;
330cd65ce7bSpyr hce_launch_checks(-1, EV_TIMEOUT, env);
331cd65ce7bSpyr break;
332feb9ff76Sreyk default:
3330325c666Sreyk return (-1);
334feb9ff76Sreyk }
335feb9ff76Sreyk
3360325c666Sreyk return (0);
3370325c666Sreyk }
3380325c666Sreyk
3390325c666Sreyk int
hce_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)3400325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
341feb9ff76Sreyk {
3424156152fSreyk struct ctl_script scr;
343feb9ff76Sreyk
3440325c666Sreyk switch (imsg->hdr.type) {
3454156152fSreyk case IMSG_SCRIPT:
3460325c666Sreyk IMSG_SIZE_CHECK(imsg, &scr);
3470325c666Sreyk bcopy(imsg->data, &scr, sizeof(scr));
3484156152fSreyk script_done(env, &scr);
3494156152fSreyk break;
350a2195becSreyk case IMSG_CFG_TABLE:
351a2195becSreyk config_gettable(env, imsg);
352adf98c11Spyr break;
353a2195becSreyk case IMSG_CFG_HOST:
354a2195becSreyk config_gethost(env, imsg);
355adf98c11Spyr break;
356a2195becSreyk case IMSG_CFG_DONE:
357a2195becSreyk config_getcfg(env, imsg);
3580dcba380Scamield break;
3590dcba380Scamield case IMSG_CTL_START:
360adf98c11Spyr hce_setup_events();
361adf98c11Spyr break;
362a2195becSreyk case IMSG_CTL_RESET:
363a2195becSreyk config_getreset(env, imsg);
364a2195becSreyk break;
365feb9ff76Sreyk default:
3660325c666Sreyk return (-1);
367feb9ff76Sreyk }
3680325c666Sreyk
3690325c666Sreyk return (0);
370feb9ff76Sreyk }
3716e07057bSblambert
3726e07057bSblambert int
hce_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)3736e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
3746e07057bSblambert {
3756e07057bSblambert switch (imsg->hdr.type) {
3766e07057bSblambert default:
3776e07057bSblambert break;
3786e07057bSblambert }
3796e07057bSblambert
3806e07057bSblambert return (-1);
3816e07057bSblambert }
382