1 /* $OpenBSD: pfe.c,v 1.55 2009/04/17 09:47:06 reyk 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/stat.h> 21 #include <sys/socket.h> 22 #include <sys/un.h> 23 24 #include <net/if.h> 25 26 #include <errno.h> 27 #include <event.h> 28 #include <fcntl.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <pwd.h> 33 34 #include <openssl/ssl.h> 35 36 #include "relayd.h" 37 38 void pfe_sig_handler(int sig, short, void *); 39 void pfe_shutdown(void); 40 void pfe_setup_events(void); 41 void pfe_disable_events(void); 42 void pfe_dispatch_imsg(int, short, void *); 43 void pfe_dispatch_parent(int, short, void *); 44 void pfe_dispatch_relay(int, short, void *); 45 void pfe_sync(void); 46 void pfe_statistics(int, short, void *); 47 48 static struct relayd *env = NULL; 49 50 struct imsgbuf *ibuf_main; 51 struct imsgbuf *ibuf_hce; 52 struct imsgbuf *ibuf_relay; 53 54 void 55 pfe_sig_handler(int sig, short event, void *arg) 56 { 57 switch (sig) { 58 case SIGINT: 59 case SIGTERM: 60 pfe_shutdown(); 61 break; 62 default: 63 fatalx("pfe_sig_handler: unexpected signal"); 64 } 65 } 66 67 pid_t 68 pfe(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], 69 int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2], 70 int pipe_pfe2relay[RELAY_MAXPROC][2]) 71 { 72 pid_t pid; 73 struct passwd *pw; 74 struct event ev_sigint; 75 struct event ev_sigterm; 76 int i; 77 size_t size; 78 79 switch (pid = fork()) { 80 case -1: 81 fatal("pfe: cannot fork"); 82 case 0: 83 break; 84 default: 85 return (pid); 86 } 87 88 env = x_env; 89 purge_config(env, PURGE_PROTOS); 90 91 if (control_init() == -1) 92 fatalx("pfe: control socket setup failed"); 93 94 init_filter(env); 95 init_tables(env); 96 97 if ((pw = getpwnam(RELAYD_USER)) == NULL) 98 fatal("pfe: getpwnam"); 99 100 #ifndef DEBUG 101 if (chroot(pw->pw_dir) == -1) 102 fatal("pfe: chroot"); 103 if (chdir("/") == -1) 104 fatal("pfe: chdir(\"/\")"); 105 #else 106 #warning disabling privilege revocation and chroot in DEBUG mode 107 #endif 108 109 setproctitle("pf update engine"); 110 relayd_process = PROC_PFE; 111 112 #ifndef DEBUG 113 if (setgroups(1, &pw->pw_gid) || 114 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 115 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 116 fatal("pfe: cannot drop privileges"); 117 #endif 118 119 event_init(); 120 121 signal_set(&ev_sigint, SIGINT, pfe_sig_handler, NULL); 122 signal_set(&ev_sigterm, SIGTERM, pfe_sig_handler, NULL); 123 signal_add(&ev_sigint, NULL); 124 signal_add(&ev_sigterm, NULL); 125 signal(SIGPIPE, SIG_IGN); 126 signal(SIGHUP, SIG_IGN); 127 128 /* setup pipes */ 129 close(pipe_pfe2hce[0]); 130 close(pipe_parent2pfe[0]); 131 close(pipe_parent2hce[0]); 132 close(pipe_parent2hce[1]); 133 for (i = 0; i < env->sc_prefork_relay; i++) { 134 close(pipe_parent2relay[i][0]); 135 close(pipe_parent2relay[i][1]); 136 close(pipe_pfe2relay[i][0]); 137 } 138 139 size = sizeof(struct imsgbuf); 140 if ((ibuf_hce = calloc(1, size)) == NULL || 141 (ibuf_relay = calloc(env->sc_prefork_relay, size)) == NULL || 142 (ibuf_main = calloc(1, size)) == NULL) 143 fatal("pfe"); 144 145 imsg_init(ibuf_hce, pipe_pfe2hce[1], pfe_dispatch_imsg); 146 imsg_init(ibuf_main, pipe_parent2pfe[1], pfe_dispatch_parent); 147 for (i = 0; i < env->sc_prefork_relay; i++) 148 imsg_init(&ibuf_relay[i], pipe_pfe2relay[i][1], 149 pfe_dispatch_relay); 150 151 ibuf_main->events = EV_READ; 152 event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events, 153 ibuf_main->handler, ibuf_main); 154 event_add(&ibuf_main->ev, NULL); 155 156 pfe_setup_events(); 157 158 TAILQ_INIT(&ctl_conns); 159 160 if (control_listen(env, ibuf_main, ibuf_hce) == -1) 161 fatalx("pfe: control socket listen failed"); 162 163 /* Initial sync */ 164 pfe_sync(); 165 166 event_dispatch(); 167 pfe_shutdown(); 168 169 return (0); 170 } 171 172 void 173 pfe_shutdown(void) 174 { 175 flush_rulesets(env); 176 log_info("pf update engine exiting"); 177 _exit(0); 178 } 179 180 void 181 pfe_setup_events(void) 182 { 183 int i; 184 struct imsgbuf *ibuf; 185 struct timeval tv; 186 187 ibuf_hce->events = EV_READ; 188 event_set(&ibuf_hce->ev, ibuf_hce->fd, ibuf_hce->events, 189 ibuf_hce->handler, ibuf_hce); 190 event_add(&ibuf_hce->ev, NULL); 191 192 for (i = 0; i < env->sc_prefork_relay; i++) { 193 ibuf = &ibuf_relay[i]; 194 195 ibuf->events = EV_READ; 196 event_set(&ibuf->ev, ibuf->fd, ibuf->events, 197 ibuf->handler, ibuf); 198 event_add(&ibuf->ev, NULL); 199 } 200 201 /* Schedule statistics timer */ 202 evtimer_set(&env->sc_statev, pfe_statistics, NULL); 203 bcopy(&env->sc_statinterval, &tv, sizeof(tv)); 204 evtimer_add(&env->sc_statev, &tv); 205 } 206 207 void 208 pfe_disable_events(void) 209 { 210 int i; 211 212 event_del(&ibuf_hce->ev); 213 214 for (i = 0; i < env->sc_prefork_relay; i++) 215 event_del(&ibuf_relay[i].ev); 216 217 event_del(&env->sc_statev); 218 } 219 220 void 221 pfe_dispatch_imsg(int fd, short event, void *ptr) 222 { 223 struct imsgbuf *ibuf; 224 struct imsg imsg; 225 ssize_t n; 226 227 struct host *host; 228 struct table *table; 229 struct ctl_status st; 230 231 ibuf = ptr; 232 switch (event) { 233 case EV_READ: 234 if ((n = imsg_read(ibuf)) == -1) 235 fatal("pfe_dispatch_imsg: imsg_read_error"); 236 if (n == 0) { 237 /* this pipe is dead, so remove the event handler */ 238 event_del(&ibuf->ev); 239 event_loopexit(NULL); 240 return; 241 } 242 break; 243 case EV_WRITE: 244 if (msgbuf_write(&ibuf->w) == -1) 245 fatal("pfe_dispatch_imsg: msgbuf_write"); 246 imsg_event_add(ibuf); 247 return; 248 default: 249 fatalx("pfe_dispatch_imsg: unknown event"); 250 } 251 252 for (;;) { 253 if ((n = imsg_get(ibuf, &imsg)) == -1) 254 fatal("pfe_dispatch_imsg: imsg_read error"); 255 if (n == 0) 256 break; 257 258 control_imsg_forward(&imsg); 259 switch (imsg.hdr.type) { 260 case IMSG_HOST_STATUS: 261 if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(st)) 262 fatalx("pfe_dispatch_imsg: invalid request"); 263 memcpy(&st, imsg.data, sizeof(st)); 264 if ((host = host_find(env, st.id)) == NULL) 265 fatalx("pfe_dispatch_imsg: invalid host id"); 266 host->he = st.he; 267 if (host->flags & F_DISABLE) 268 break; 269 host->retry_cnt = st.retry_cnt; 270 if (st.up != HOST_UNKNOWN) { 271 host->check_cnt++; 272 if (st.up == HOST_UP) 273 host->up_cnt++; 274 } 275 if (host->check_cnt != st.check_cnt) { 276 log_debug("pfe_dispatch_imsg: host %d => %d", 277 host->conf.id, host->up); 278 fatalx("pfe_dispatch_imsg: desynchronized"); 279 } 280 281 if (host->up == st.up) 282 break; 283 284 /* Forward to relay engine(s) */ 285 for (n = 0; n < env->sc_prefork_relay; n++) 286 imsg_compose(&ibuf_relay[n], 287 IMSG_HOST_STATUS, 0, 0, -1, &st, 288 sizeof(st)); 289 290 if ((table = table_find(env, host->conf.tableid)) 291 == NULL) 292 fatalx("pfe_dispatch_imsg: invalid table id"); 293 294 log_debug("pfe_dispatch_imsg: state %d for host %u %s", 295 st.up, host->conf.id, host->conf.name); 296 297 /* 298 * Do not change the table state when the host 299 * state switches between UNKNOWN and DOWN. 300 */ 301 if (HOST_ISUP(st.up)) { 302 table->conf.flags |= F_CHANGED; 303 table->up++; 304 host->flags |= F_ADD; 305 host->flags &= ~(F_DEL); 306 } else if (HOST_ISUP(host->up)) { 307 table->up--; 308 table->conf.flags |= F_CHANGED; 309 host->flags |= F_DEL; 310 host->flags &= ~(F_ADD); 311 } 312 313 host->up = st.up; 314 break; 315 case IMSG_SYNC: 316 pfe_sync(); 317 break; 318 default: 319 log_debug("pfe_dispatch_imsg: unexpected imsg %d", 320 imsg.hdr.type); 321 break; 322 } 323 imsg_free(&imsg); 324 } 325 imsg_event_add(ibuf); 326 } 327 328 void 329 pfe_dispatch_parent(int fd, short event, void * ptr) 330 { 331 struct imsgbuf *ibuf; 332 struct imsg imsg; 333 ssize_t n; 334 335 static struct rdr *rdr = NULL; 336 static struct table *table = NULL; 337 struct host *host, *parent; 338 struct address *virt; 339 340 ibuf = ptr; 341 switch (event) { 342 case EV_READ: 343 if ((n = imsg_read(ibuf)) == -1) 344 fatal("imsg_read error"); 345 if (n == 0) { 346 /* this pipe is dead, so remove the event handler */ 347 event_del(&ibuf->ev); 348 event_loopexit(NULL); 349 return; 350 } 351 break; 352 case EV_WRITE: 353 if (msgbuf_write(&ibuf->w) == -1) 354 fatal("msgbuf_write"); 355 imsg_event_add(ibuf); 356 return; 357 default: 358 fatalx("pfe_dispatch_parent: unknown event"); 359 } 360 361 for (;;) { 362 if ((n = imsg_get(ibuf, &imsg)) == -1) 363 fatal("pfe_dispatch_parent: imsg_read error"); 364 if (n == 0) 365 break; 366 367 switch (imsg.hdr.type) { 368 case IMSG_RECONF: 369 log_debug("pfe: reloading configuration"); 370 if (imsg.hdr.len != 371 sizeof(struct relayd) + IMSG_HEADER_SIZE) 372 fatalx("corrupted reload data"); 373 pfe_disable_events(); 374 purge_config(env, PURGE_RDRS|PURGE_TABLES); 375 merge_config(env, (struct relayd *)imsg.data); 376 /* 377 * no relays when reconfiguring yet. 378 */ 379 env->sc_relays = NULL; 380 env->sc_protos = NULL; 381 382 env->sc_tables = calloc(1, sizeof(*env->sc_tables)); 383 env->sc_rdrs = calloc(1, sizeof(*env->sc_rdrs)); 384 if (env->sc_tables == NULL || env->sc_rdrs == NULL) 385 fatal(NULL); 386 387 TAILQ_INIT(env->sc_tables); 388 TAILQ_INIT(env->sc_rdrs); 389 break; 390 case IMSG_RECONF_TABLE: 391 if ((table = calloc(1, sizeof(*table))) == NULL) 392 fatal(NULL); 393 memcpy(&table->conf, imsg.data, sizeof(table->conf)); 394 TAILQ_INIT(&table->hosts); 395 TAILQ_INSERT_TAIL(env->sc_tables, table, entry); 396 break; 397 case IMSG_RECONF_HOST: 398 if ((host = calloc(1, sizeof(*host))) == NULL) 399 fatal(NULL); 400 memcpy(&host->conf, imsg.data, sizeof(host->conf)); 401 host->tablename = table->conf.name; 402 TAILQ_INSERT_TAIL(&table->hosts, host, entry); 403 if (host->conf.parentid) { 404 parent = host_find(env, host->conf.parentid); 405 SLIST_INSERT_HEAD(&parent->children, 406 host, child); 407 } 408 break; 409 case IMSG_RECONF_RDR: 410 if ((rdr = calloc(1, sizeof(*rdr))) == NULL) 411 fatal(NULL); 412 memcpy(&rdr->conf, imsg.data, 413 sizeof(rdr->conf)); 414 rdr->table = table_find(env, 415 rdr->conf.table_id); 416 if (rdr->conf.backup_id == EMPTY_TABLE) 417 rdr->backup = &env->sc_empty_table; 418 else 419 rdr->backup = table_find(env, 420 rdr->conf.backup_id); 421 if (rdr->table == NULL || rdr->backup == NULL) 422 fatal("pfe_dispatch_parent:" 423 " corrupted configuration"); 424 log_debug("pfe_dispatch_parent: rdr->table: %s", 425 rdr->table->conf.name); 426 log_debug("pfe_dispatch_parent: rdr->backup: %s", 427 rdr->backup->conf.name); 428 TAILQ_INIT(&rdr->virts); 429 TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry); 430 break; 431 case IMSG_RECONF_VIRT: 432 if ((virt = calloc(1, sizeof(*virt))) == NULL) 433 fatal(NULL); 434 memcpy(virt, imsg.data, sizeof(*virt)); 435 TAILQ_INSERT_TAIL(&rdr->virts, virt, entry); 436 break; 437 case IMSG_RECONF_END: 438 log_warnx("pfe: configuration reloaded"); 439 init_tables(env); 440 pfe_setup_events(); 441 pfe_sync(); 442 break; 443 default: 444 log_debug("pfe_dispatch_parent: unexpected imsg %d", 445 imsg.hdr.type); 446 break; 447 } 448 imsg_free(&imsg); 449 } 450 imsg_event_add(ibuf); 451 } 452 453 void 454 pfe_dispatch_relay(int fd, short event, void * ptr) 455 { 456 struct imsgbuf *ibuf; 457 struct imsg imsg; 458 ssize_t n; 459 struct ctl_natlook cnl; 460 struct ctl_stats crs; 461 struct relay *rlay; 462 463 ibuf = ptr; 464 switch (event) { 465 case EV_READ: 466 if ((n = imsg_read(ibuf)) == -1) 467 fatal("imsg_read error"); 468 if (n == 0) { 469 /* this pipe is dead, so remove the event handler */ 470 event_del(&ibuf->ev); 471 event_loopexit(NULL); 472 return; 473 } 474 break; 475 case EV_WRITE: 476 if (msgbuf_write(&ibuf->w) == -1) 477 fatal("msgbuf_write"); 478 imsg_event_add(ibuf); 479 return; 480 default: 481 fatalx("unknown event"); 482 } 483 484 for (;;) { 485 if ((n = imsg_get(ibuf, &imsg)) == -1) 486 fatal("pfe_dispatch_relay: imsg_read error"); 487 if (n == 0) 488 break; 489 490 switch (imsg.hdr.type) { 491 case IMSG_NATLOOK: 492 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(cnl)) 493 fatalx("invalid imsg header len"); 494 bcopy(imsg.data, &cnl, sizeof(cnl)); 495 if (cnl.proc > env->sc_prefork_relay) 496 fatalx("pfe_dispatch_relay: " 497 "invalid relay proc"); 498 if (natlook(env, &cnl) != 0) 499 cnl.in = -1; 500 imsg_compose(&ibuf_relay[cnl.proc], IMSG_NATLOOK, 0, 0, 501 -1, &cnl, sizeof(cnl)); 502 break; 503 case IMSG_STATISTICS: 504 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(crs)) 505 fatalx("invalid imsg header len"); 506 bcopy(imsg.data, &crs, sizeof(crs)); 507 if (crs.proc > env->sc_prefork_relay) 508 fatalx("pfe_dispatch_relay: " 509 "invalid relay proc"); 510 if ((rlay = relay_find(env, crs.id)) == NULL) 511 fatalx("pfe_dispatch_relay: invalid relay id"); 512 bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs)); 513 rlay->rl_stats[crs.proc].interval = 514 env->sc_statinterval.tv_sec; 515 break; 516 default: 517 log_debug("pfe_dispatch_relay: unexpected imsg %d", 518 imsg.hdr.type); 519 break; 520 } 521 imsg_free(&imsg); 522 } 523 imsg_event_add(ibuf); 524 } 525 526 void 527 show(struct ctl_conn *c) 528 { 529 struct rdr *rdr; 530 struct host *host; 531 struct relay *rlay; 532 533 if (env->sc_rdrs == NULL) 534 goto relays; 535 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { 536 imsg_compose(&c->ibuf, IMSG_CTL_RDR, 0, 0, -1, 537 rdr, sizeof(*rdr)); 538 if (rdr->conf.flags & F_DISABLE) 539 continue; 540 541 imsg_compose(&c->ibuf, IMSG_CTL_RDR_STATS, 0, 0, -1, 542 &rdr->stats, sizeof(rdr->stats)); 543 544 imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1, 545 rdr->table, sizeof(*rdr->table)); 546 if (!(rdr->table->conf.flags & F_DISABLE)) 547 TAILQ_FOREACH(host, &rdr->table->hosts, entry) 548 imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1, 549 host, sizeof(*host)); 550 551 if (rdr->backup->conf.id == EMPTY_TABLE) 552 continue; 553 imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1, 554 rdr->backup, sizeof(*rdr->backup)); 555 if (!(rdr->backup->conf.flags & F_DISABLE)) 556 TAILQ_FOREACH(host, &rdr->backup->hosts, entry) 557 imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1, 558 host, sizeof(*host)); 559 } 560 relays: 561 if (env->sc_relays == NULL) 562 goto end; 563 TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { 564 rlay->rl_stats[env->sc_prefork_relay].id = EMPTY_ID; 565 imsg_compose(&c->ibuf, IMSG_CTL_RELAY, 0, 0, -1, 566 rlay, sizeof(*rlay)); 567 imsg_compose(&c->ibuf, IMSG_CTL_RELAY_STATS, 0, 0, -1, 568 &rlay->rl_stats, sizeof(rlay->rl_stats)); 569 570 if (rlay->rl_dsttable == NULL) 571 continue; 572 imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1, 573 rlay->rl_dsttable, sizeof(*rlay->rl_dsttable)); 574 if (!(rlay->rl_dsttable->conf.flags & F_DISABLE)) 575 TAILQ_FOREACH(host, &rlay->rl_dsttable->hosts, entry) 576 imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1, 577 host, sizeof(*host)); 578 } 579 end: 580 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 581 } 582 583 void 584 show_sessions(struct ctl_conn *c) 585 { 586 int n, proc, done; 587 struct imsg imsg; 588 589 for (proc = 0; proc < env->sc_prefork_relay; proc++) { 590 /* 591 * Request all the running sessions from the process 592 */ 593 imsg_compose(&ibuf_relay[proc], 594 IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); 595 while (ibuf_relay[proc].w.queued) 596 if (msgbuf_write(&ibuf_relay[proc].w) < 0) 597 fatalx("write error"); 598 599 /* 600 * Wait for the reply and forward the messages to the 601 * control connection. 602 */ 603 done = 0; 604 while (!done) { 605 do { 606 if ((n = imsg_read(&ibuf_relay[proc])) == -1) 607 fatalx("imsg_read error"); 608 } while (n == -2); /* handle non-blocking I/O */ 609 while (!done) { 610 if ((n = imsg_get(&ibuf_relay[proc], 611 &imsg)) == -1) 612 fatalx("imsg_get error"); 613 if (n == 0) 614 break; 615 switch (imsg.hdr.type) { 616 case IMSG_CTL_SESSION: 617 imsg_compose(&c->ibuf, 618 IMSG_CTL_SESSION, proc, 0, -1, 619 imsg.data, sizeof(struct session)); 620 break; 621 case IMSG_CTL_END: 622 done = 1; 623 break; 624 default: 625 fatalx("wrong message for session"); 626 break; 627 } 628 imsg_free(&imsg); 629 } 630 } 631 } 632 633 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 634 } 635 636 int 637 disable_rdr(struct ctl_conn *c, struct ctl_id *id) 638 { 639 struct rdr *rdr; 640 641 if (id->id == EMPTY_ID) 642 rdr = rdr_findbyname(env, id->name); 643 else 644 rdr = rdr_find(env, id->id); 645 if (rdr == NULL) 646 return (-1); 647 id->id = rdr->conf.id; 648 649 if (rdr->conf.flags & F_DISABLE) 650 return (0); 651 652 rdr->conf.flags |= F_DISABLE; 653 rdr->conf.flags &= ~(F_ADD); 654 rdr->conf.flags |= F_DEL; 655 rdr->table->conf.flags |= F_DISABLE; 656 log_debug("disable_rdr: disabled rdr %d", rdr->conf.id); 657 pfe_sync(); 658 return (0); 659 } 660 661 int 662 enable_rdr(struct ctl_conn *c, struct ctl_id *id) 663 { 664 struct rdr *rdr; 665 struct ctl_id eid; 666 667 if (id->id == EMPTY_ID) 668 rdr = rdr_findbyname(env, id->name); 669 else 670 rdr = rdr_find(env, id->id); 671 if (rdr == NULL) 672 return (-1); 673 id->id = rdr->conf.id; 674 675 if (!(rdr->conf.flags & F_DISABLE)) 676 return (0); 677 678 rdr->conf.flags &= ~(F_DISABLE); 679 rdr->conf.flags &= ~(F_DEL); 680 rdr->conf.flags |= F_ADD; 681 log_debug("enable_rdr: enabled rdr %d", rdr->conf.id); 682 683 bzero(&eid, sizeof(eid)); 684 685 /* XXX: we're syncing twice */ 686 eid.id = rdr->table->conf.id; 687 if (enable_table(c, &eid) == -1) 688 return (-1); 689 if (rdr->backup->conf.id == EMPTY_ID) 690 return (0); 691 eid.id = rdr->backup->conf.id; 692 if (enable_table(c, &eid) == -1) 693 return (-1); 694 return (0); 695 } 696 697 int 698 disable_table(struct ctl_conn *c, struct ctl_id *id) 699 { 700 struct table *table; 701 struct rdr *rdr; 702 struct host *host; 703 704 if (id->id == EMPTY_ID) 705 table = table_findbyname(env, id->name); 706 else 707 table = table_find(env, id->id); 708 if (table == NULL) 709 return (-1); 710 id->id = table->conf.id; 711 if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL) 712 fatalx("disable_table: desynchronised"); 713 714 if (table->conf.flags & F_DISABLE) 715 return (0); 716 table->conf.flags |= (F_DISABLE|F_CHANGED); 717 table->up = 0; 718 TAILQ_FOREACH(host, &table->hosts, entry) 719 host->up = HOST_UNKNOWN; 720 imsg_compose(ibuf_hce, IMSG_TABLE_DISABLE, 0, 0, -1, 721 &table->conf.id, sizeof(table->conf.id)); 722 log_debug("disable_table: disabled table %d", table->conf.id); 723 pfe_sync(); 724 return (0); 725 } 726 727 int 728 enable_table(struct ctl_conn *c, struct ctl_id *id) 729 { 730 struct rdr *rdr; 731 struct table *table; 732 struct host *host; 733 734 if (id->id == EMPTY_ID) 735 table = table_findbyname(env, id->name); 736 else 737 table = table_find(env, id->id); 738 if (table == NULL) 739 return (-1); 740 id->id = table->conf.id; 741 742 if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL) 743 fatalx("enable_table: desynchronised"); 744 745 if (!(table->conf.flags & F_DISABLE)) 746 return (0); 747 table->conf.flags &= ~(F_DISABLE); 748 table->conf.flags |= F_CHANGED; 749 table->up = 0; 750 TAILQ_FOREACH(host, &table->hosts, entry) 751 host->up = HOST_UNKNOWN; 752 imsg_compose(ibuf_hce, IMSG_TABLE_ENABLE, 0, 0, -1, 753 &table->conf.id, sizeof(table->conf.id)); 754 log_debug("enable_table: enabled table %d", table->conf.id); 755 pfe_sync(); 756 return (0); 757 } 758 759 int 760 disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host) 761 { 762 struct host *h; 763 struct table *table; 764 int n; 765 766 if (host == NULL) { 767 if (id->id == EMPTY_ID) 768 host = host_findbyname(env, id->name); 769 else 770 host = host_find(env, id->id); 771 if (host == NULL || host->conf.parentid) 772 return (-1); 773 } 774 id->id = host->conf.id; 775 776 if (host->flags & F_DISABLE) 777 return (0); 778 779 if (host->up == HOST_UP) { 780 if ((table = table_find(env, host->conf.tableid)) == NULL) 781 fatalx("disable_host: invalid table id"); 782 table->up--; 783 table->conf.flags |= F_CHANGED; 784 } 785 786 host->up = HOST_UNKNOWN; 787 host->flags |= F_DISABLE; 788 host->flags |= F_DEL; 789 host->flags &= ~(F_ADD); 790 host->check_cnt = 0; 791 host->up_cnt = 0; 792 793 imsg_compose(ibuf_hce, IMSG_HOST_DISABLE, 0, 0, -1, 794 &host->conf.id, sizeof(host->conf.id)); 795 /* Forward to relay engine(s) */ 796 for (n = 0; n < env->sc_prefork_relay; n++) 797 imsg_compose(&ibuf_relay[n], 798 IMSG_HOST_DISABLE, 0, 0, -1, 799 &host->conf.id, sizeof(host->conf.id)); 800 log_debug("disable_host: disabled host %d", host->conf.id); 801 802 if (!host->conf.parentid) { 803 /* Disable all children */ 804 SLIST_FOREACH(h, &host->children, child) 805 disable_host(c, id, h); 806 pfe_sync(); 807 } 808 return (0); 809 } 810 811 int 812 enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host) 813 { 814 struct host *h; 815 int n; 816 817 if (host == NULL) { 818 if (id->id == EMPTY_ID) 819 host = host_findbyname(env, id->name); 820 else 821 host = host_find(env, id->id); 822 if (host == NULL || host->conf.parentid) 823 return (-1); 824 } 825 id->id = host->conf.id; 826 827 if (!(host->flags & F_DISABLE)) 828 return (0); 829 830 host->up = HOST_UNKNOWN; 831 host->flags &= ~(F_DISABLE); 832 host->flags &= ~(F_DEL); 833 host->flags &= ~(F_ADD); 834 835 imsg_compose(ibuf_hce, IMSG_HOST_ENABLE, 0, 0, -1, 836 &host->conf.id, sizeof (host->conf.id)); 837 /* Forward to relay engine(s) */ 838 for (n = 0; n < env->sc_prefork_relay; n++) 839 imsg_compose(&ibuf_relay[n], 840 IMSG_HOST_ENABLE, 0, 0, -1, 841 &host->conf.id, sizeof(host->conf.id)); 842 log_debug("enable_host: enabled host %d", host->conf.id); 843 844 if (!host->conf.parentid) { 845 /* Enable all children */ 846 SLIST_FOREACH(h, &host->children, child) 847 enable_host(c, id, h); 848 pfe_sync(); 849 } 850 return (0); 851 } 852 853 void 854 pfe_sync(void) 855 { 856 struct rdr *rdr; 857 struct table *active; 858 struct table *table; 859 struct ctl_id id; 860 struct imsg imsg; 861 struct ctl_demote demote; 862 863 bzero(&id, sizeof(id)); 864 bzero(&imsg, sizeof(imsg)); 865 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { 866 rdr->conf.flags &= ~(F_BACKUP); 867 rdr->conf.flags &= ~(F_DOWN); 868 869 if (rdr->conf.flags & F_DISABLE || 870 (rdr->table->up == 0 && rdr->backup->up == 0)) { 871 rdr->conf.flags |= F_DOWN; 872 active = NULL; 873 } else if (rdr->table->up == 0 && rdr->backup->up > 0) { 874 rdr->conf.flags |= F_BACKUP; 875 active = rdr->backup; 876 active->conf.flags |= 877 rdr->table->conf.flags & F_CHANGED; 878 active->conf.flags |= 879 rdr->backup->conf.flags & F_CHANGED; 880 } else 881 active = rdr->table; 882 883 if (active != NULL && active->conf.flags & F_CHANGED) { 884 id.id = active->conf.id; 885 imsg.hdr.type = IMSG_CTL_TABLE_CHANGED; 886 imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE; 887 imsg.data = &id; 888 sync_table(env, rdr, active); 889 control_imsg_forward(&imsg); 890 } 891 892 if (rdr->conf.flags & F_DOWN) { 893 if (rdr->conf.flags & F_ACTIVE_RULESET) { 894 flush_table(env, rdr); 895 log_debug("pfe_sync: disabling ruleset"); 896 rdr->conf.flags &= ~(F_ACTIVE_RULESET); 897 id.id = rdr->conf.id; 898 imsg.hdr.type = IMSG_CTL_PULL_RULESET; 899 imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE; 900 imsg.data = &id; 901 sync_ruleset(env, rdr, 0); 902 control_imsg_forward(&imsg); 903 } 904 } else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) { 905 log_debug("pfe_sync: enabling ruleset"); 906 rdr->conf.flags |= F_ACTIVE_RULESET; 907 id.id = rdr->conf.id; 908 imsg.hdr.type = IMSG_CTL_PUSH_RULESET; 909 imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE; 910 imsg.data = &id; 911 sync_ruleset(env, rdr, 1); 912 control_imsg_forward(&imsg); 913 } 914 } 915 916 TAILQ_FOREACH(table, env->sc_tables, entry) { 917 /* 918 * clean up change flag. 919 */ 920 table->conf.flags &= ~(F_CHANGED); 921 922 /* 923 * handle demotion. 924 */ 925 if ((table->conf.flags & F_DEMOTE) == 0) 926 continue; 927 demote.level = 0; 928 if (table->up && table->conf.flags & F_DEMOTED) { 929 demote.level = -1; 930 table->conf.flags &= ~F_DEMOTED; 931 } 932 else if (!table->up && !(table->conf.flags & F_DEMOTED)) { 933 demote.level = 1; 934 table->conf.flags |= F_DEMOTED; 935 } 936 if (demote.level == 0) 937 continue; 938 log_debug("pfe_sync: demote %d table '%s' group '%s'", 939 demote.level, table->conf.name, table->conf.demote_group); 940 (void)strlcpy(demote.group, table->conf.demote_group, 941 sizeof(demote.group)); 942 imsg_compose(ibuf_main, IMSG_DEMOTE, 0, 0, -1, 943 &demote, sizeof(demote)); 944 } 945 } 946 947 void 948 pfe_statistics(int fd, short events, void *arg) 949 { 950 struct rdr *rdr; 951 struct ctl_stats *cur; 952 struct timeval tv, tv_now; 953 int resethour, resetday; 954 u_long cnt; 955 956 timerclear(&tv); 957 if (gettimeofday(&tv_now, NULL) == -1) 958 fatal("pfe_statistics: gettimeofday"); 959 960 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { 961 cnt = check_table(env, rdr, rdr->table); 962 if (rdr->conf.backup_id != EMPTY_TABLE) 963 cnt += check_table(env, rdr, rdr->backup); 964 965 resethour = resetday = 0; 966 967 cur = &rdr->stats; 968 cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0; 969 970 cur->cnt = cnt; 971 cur->tick++; 972 cur->avg = (cur->last + cur->avg) / 2; 973 cur->last_hour += cur->last; 974 if ((cur->tick % (3600 / env->sc_statinterval.tv_sec)) == 0) { 975 cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2; 976 resethour++; 977 } 978 cur->last_day += cur->last; 979 if ((cur->tick % (86400 / env->sc_statinterval.tv_sec)) == 0) { 980 cur->avg_day = (cur->last_day + cur->avg_day) / 2; 981 resethour++; 982 } 983 if (resethour) 984 cur->last_hour = 0; 985 if (resetday) 986 cur->last_day = 0; 987 988 rdr->stats.interval = env->sc_statinterval.tv_sec; 989 } 990 991 /* Schedule statistics timer */ 992 evtimer_set(&env->sc_statev, pfe_statistics, NULL); 993 bcopy(&env->sc_statinterval, &tv, sizeof(tv)); 994 evtimer_add(&env->sc_statev, &tv); 995 } 996