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