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