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 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 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 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 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 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 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 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 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 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