1 /* $OpenBSD: pfe.c,v 1.54 2009/04/01 15:05: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; 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 break; 404 case IMSG_RECONF_RDR: 405 if ((rdr = calloc(1, sizeof(*rdr))) == NULL) 406 fatal(NULL); 407 memcpy(&rdr->conf, imsg.data, 408 sizeof(rdr->conf)); 409 rdr->table = table_find(env, 410 rdr->conf.table_id); 411 if (rdr->conf.backup_id == EMPTY_TABLE) 412 rdr->backup = &env->sc_empty_table; 413 else 414 rdr->backup = table_find(env, 415 rdr->conf.backup_id); 416 if (rdr->table == NULL || rdr->backup == NULL) 417 fatal("pfe_dispatch_parent:" 418 " corrupted configuration"); 419 log_debug("pfe_dispatch_parent: rdr->table: %s", 420 rdr->table->conf.name); 421 log_debug("pfe_dispatch_parent: rdr->backup: %s", 422 rdr->backup->conf.name); 423 TAILQ_INIT(&rdr->virts); 424 TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry); 425 break; 426 case IMSG_RECONF_VIRT: 427 if ((virt = calloc(1, sizeof(*virt))) == NULL) 428 fatal(NULL); 429 memcpy(virt, imsg.data, sizeof(*virt)); 430 TAILQ_INSERT_TAIL(&rdr->virts, virt, entry); 431 break; 432 case IMSG_RECONF_END: 433 log_warnx("pfe: configuration reloaded"); 434 init_tables(env); 435 pfe_setup_events(); 436 pfe_sync(); 437 break; 438 default: 439 log_debug("pfe_dispatch_parent: unexpected imsg %d", 440 imsg.hdr.type); 441 break; 442 } 443 imsg_free(&imsg); 444 } 445 imsg_event_add(ibuf); 446 } 447 448 void 449 pfe_dispatch_relay(int fd, short event, void * ptr) 450 { 451 struct imsgbuf *ibuf; 452 struct imsg imsg; 453 ssize_t n; 454 struct ctl_natlook cnl; 455 struct ctl_stats crs; 456 struct relay *rlay; 457 458 ibuf = ptr; 459 switch (event) { 460 case EV_READ: 461 if ((n = imsg_read(ibuf)) == -1) 462 fatal("imsg_read error"); 463 if (n == 0) { 464 /* this pipe is dead, so remove the event handler */ 465 event_del(&ibuf->ev); 466 event_loopexit(NULL); 467 return; 468 } 469 break; 470 case EV_WRITE: 471 if (msgbuf_write(&ibuf->w) == -1) 472 fatal("msgbuf_write"); 473 imsg_event_add(ibuf); 474 return; 475 default: 476 fatalx("unknown event"); 477 } 478 479 for (;;) { 480 if ((n = imsg_get(ibuf, &imsg)) == -1) 481 fatal("pfe_dispatch_relay: imsg_read error"); 482 if (n == 0) 483 break; 484 485 switch (imsg.hdr.type) { 486 case IMSG_NATLOOK: 487 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(cnl)) 488 fatalx("invalid imsg header len"); 489 bcopy(imsg.data, &cnl, sizeof(cnl)); 490 if (cnl.proc > env->sc_prefork_relay) 491 fatalx("pfe_dispatch_relay: " 492 "invalid relay proc"); 493 if (natlook(env, &cnl) != 0) 494 cnl.in = -1; 495 imsg_compose(&ibuf_relay[cnl.proc], IMSG_NATLOOK, 0, 0, 496 -1, &cnl, sizeof(cnl)); 497 break; 498 case IMSG_STATISTICS: 499 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(crs)) 500 fatalx("invalid imsg header len"); 501 bcopy(imsg.data, &crs, sizeof(crs)); 502 if (crs.proc > env->sc_prefork_relay) 503 fatalx("pfe_dispatch_relay: " 504 "invalid relay proc"); 505 if ((rlay = relay_find(env, crs.id)) == NULL) 506 fatalx("pfe_dispatch_relay: invalid relay id"); 507 bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs)); 508 rlay->rl_stats[crs.proc].interval = 509 env->sc_statinterval.tv_sec; 510 break; 511 default: 512 log_debug("pfe_dispatch_relay: unexpected imsg %d", 513 imsg.hdr.type); 514 break; 515 } 516 imsg_free(&imsg); 517 } 518 imsg_event_add(ibuf); 519 } 520 521 void 522 show(struct ctl_conn *c) 523 { 524 struct rdr *rdr; 525 struct host *host; 526 struct relay *rlay; 527 528 if (env->sc_rdrs == NULL) 529 goto relays; 530 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { 531 imsg_compose(&c->ibuf, IMSG_CTL_RDR, 0, 0, -1, 532 rdr, sizeof(*rdr)); 533 if (rdr->conf.flags & F_DISABLE) 534 continue; 535 536 imsg_compose(&c->ibuf, IMSG_CTL_RDR_STATS, 0, 0, -1, 537 &rdr->stats, sizeof(rdr->stats)); 538 539 imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1, 540 rdr->table, sizeof(*rdr->table)); 541 if (!(rdr->table->conf.flags & F_DISABLE)) 542 TAILQ_FOREACH(host, &rdr->table->hosts, entry) 543 imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1, 544 host, sizeof(*host)); 545 546 if (rdr->backup->conf.id == EMPTY_TABLE) 547 continue; 548 imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1, 549 rdr->backup, sizeof(*rdr->backup)); 550 if (!(rdr->backup->conf.flags & F_DISABLE)) 551 TAILQ_FOREACH(host, &rdr->backup->hosts, entry) 552 imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1, 553 host, sizeof(*host)); 554 } 555 relays: 556 if (env->sc_relays == NULL) 557 goto end; 558 TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { 559 rlay->rl_stats[env->sc_prefork_relay].id = EMPTY_ID; 560 imsg_compose(&c->ibuf, IMSG_CTL_RELAY, 0, 0, -1, 561 rlay, sizeof(*rlay)); 562 imsg_compose(&c->ibuf, IMSG_CTL_RELAY_STATS, 0, 0, -1, 563 &rlay->rl_stats, sizeof(rlay->rl_stats)); 564 565 if (rlay->rl_dsttable == NULL) 566 continue; 567 imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1, 568 rlay->rl_dsttable, sizeof(*rlay->rl_dsttable)); 569 if (!(rlay->rl_dsttable->conf.flags & F_DISABLE)) 570 TAILQ_FOREACH(host, &rlay->rl_dsttable->hosts, entry) 571 imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1, 572 host, sizeof(*host)); 573 } 574 end: 575 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 576 } 577 578 void 579 show_sessions(struct ctl_conn *c) 580 { 581 int n, proc, done; 582 struct imsg imsg; 583 584 for (proc = 0; proc < env->sc_prefork_relay; proc++) { 585 /* 586 * Request all the running sessions from the process 587 */ 588 imsg_compose(&ibuf_relay[proc], 589 IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); 590 while (ibuf_relay[proc].w.queued) 591 if (msgbuf_write(&ibuf_relay[proc].w) < 0) 592 fatalx("write error"); 593 594 /* 595 * Wait for the reply and forward the messages to the 596 * control connection. 597 */ 598 done = 0; 599 while (!done) { 600 do { 601 if ((n = imsg_read(&ibuf_relay[proc])) == -1) 602 fatalx("imsg_read error"); 603 } while (n == -2); /* handle non-blocking I/O */ 604 while (!done) { 605 if ((n = imsg_get(&ibuf_relay[proc], 606 &imsg)) == -1) 607 fatalx("imsg_get error"); 608 if (n == 0) 609 break; 610 switch (imsg.hdr.type) { 611 case IMSG_CTL_SESSION: 612 imsg_compose(&c->ibuf, 613 IMSG_CTL_SESSION, proc, 0, -1, 614 imsg.data, sizeof(struct session)); 615 break; 616 case IMSG_CTL_END: 617 done = 1; 618 break; 619 default: 620 fatalx("wrong message for session"); 621 break; 622 } 623 imsg_free(&imsg); 624 } 625 } 626 } 627 628 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); 629 } 630 631 int 632 disable_rdr(struct ctl_conn *c, struct ctl_id *id) 633 { 634 struct rdr *rdr; 635 636 if (id->id == EMPTY_ID) 637 rdr = rdr_findbyname(env, id->name); 638 else 639 rdr = rdr_find(env, id->id); 640 if (rdr == NULL) 641 return (-1); 642 id->id = rdr->conf.id; 643 644 if (rdr->conf.flags & F_DISABLE) 645 return (0); 646 647 rdr->conf.flags |= F_DISABLE; 648 rdr->conf.flags &= ~(F_ADD); 649 rdr->conf.flags |= F_DEL; 650 rdr->table->conf.flags |= F_DISABLE; 651 log_debug("disable_rdr: disabled rdr %d", rdr->conf.id); 652 pfe_sync(); 653 return (0); 654 } 655 656 int 657 enable_rdr(struct ctl_conn *c, struct ctl_id *id) 658 { 659 struct rdr *rdr; 660 struct ctl_id eid; 661 662 if (id->id == EMPTY_ID) 663 rdr = rdr_findbyname(env, id->name); 664 else 665 rdr = rdr_find(env, id->id); 666 if (rdr == NULL) 667 return (-1); 668 id->id = rdr->conf.id; 669 670 if (!(rdr->conf.flags & F_DISABLE)) 671 return (0); 672 673 rdr->conf.flags &= ~(F_DISABLE); 674 rdr->conf.flags &= ~(F_DEL); 675 rdr->conf.flags |= F_ADD; 676 log_debug("enable_rdr: enabled rdr %d", rdr->conf.id); 677 678 bzero(&eid, sizeof(eid)); 679 680 /* XXX: we're syncing twice */ 681 eid.id = rdr->table->conf.id; 682 if (enable_table(c, &eid) == -1) 683 return (-1); 684 if (rdr->backup->conf.id == EMPTY_ID) 685 return (0); 686 eid.id = rdr->backup->conf.id; 687 if (enable_table(c, &eid) == -1) 688 return (-1); 689 return (0); 690 } 691 692 int 693 disable_table(struct ctl_conn *c, struct ctl_id *id) 694 { 695 struct table *table; 696 struct rdr *rdr; 697 struct host *host; 698 699 if (id->id == EMPTY_ID) 700 table = table_findbyname(env, id->name); 701 else 702 table = table_find(env, id->id); 703 if (table == NULL) 704 return (-1); 705 id->id = table->conf.id; 706 if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL) 707 fatalx("disable_table: desynchronised"); 708 709 if (table->conf.flags & F_DISABLE) 710 return (0); 711 table->conf.flags |= (F_DISABLE|F_CHANGED); 712 table->up = 0; 713 TAILQ_FOREACH(host, &table->hosts, entry) 714 host->up = HOST_UNKNOWN; 715 imsg_compose(ibuf_hce, IMSG_TABLE_DISABLE, 0, 0, -1, 716 &table->conf.id, sizeof(table->conf.id)); 717 log_debug("disable_table: disabled table %d", table->conf.id); 718 pfe_sync(); 719 return (0); 720 } 721 722 int 723 enable_table(struct ctl_conn *c, struct ctl_id *id) 724 { 725 struct rdr *rdr; 726 struct table *table; 727 struct host *host; 728 729 if (id->id == EMPTY_ID) 730 table = table_findbyname(env, id->name); 731 else 732 table = table_find(env, id->id); 733 if (table == NULL) 734 return (-1); 735 id->id = table->conf.id; 736 737 if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL) 738 fatalx("enable_table: desynchronised"); 739 740 if (!(table->conf.flags & F_DISABLE)) 741 return (0); 742 table->conf.flags &= ~(F_DISABLE); 743 table->conf.flags |= F_CHANGED; 744 table->up = 0; 745 TAILQ_FOREACH(host, &table->hosts, entry) 746 host->up = HOST_UNKNOWN; 747 imsg_compose(ibuf_hce, IMSG_TABLE_ENABLE, 0, 0, -1, 748 &table->conf.id, sizeof(table->conf.id)); 749 log_debug("enable_table: enabled table %d", table->conf.id); 750 pfe_sync(); 751 return (0); 752 } 753 754 int 755 disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host) 756 { 757 struct host *h; 758 struct table *table; 759 int n; 760 761 if (host == NULL) { 762 if (id->id == EMPTY_ID) 763 host = host_findbyname(env, id->name); 764 else 765 host = host_find(env, id->id); 766 if (host == NULL || host->conf.parentid) 767 return (-1); 768 } 769 id->id = host->conf.id; 770 771 if (host->flags & F_DISABLE) 772 return (0); 773 774 if (host->up == HOST_UP) { 775 if ((table = table_find(env, host->conf.tableid)) == NULL) 776 fatalx("disable_host: invalid table id"); 777 table->up--; 778 table->conf.flags |= F_CHANGED; 779 } 780 781 host->up = HOST_UNKNOWN; 782 host->flags |= F_DISABLE; 783 host->flags |= F_DEL; 784 host->flags &= ~(F_ADD); 785 host->check_cnt = 0; 786 host->up_cnt = 0; 787 788 imsg_compose(ibuf_hce, IMSG_HOST_DISABLE, 0, 0, -1, 789 &host->conf.id, sizeof(host->conf.id)); 790 /* Forward to relay engine(s) */ 791 for (n = 0; n < env->sc_prefork_relay; n++) 792 imsg_compose(&ibuf_relay[n], 793 IMSG_HOST_DISABLE, 0, 0, -1, 794 &host->conf.id, sizeof(host->conf.id)); 795 log_debug("disable_host: disabled host %d", host->conf.id); 796 797 if (!host->conf.parentid) { 798 /* Disable all children */ 799 SLIST_FOREACH(h, &host->children, child) 800 disable_host(c, id, h); 801 pfe_sync(); 802 } 803 return (0); 804 } 805 806 int 807 enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host) 808 { 809 struct host *h; 810 int n; 811 812 if (host == NULL) { 813 if (id->id == EMPTY_ID) 814 host = host_findbyname(env, id->name); 815 else 816 host = host_find(env, id->id); 817 if (host == NULL || host->conf.parentid) 818 return (-1); 819 } 820 id->id = host->conf.id; 821 822 if (!(host->flags & F_DISABLE)) 823 return (0); 824 825 host->up = HOST_UNKNOWN; 826 host->flags &= ~(F_DISABLE); 827 host->flags &= ~(F_DEL); 828 host->flags &= ~(F_ADD); 829 830 imsg_compose(ibuf_hce, IMSG_HOST_ENABLE, 0, 0, -1, 831 &host->conf.id, sizeof (host->conf.id)); 832 /* Forward to relay engine(s) */ 833 for (n = 0; n < env->sc_prefork_relay; n++) 834 imsg_compose(&ibuf_relay[n], 835 IMSG_HOST_ENABLE, 0, 0, -1, 836 &host->conf.id, sizeof(host->conf.id)); 837 log_debug("enable_host: enabled host %d", host->conf.id); 838 839 if (!host->conf.parentid) { 840 /* Enable all children */ 841 SLIST_FOREACH(h, &host->children, child) 842 enable_host(c, id, h); 843 pfe_sync(); 844 } 845 return (0); 846 } 847 848 void 849 pfe_sync(void) 850 { 851 struct rdr *rdr; 852 struct table *active; 853 struct table *table; 854 struct ctl_id id; 855 struct imsg imsg; 856 struct ctl_demote demote; 857 858 bzero(&id, sizeof(id)); 859 bzero(&imsg, sizeof(imsg)); 860 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { 861 rdr->conf.flags &= ~(F_BACKUP); 862 rdr->conf.flags &= ~(F_DOWN); 863 864 if (rdr->conf.flags & F_DISABLE || 865 (rdr->table->up == 0 && rdr->backup->up == 0)) { 866 rdr->conf.flags |= F_DOWN; 867 active = NULL; 868 } else if (rdr->table->up == 0 && rdr->backup->up > 0) { 869 rdr->conf.flags |= F_BACKUP; 870 active = rdr->backup; 871 active->conf.flags |= 872 rdr->table->conf.flags & F_CHANGED; 873 active->conf.flags |= 874 rdr->backup->conf.flags & F_CHANGED; 875 } else 876 active = rdr->table; 877 878 if (active != NULL && active->conf.flags & F_CHANGED) { 879 id.id = active->conf.id; 880 imsg.hdr.type = IMSG_CTL_TABLE_CHANGED; 881 imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE; 882 imsg.data = &id; 883 sync_table(env, rdr, active); 884 control_imsg_forward(&imsg); 885 } 886 887 if (rdr->conf.flags & F_DOWN) { 888 if (rdr->conf.flags & F_ACTIVE_RULESET) { 889 flush_table(env, rdr); 890 log_debug("pfe_sync: disabling ruleset"); 891 rdr->conf.flags &= ~(F_ACTIVE_RULESET); 892 id.id = rdr->conf.id; 893 imsg.hdr.type = IMSG_CTL_PULL_RULESET; 894 imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE; 895 imsg.data = &id; 896 sync_ruleset(env, rdr, 0); 897 control_imsg_forward(&imsg); 898 } 899 } else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) { 900 log_debug("pfe_sync: enabling ruleset"); 901 rdr->conf.flags |= F_ACTIVE_RULESET; 902 id.id = rdr->conf.id; 903 imsg.hdr.type = IMSG_CTL_PUSH_RULESET; 904 imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE; 905 imsg.data = &id; 906 sync_ruleset(env, rdr, 1); 907 control_imsg_forward(&imsg); 908 } 909 } 910 911 TAILQ_FOREACH(table, env->sc_tables, entry) { 912 /* 913 * clean up change flag. 914 */ 915 table->conf.flags &= ~(F_CHANGED); 916 917 /* 918 * handle demotion. 919 */ 920 if ((table->conf.flags & F_DEMOTE) == 0) 921 continue; 922 demote.level = 0; 923 if (table->up && table->conf.flags & F_DEMOTED) { 924 demote.level = -1; 925 table->conf.flags &= ~F_DEMOTED; 926 } 927 else if (!table->up && !(table->conf.flags & F_DEMOTED)) { 928 demote.level = 1; 929 table->conf.flags |= F_DEMOTED; 930 } 931 if (demote.level == 0) 932 continue; 933 log_debug("pfe_sync: demote %d table '%s' group '%s'", 934 demote.level, table->conf.name, table->conf.demote_group); 935 (void)strlcpy(demote.group, table->conf.demote_group, 936 sizeof(demote.group)); 937 imsg_compose(ibuf_main, IMSG_DEMOTE, 0, 0, -1, 938 &demote, sizeof(demote)); 939 } 940 } 941 942 void 943 pfe_statistics(int fd, short events, void *arg) 944 { 945 struct rdr *rdr; 946 struct ctl_stats *cur; 947 struct timeval tv, tv_now; 948 int resethour, resetday; 949 u_long cnt; 950 951 timerclear(&tv); 952 if (gettimeofday(&tv_now, NULL) == -1) 953 fatal("pfe_statistics: gettimeofday"); 954 955 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { 956 cnt = check_table(env, rdr, rdr->table); 957 if (rdr->conf.backup_id != EMPTY_TABLE) 958 cnt += check_table(env, rdr, rdr->backup); 959 960 resethour = resetday = 0; 961 962 cur = &rdr->stats; 963 cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0; 964 965 cur->cnt = cnt; 966 cur->tick++; 967 cur->avg = (cur->last + cur->avg) / 2; 968 cur->last_hour += cur->last; 969 if ((cur->tick % (3600 / env->sc_statinterval.tv_sec)) == 0) { 970 cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2; 971 resethour++; 972 } 973 cur->last_day += cur->last; 974 if ((cur->tick % (86400 / env->sc_statinterval.tv_sec)) == 0) { 975 cur->avg_day = (cur->last_day + cur->avg_day) / 2; 976 resethour++; 977 } 978 if (resethour) 979 cur->last_hour = 0; 980 if (resetday) 981 cur->last_day = 0; 982 983 rdr->stats.interval = env->sc_statinterval.tv_sec; 984 } 985 986 /* Schedule statistics timer */ 987 evtimer_set(&env->sc_statev, pfe_statistics, NULL); 988 bcopy(&env->sc_statinterval, &tv, sizeof(tv)); 989 evtimer_add(&env->sc_statev, &tv); 990 } 991