1 /* $OpenBSD: hce.c,v 1.81 2022/06/03 13:23:16 tb Exp $ */
2
3 /*
4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/time.h>
22 #include <sys/uio.h>
23
24 #include <event.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <imsg.h>
29
30 #include "relayd.h"
31
32 void hce_init(struct privsep *, struct privsep_proc *p, void *);
33 void hce_sig_handler(int sig, short, void *);
34 void hce_launch_checks(int, short, void *);
35 void hce_setup_events(void);
36 void hce_disable_events(void);
37
38 int hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
39 int hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
40 int hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
41
42 static struct relayd *env = NULL;
43 int running = 0;
44
45 static struct privsep_proc procs[] = {
46 { "parent", PROC_PARENT, hce_dispatch_parent },
47 { "pfe", PROC_PFE, hce_dispatch_pfe },
48 { "relay", PROC_RELAY, hce_dispatch_relay },
49 };
50
51 void
hce(struct privsep * ps,struct privsep_proc * p)52 hce(struct privsep *ps, struct privsep_proc *p)
53 {
54 env = ps->ps_env;
55
56 /* this is needed for icmp tests */
57 icmp_init(env);
58
59 proc_run(ps, p, procs, nitems(procs), hce_init, NULL);
60 }
61
62 void
hce_init(struct privsep * ps,struct privsep_proc * p,void * arg)63 hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
64 {
65 if (config_init(ps->ps_env) == -1)
66 fatal("failed to initialize configuration");
67
68 env->sc_id = getpid() & 0xffff;
69
70 /* Allow maximum available sockets for TCP checks */
71 socket_rlimit(-1);
72
73 if (pledge("stdio recvfd inet", NULL) == -1)
74 fatal("%s: pledge", __func__);
75 }
76
77 void
hce_setup_events(void)78 hce_setup_events(void)
79 {
80 struct timeval tv;
81 struct table *table;
82
83 if (!event_initialized(&env->sc_ev)) {
84 evtimer_set(&env->sc_ev, hce_launch_checks, env);
85 bzero(&tv, sizeof(tv));
86 evtimer_add(&env->sc_ev, &tv);
87 }
88
89 if (env->sc_conf.flags & F_TLS) {
90 TAILQ_FOREACH(table, env->sc_tables, entry) {
91 if (!(table->conf.flags & F_TLS) ||
92 table->tls_cfg != NULL)
93 continue;
94 table->tls_cfg = tls_config_new();
95 if (table->tls_cfg == NULL)
96 fatalx("%s: tls_config_new", __func__);
97 tls_config_insecure_noverifycert(table->tls_cfg);
98 tls_config_insecure_noverifyname(table->tls_cfg);
99 }
100 }
101 }
102
103 void
hce_disable_events(void)104 hce_disable_events(void)
105 {
106 struct table *table;
107 struct host *host;
108
109 evtimer_del(&env->sc_ev);
110 TAILQ_FOREACH(table, env->sc_tables, entry) {
111 TAILQ_FOREACH(host, &table->hosts, entry) {
112 host->he = HCE_ABORT;
113 if (event_initialized(&host->cte.ev)) {
114 event_del(&host->cte.ev);
115 close(host->cte.s);
116 }
117 }
118 }
119 if (env->sc_has_icmp) {
120 event_del(&env->sc_icmp_send.ev);
121 event_del(&env->sc_icmp_recv.ev);
122 }
123 if (env->sc_has_icmp6) {
124 event_del(&env->sc_icmp6_send.ev);
125 event_del(&env->sc_icmp6_recv.ev);
126 }
127 }
128
129 void
hce_launch_checks(int fd,short event,void * arg)130 hce_launch_checks(int fd, short event, void *arg)
131 {
132 struct host *host;
133 struct table *table;
134 struct timeval tv;
135
136 /*
137 * notify pfe checks are done and schedule next check
138 */
139 proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
140 TAILQ_FOREACH(table, env->sc_tables, entry) {
141 TAILQ_FOREACH(host, &table->hosts, entry) {
142 if ((host->flags & F_CHECK_DONE) == 0)
143 host->he = HCE_INTERVAL_TIMEOUT;
144 if (event_initialized(&host->cte.ev)) {
145 event_del(&host->cte.ev);
146 close(host->cte.s);
147 }
148 host->cte.s = -1;
149 }
150 }
151
152 getmonotime(&tv);
153
154 TAILQ_FOREACH(table, env->sc_tables, entry) {
155 if (table->conf.flags & F_DISABLE)
156 continue;
157 if (table->conf.skip_cnt) {
158 if (table->skipped++ > table->conf.skip_cnt)
159 table->skipped = 0;
160 if (table->skipped != 1)
161 continue;
162 }
163 if (table->conf.check == CHECK_NOCHECK)
164 fatalx("%s: unknown check type", __func__);
165
166 TAILQ_FOREACH(host, &table->hosts, entry) {
167 if (host->flags & F_DISABLE || host->conf.parentid)
168 continue;
169 bcopy(&tv, &host->cte.tv_start,
170 sizeof(host->cte.tv_start));
171 switch (table->conf.check) {
172 case CHECK_ICMP:
173 schedule_icmp(env, host);
174 break;
175 case CHECK_SCRIPT:
176 check_script(env, host);
177 break;
178 default:
179 /* Any other TCP-style checks */
180 host->last_up = host->up;
181 host->cte.host = host;
182 host->cte.table = table;
183 check_tcp(&host->cte);
184 break;
185 }
186 }
187 }
188 check_icmp(env, &tv);
189
190 bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
191 evtimer_add(&env->sc_ev, &tv);
192 }
193
194 void
hce_notify_done(struct host * host,enum host_error he)195 hce_notify_done(struct host *host, enum host_error he)
196 {
197 struct table *table;
198 struct ctl_status st;
199 struct timeval tv_now, tv_dur;
200 u_long duration;
201 u_int logopt = RELAYD_OPT_LOGHOSTCHECK;
202 struct host *h, *hostnst;
203 int hostup;
204 const char *msg;
205 char *codemsg = NULL;
206
207 if ((hostnst = host_find(env, host->conf.id)) == NULL)
208 fatalx("%s: desynchronized", __func__);
209
210 if ((table = table_find(env, host->conf.tableid)) == NULL)
211 fatalx("%s: invalid table id", __func__);
212
213 if (hostnst->flags & F_DISABLE) {
214 if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
215 log_info("host %s, check %s%s (ignoring result, "
216 "host disabled)",
217 host->conf.name, table_check(table->conf.check),
218 (table->conf.flags & F_TLS) ? " use tls" : "");
219 }
220 host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
221 return;
222 }
223
224 hostup = host->up;
225 host->he = he;
226
227 if (host->up == HOST_DOWN && host->retry_cnt) {
228 log_debug("%s: host %s retry %d", __func__,
229 host->conf.name, host->retry_cnt);
230 host->up = host->last_up;
231 host->retry_cnt--;
232 } else
233 host->retry_cnt = host->conf.retry;
234 if (host->up != HOST_UNKNOWN) {
235 host->check_cnt++;
236 if (host->up == HOST_UP)
237 host->up_cnt++;
238 }
239 st.id = host->conf.id;
240 st.up = host->up;
241 st.check_cnt = host->check_cnt;
242 st.retry_cnt = host->retry_cnt;
243 st.he = he;
244 host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
245 msg = host_error(he);
246 if (msg)
247 log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
248
249 proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
250 if (host->up != host->last_up)
251 logopt = RELAYD_OPT_LOGUPDATE;
252
253 getmonotime(&tv_now);
254 timersub(&tv_now, &host->cte.tv_start, &tv_dur);
255 if (timercmp(&host->cte.tv_start, &tv_dur, >))
256 duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
257 else
258 duration = 0;
259
260 if (env->sc_conf.opts & logopt) {
261 if (host->code > 0)
262 asprintf(&codemsg, ",%d", host->code);
263 log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, "
264 "availability %s",
265 host->conf.name, table_check(table->conf.check),
266 (table->conf.flags & F_TLS) ? " use tls" : "", duration,
267 msg, (codemsg != NULL) ? codemsg : "",
268 host_status(host->last_up), host_status(host->up),
269 print_availability(host->check_cnt, host->up_cnt));
270 free(codemsg);
271 }
272
273 host->last_up = host->up;
274
275 if (SLIST_EMPTY(&host->children))
276 return;
277
278 /* Notify for all other hosts that inherit the state from this one */
279 SLIST_FOREACH(h, &host->children, child) {
280 h->up = hostup;
281 hce_notify_done(h, he);
282 }
283 }
284
285 int
hce_dispatch_pfe(int fd,struct privsep_proc * p,struct imsg * imsg)286 hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
287 {
288 objid_t id;
289 struct host *host;
290 struct table *table;
291
292 switch (imsg->hdr.type) {
293 case IMSG_HOST_DISABLE:
294 memcpy(&id, imsg->data, sizeof(id));
295 if ((host = host_find(env, id)) == NULL)
296 fatalx("%s: desynchronized", __func__);
297 host->flags |= F_DISABLE;
298 host->up = HOST_UNKNOWN;
299 host->check_cnt = 0;
300 host->up_cnt = 0;
301 host->he = HCE_NONE;
302 break;
303 case IMSG_HOST_ENABLE:
304 memcpy(&id, imsg->data, sizeof(id));
305 if ((host = host_find(env, id)) == NULL)
306 fatalx("%s: desynchronized", __func__);
307 host->flags &= ~(F_DISABLE);
308 host->up = HOST_UNKNOWN;
309 host->he = HCE_NONE;
310 break;
311 case IMSG_TABLE_DISABLE:
312 memcpy(&id, imsg->data, sizeof(id));
313 if ((table = table_find(env, id)) == NULL)
314 fatalx("%s: desynchronized", __func__);
315 table->conf.flags |= F_DISABLE;
316 TAILQ_FOREACH(host, &table->hosts, entry)
317 host->up = HOST_UNKNOWN;
318 break;
319 case IMSG_TABLE_ENABLE:
320 memcpy(&id, imsg->data, sizeof(id));
321 if ((table = table_find(env, id)) == NULL)
322 fatalx("%s: desynchronized", __func__);
323 table->conf.flags &= ~(F_DISABLE);
324 TAILQ_FOREACH(host, &table->hosts, entry)
325 host->up = HOST_UNKNOWN;
326 break;
327 case IMSG_CTL_POLL:
328 evtimer_del(&env->sc_ev);
329 TAILQ_FOREACH(table, env->sc_tables, entry)
330 table->skipped = 0;
331 hce_launch_checks(-1, EV_TIMEOUT, env);
332 break;
333 default:
334 return (-1);
335 }
336
337 return (0);
338 }
339
340 int
hce_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)341 hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
342 {
343 struct ctl_script scr;
344
345 switch (imsg->hdr.type) {
346 case IMSG_SCRIPT:
347 IMSG_SIZE_CHECK(imsg, &scr);
348 bcopy(imsg->data, &scr, sizeof(scr));
349 script_done(env, &scr);
350 break;
351 case IMSG_CFG_TABLE:
352 config_gettable(env, imsg);
353 break;
354 case IMSG_CFG_HOST:
355 config_gethost(env, imsg);
356 break;
357 case IMSG_CFG_DONE:
358 config_getcfg(env, imsg);
359 break;
360 case IMSG_CTL_START:
361 hce_setup_events();
362 break;
363 case IMSG_CTL_RESET:
364 config_getreset(env, imsg);
365 break;
366 default:
367 return (-1);
368 }
369
370 return (0);
371 }
372
373 int
hce_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)374 hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
375 {
376 switch (imsg->hdr.type) {
377 default:
378 break;
379 }
380
381 return (-1);
382 }
383