1 /* $OpenBSD: ifstated.c,v 1.64 2019/06/28 13:32:47 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org> 5 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * ifstated listens to link_state transitions on interfaces 22 * and executes predefined commands. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/time.h> 27 #include <sys/socket.h> 28 #include <sys/wait.h> 29 30 #include <net/if.h> 31 #include <net/route.h> 32 #include <netinet/in.h> 33 34 #include <paths.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <signal.h> 39 #include <stdint.h> 40 #include <syslog.h> 41 #include <errno.h> 42 #include <event.h> 43 #include <unistd.h> 44 #include <ifaddrs.h> 45 46 #include "ifstated.h" 47 #include "log.h" 48 49 struct ifsd_config *conf, *newconf; 50 51 int opts; 52 int opt_inhibit; 53 char *configfile = "/etc/ifstated.conf"; 54 struct event rt_msg_ev, sighup_ev, startup_ev, sigchld_ev; 55 56 void startup_handler(int, short, void *); 57 void sighup_handler(int, short, void *); 58 int load_config(void); 59 void sigchld_handler(int, short, void *); 60 void rt_msg_handler(int, short, void *); 61 void external_handler(int, short, void *); 62 void external_exec(struct ifsd_external *, int); 63 void check_external_status(struct ifsd_state *); 64 void check_ifdeparture(void); 65 void external_evtimer_setup(struct ifsd_state *, int); 66 void scan_ifstate(const char *, int, int); 67 int scan_ifstate_single(const char *, int, struct ifsd_state *); 68 void fetch_ifstate(int); 69 __dead void usage(void); 70 void adjust_expressions(struct ifsd_expression_list *, int); 71 void adjust_external_expressions(struct ifsd_state *); 72 void eval_state(struct ifsd_state *); 73 int state_change(void); 74 void do_action(struct ifsd_action *); 75 void remove_action(struct ifsd_action *, struct ifsd_state *); 76 void remove_expression(struct ifsd_expression *, 77 struct ifsd_state *); 78 79 __dead void 80 usage(void) 81 { 82 extern char *__progname; 83 84 fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n", 85 __progname); 86 exit(1); 87 } 88 89 int 90 main(int argc, char *argv[]) 91 { 92 struct timeval tv; 93 int ch, rt_fd; 94 int debug = 0; 95 unsigned int rtfilter; 96 97 log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ 98 log_setverbose(1); 99 100 while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) { 101 switch (ch) { 102 case 'd': 103 debug = 1; 104 break; 105 case 'D': 106 if (cmdline_symset(optarg) < 0) 107 fatalx("could not parse macro definition %s", 108 optarg); 109 break; 110 case 'f': 111 configfile = optarg; 112 break; 113 case 'h': 114 usage(); 115 break; 116 case 'n': 117 opts |= IFSD_OPT_NOACTION; 118 break; 119 case 'i': 120 opt_inhibit = 1; 121 break; 122 case 'v': 123 if (opts & IFSD_OPT_VERBOSE) 124 opts |= IFSD_OPT_VERBOSE2; 125 opts |= IFSD_OPT_VERBOSE; 126 break; 127 default: 128 usage(); 129 } 130 } 131 132 argc -= optind; 133 argv += optind; 134 if (argc > 0) 135 usage(); 136 137 if (opts & IFSD_OPT_NOACTION) { 138 if ((newconf = parse_config(configfile, opts)) == NULL) 139 exit(1); 140 fprintf(stderr, "configuration OK\n"); 141 exit(0); 142 } 143 144 if (!debug) 145 daemon(1, 0); 146 147 event_init(); 148 log_init(debug, LOG_DAEMON); 149 log_setverbose(opts & IFSD_OPT_VERBOSE); 150 151 if ((rt_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) 152 fatal("no routing socket"); 153 154 rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE); 155 if (setsockopt(rt_fd, AF_ROUTE, ROUTE_MSGFILTER, 156 &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ 157 log_warn("%s: setsockopt msgfilter", __func__); 158 159 rtfilter = RTABLE_ANY; 160 if (setsockopt(rt_fd, AF_ROUTE, ROUTE_TABLEFILTER, 161 &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ 162 log_warn("%s: setsockopt tablefilter", __func__); 163 164 if (unveil(configfile, "r") == -1) 165 fatal("unveil"); 166 if (unveil(_PATH_BSHELL, "x") == -1) 167 fatal("unveil"); 168 if (pledge("stdio rpath route proc exec", NULL) == -1) 169 fatal("pledge"); 170 171 signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL); 172 signal_add(&sigchld_ev, NULL); 173 174 /* Loading the config needs to happen in the event loop */ 175 timerclear(&tv); 176 evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd); 177 evtimer_add(&startup_ev, &tv); 178 179 event_loop(0); 180 exit(0); 181 } 182 183 void 184 startup_handler(int fd, short event, void *arg) 185 { 186 int rfd = (int)(long)arg; 187 188 if (load_config() != 0) { 189 log_warnx("unable to load config"); 190 exit(1); 191 } 192 193 event_set(&rt_msg_ev, rfd, EV_READ|EV_PERSIST, rt_msg_handler, NULL); 194 event_add(&rt_msg_ev, NULL); 195 196 signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL); 197 signal_add(&sighup_ev, NULL); 198 199 log_info("started"); 200 } 201 202 void 203 sighup_handler(int fd, short event, void *arg) 204 { 205 log_info("reloading config"); 206 if (load_config() != 0) 207 log_warnx("unable to reload config"); 208 } 209 210 int 211 load_config(void) 212 { 213 if ((newconf = parse_config(configfile, opts)) == NULL) 214 return (-1); 215 if (conf != NULL) 216 clear_config(conf); 217 conf = newconf; 218 conf->initstate.entered = time(NULL); 219 fetch_ifstate(0); 220 external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD); 221 adjust_external_expressions(&conf->initstate); 222 eval_state(&conf->initstate); 223 if (conf->curstate != NULL) { 224 log_info("initial state: %s", conf->curstate->name); 225 conf->curstate->entered = time(NULL); 226 conf->nextstate = conf->curstate; 227 conf->curstate = NULL; 228 while (state_change()) { 229 do_action(conf->curstate->init); 230 do_action(conf->curstate->body); 231 } 232 } 233 return (0); 234 } 235 236 void 237 rt_msg_handler(int fd, short event, void *arg) 238 { 239 char msg[2048]; 240 struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; 241 struct if_msghdr ifm; 242 struct if_announcemsghdr ifan; 243 char ifnamebuf[IFNAMSIZ]; 244 char *ifname; 245 ssize_t len; 246 247 if ((len = read(fd, msg, sizeof(msg))) == -1) { 248 if (errno == EAGAIN || errno == EINTR) 249 return; 250 fatal("%s: routing socket read error", __func__); 251 } 252 253 if (len == 0) 254 fatal("%s: routing socket closed", __func__); 255 256 if (rtm->rtm_version != RTM_VERSION) 257 return; 258 259 switch (rtm->rtm_type) { 260 case RTM_IFINFO: 261 memcpy(&ifm, rtm, sizeof(ifm)); 262 ifname = if_indextoname(ifm.ifm_index, ifnamebuf); 263 /* ifname is NULL on interface departure */ 264 if (ifname != NULL) 265 scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1); 266 break; 267 case RTM_IFANNOUNCE: 268 memcpy(&ifan, rtm, sizeof(ifan)); 269 switch (ifan.ifan_what) { 270 case IFAN_DEPARTURE: 271 log_warnx("interface %s departed", ifan.ifan_name); 272 check_ifdeparture(); 273 break; 274 case IFAN_ARRIVAL: 275 log_warnx("interface %s arrived", ifan.ifan_name); 276 fetch_ifstate(1); 277 break; 278 } 279 break; 280 case RTM_DESYNC: 281 /* we lost some routing messages so rescan interfaces */ 282 check_ifdeparture(); 283 fetch_ifstate(1); 284 break; 285 } 286 return; 287 } 288 289 void 290 sigchld_handler(int fd, short event, void *arg) 291 { 292 check_external_status(&conf->initstate); 293 if (conf->curstate != NULL) 294 check_external_status(conf->curstate); 295 } 296 297 void 298 external_handler(int fd, short event, void *arg) 299 { 300 struct ifsd_external *external = (struct ifsd_external *)arg; 301 struct timeval tv; 302 303 /* re-schedule */ 304 timerclear(&tv); 305 tv.tv_sec = external->frequency; 306 evtimer_set(&external->ev, external_handler, external); 307 evtimer_add(&external->ev, &tv); 308 309 /* execute */ 310 external_exec(external, 1); 311 } 312 313 void 314 external_exec(struct ifsd_external *external, int async) 315 { 316 char *argp[] = {"sh", "-c", NULL, NULL}; 317 pid_t pid; 318 int s; 319 320 if (external->pid > 0) { 321 log_debug("previous command %s [%d] still running, killing it", 322 external->command, external->pid); 323 kill(external->pid, SIGKILL); 324 waitpid(external->pid, &s, 0); 325 external->pid = 0; 326 } 327 328 argp[2] = external->command; 329 log_debug("running %s", external->command); 330 pid = fork(); 331 if (pid == -1) { 332 log_warn("fork error"); 333 } else if (pid == 0) { 334 execv(_PATH_BSHELL, argp); 335 _exit(1); 336 /* NOTREACHED */ 337 } else { 338 external->pid = pid; 339 } 340 if (!async) { 341 waitpid(external->pid, &s, 0); 342 external->pid = 0; 343 if (WIFEXITED(s)) 344 external->prevstatus = WEXITSTATUS(s); 345 } 346 } 347 348 void 349 adjust_external_expressions(struct ifsd_state *state) 350 { 351 struct ifsd_external *external; 352 struct ifsd_expression_list expressions; 353 354 TAILQ_INIT(&expressions); 355 TAILQ_FOREACH(external, &state->external_tests, entries) { 356 struct ifsd_expression *expression; 357 358 if (external->prevstatus == -1) 359 continue; 360 361 TAILQ_FOREACH(expression, &external->expressions, entries) { 362 TAILQ_INSERT_TAIL(&expressions, 363 expression, eval); 364 expression->truth = !external->prevstatus; 365 } 366 adjust_expressions(&expressions, conf->maxdepth); 367 } 368 } 369 370 void 371 check_external_status(struct ifsd_state *state) 372 { 373 struct ifsd_external *external, *end = NULL; 374 int status, s, changed = 0; 375 376 /* Do this manually; change ordering so the oldest is first */ 377 external = TAILQ_FIRST(&state->external_tests); 378 while (external != NULL && external != end) { 379 struct ifsd_external *newexternal; 380 381 newexternal = TAILQ_NEXT(external, entries); 382 383 if (external->pid <= 0) 384 goto loop; 385 386 if (wait4(external->pid, &s, WNOHANG, NULL) == 0) 387 goto loop; 388 389 external->pid = 0; 390 if (end == NULL) 391 end = external; 392 if (WIFEXITED(s)) 393 status = WEXITSTATUS(s); 394 else { 395 log_warnx("%s exited abnormally", external->command); 396 goto loop; 397 } 398 399 if (external->prevstatus != status && 400 (external->prevstatus != -1 || !opt_inhibit)) { 401 changed = 1; 402 external->prevstatus = status; 403 } 404 external->lastexec = time(NULL); 405 TAILQ_REMOVE(&state->external_tests, external, entries); 406 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 407 loop: 408 external = newexternal; 409 } 410 411 if (changed) { 412 adjust_external_expressions(state); 413 eval_state(state); 414 } 415 } 416 417 void 418 external_evtimer_setup(struct ifsd_state *state, int action) 419 { 420 struct ifsd_external *external; 421 int s; 422 423 if (state != NULL) { 424 switch (action) { 425 case IFSD_EVTIMER_ADD: 426 TAILQ_FOREACH(external, 427 &state->external_tests, entries) { 428 struct timeval tv; 429 430 /* run it once right away */ 431 external_exec(external, 0); 432 433 /* schedule it for later */ 434 timerclear(&tv); 435 tv.tv_sec = external->frequency; 436 evtimer_set(&external->ev, external_handler, 437 external); 438 evtimer_add(&external->ev, &tv); 439 } 440 break; 441 case IFSD_EVTIMER_DEL: 442 TAILQ_FOREACH(external, 443 &state->external_tests, entries) { 444 if (external->pid > 0) { 445 kill(external->pid, SIGKILL); 446 waitpid(external->pid, &s, 0); 447 external->pid = 0; 448 } 449 evtimer_del(&external->ev); 450 } 451 break; 452 } 453 } 454 } 455 456 #define LINK_STATE_IS_DOWN(_s) (!LINK_STATE_IS_UP((_s))) 457 458 int 459 scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state) 460 { 461 struct ifsd_ifstate *ifstate; 462 struct ifsd_expression_list expressions; 463 int changed = 0; 464 465 TAILQ_INIT(&expressions); 466 467 TAILQ_FOREACH(ifstate, &state->interface_states, entries) { 468 if (strcmp(ifstate->ifname, ifname) == 0) { 469 if (ifstate->prevstate != s && 470 (ifstate->prevstate != -1 || !opt_inhibit)) { 471 struct ifsd_expression *expression; 472 int truth; 473 474 truth = 475 (ifstate->ifstate == IFSD_LINKUNKNOWN && 476 s == LINK_STATE_UNKNOWN) || 477 (ifstate->ifstate == IFSD_LINKDOWN && 478 LINK_STATE_IS_DOWN(s)) || 479 (ifstate->ifstate == IFSD_LINKUP && 480 LINK_STATE_IS_UP(s)); 481 482 TAILQ_FOREACH(expression, 483 &ifstate->expressions, entries) { 484 expression->truth = truth; 485 TAILQ_INSERT_TAIL(&expressions, 486 expression, eval); 487 changed = 1; 488 } 489 ifstate->prevstate = s; 490 } 491 } 492 } 493 494 if (changed) 495 adjust_expressions(&expressions, conf->maxdepth); 496 return (changed); 497 } 498 499 void 500 scan_ifstate(const char *ifname, int s, int do_eval) 501 { 502 struct ifsd_state *state; 503 int cur_eval = 0; 504 505 if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval) 506 eval_state(&conf->initstate); 507 TAILQ_FOREACH(state, &conf->states, entries) { 508 if (scan_ifstate_single(ifname, s, state) && 509 (do_eval && state == conf->curstate)) 510 cur_eval = 1; 511 } 512 /* execute actions _after_ all expressions have been adjusted */ 513 if (cur_eval) 514 eval_state(conf->curstate); 515 } 516 517 /* 518 * Do a bottom-up ajustment of the expression tree's truth value, 519 * level-by-level to ensure that each expression's subexpressions have been 520 * evaluated. 521 */ 522 void 523 adjust_expressions(struct ifsd_expression_list *expressions, int depth) 524 { 525 struct ifsd_expression_list nexpressions; 526 struct ifsd_expression *expression; 527 528 TAILQ_INIT(&nexpressions); 529 while ((expression = TAILQ_FIRST(expressions)) != NULL) { 530 TAILQ_REMOVE(expressions, expression, eval); 531 if (expression->depth == depth) { 532 struct ifsd_expression *te; 533 534 switch (expression->type) { 535 case IFSD_OPER_AND: 536 expression->truth = expression->left->truth && 537 expression->right->truth; 538 break; 539 case IFSD_OPER_OR: 540 expression->truth = expression->left->truth || 541 expression->right->truth; 542 break; 543 case IFSD_OPER_NOT: 544 expression->truth = !expression->right->truth; 545 break; 546 default: 547 break; 548 } 549 if (expression->parent != NULL) { 550 if (TAILQ_EMPTY(&nexpressions)) 551 te = NULL; 552 TAILQ_FOREACH(te, &nexpressions, eval) 553 if (expression->parent == te) 554 break; 555 if (te == NULL) 556 TAILQ_INSERT_TAIL(&nexpressions, 557 expression->parent, eval); 558 } 559 } else 560 TAILQ_INSERT_TAIL(&nexpressions, expression, eval); 561 } 562 if (depth > 0) 563 adjust_expressions(&nexpressions, depth - 1); 564 } 565 566 void 567 eval_state(struct ifsd_state *state) 568 { 569 struct ifsd_external *external; 570 571 external = TAILQ_FIRST(&state->external_tests); 572 if (external == NULL || external->lastexec >= state->entered || 573 external->lastexec == 0) { 574 do_action(state->body); 575 while (state_change()) { 576 do_action(conf->curstate->init); 577 do_action(conf->curstate->body); 578 } 579 } 580 } 581 582 int 583 state_change(void) 584 { 585 if (conf->nextstate != NULL && conf->curstate != conf->nextstate) { 586 log_info("changing state to %s", conf->nextstate->name); 587 if (conf->curstate != NULL) { 588 evtimer_del(&conf->curstate->ev); 589 external_evtimer_setup(conf->curstate, 590 IFSD_EVTIMER_DEL); 591 } 592 conf->curstate = conf->nextstate; 593 conf->nextstate = NULL; 594 conf->curstate->entered = time(NULL); 595 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD); 596 adjust_external_expressions(conf->curstate); 597 return (1); 598 } 599 return (0); 600 } 601 602 /* 603 * Run recursively through the tree of actions. 604 */ 605 void 606 do_action(struct ifsd_action *action) 607 { 608 struct ifsd_action *subaction; 609 610 switch (action->type) { 611 case IFSD_ACTION_COMMAND: 612 log_debug("running %s", action->act.command); 613 system(action->act.command); 614 break; 615 case IFSD_ACTION_CHANGESTATE: 616 conf->nextstate = action->act.nextstate; 617 break; 618 case IFSD_ACTION_CONDITION: 619 if ((action->act.c.expression != NULL && 620 action->act.c.expression->truth) || 621 action->act.c.expression == NULL) { 622 TAILQ_FOREACH(subaction, &action->act.c.actions, 623 entries) 624 do_action(subaction); 625 } 626 break; 627 default: 628 log_debug("%s: unknown action %d", __func__, action->type); 629 break; 630 } 631 } 632 633 /* 634 * Fetch the current link states. 635 */ 636 void 637 fetch_ifstate(int do_eval) 638 { 639 struct ifaddrs *ifap, *ifa; 640 641 if (getifaddrs(&ifap) != 0) 642 fatal("getifaddrs"); 643 644 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 645 if (ifa->ifa_addr->sa_family == AF_LINK) { 646 struct if_data *ifdata = ifa->ifa_data; 647 scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state, 648 do_eval); 649 } 650 } 651 652 freeifaddrs(ifap); 653 } 654 655 void 656 check_ifdeparture(void) 657 { 658 struct ifsd_state *state; 659 struct ifsd_ifstate *ifstate; 660 661 TAILQ_FOREACH(state, &conf->states, entries) { 662 TAILQ_FOREACH(ifstate, &state->interface_states, entries) { 663 if (if_nametoindex(ifstate->ifname) == 0) 664 scan_ifstate(ifstate->ifname, 665 LINK_STATE_DOWN, 1); 666 } 667 } 668 } 669 670 void 671 clear_config(struct ifsd_config *oconf) 672 { 673 struct ifsd_state *state; 674 675 external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL); 676 if (conf != NULL && conf->curstate != NULL) 677 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL); 678 while ((state = TAILQ_FIRST(&oconf->states)) != NULL) { 679 TAILQ_REMOVE(&oconf->states, state, entries); 680 remove_action(state->init, state); 681 remove_action(state->body, state); 682 free(state->name); 683 free(state); 684 } 685 remove_action(oconf->initstate.init, &oconf->initstate); 686 remove_action(oconf->initstate.body, &oconf->initstate); 687 free(oconf); 688 } 689 690 void 691 remove_action(struct ifsd_action *action, struct ifsd_state *state) 692 { 693 struct ifsd_action *subaction; 694 695 if (action == NULL || state == NULL) 696 return; 697 698 switch (action->type) { 699 case IFSD_ACTION_COMMAND: 700 free(action->act.command); 701 break; 702 case IFSD_ACTION_CHANGESTATE: 703 break; 704 case IFSD_ACTION_CONDITION: 705 if (action->act.c.expression != NULL) 706 remove_expression(action->act.c.expression, state); 707 while ((subaction = 708 TAILQ_FIRST(&action->act.c.actions)) != NULL) { 709 TAILQ_REMOVE(&action->act.c.actions, 710 subaction, entries); 711 remove_action(subaction, state); 712 } 713 } 714 free(action); 715 } 716 717 void 718 remove_expression(struct ifsd_expression *expression, 719 struct ifsd_state *state) 720 { 721 switch (expression->type) { 722 case IFSD_OPER_IFSTATE: 723 TAILQ_REMOVE(&expression->u.ifstate->expressions, expression, 724 entries); 725 if (--expression->u.ifstate->refcount == 0) { 726 TAILQ_REMOVE(&state->interface_states, 727 expression->u.ifstate, entries); 728 free(expression->u.ifstate); 729 } 730 break; 731 case IFSD_OPER_EXTERNAL: 732 TAILQ_REMOVE(&expression->u.external->expressions, expression, 733 entries); 734 if (--expression->u.external->refcount == 0) { 735 TAILQ_REMOVE(&state->external_tests, 736 expression->u.external, entries); 737 free(expression->u.external->command); 738 event_del(&expression->u.external->ev); 739 free(expression->u.external); 740 } 741 break; 742 default: 743 if (expression->left != NULL) 744 remove_expression(expression->left, state); 745 if (expression->right != NULL) 746 remove_expression(expression->right, state); 747 break; 748 } 749 free(expression); 750 } 751