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