1 /* $OpenBSD: ifstated.c,v 1.40 2011/07/04 04:34:14 claudio 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"); 173 174 event_set(&rt_msg_ev, rt_fd, EV_READ|EV_PERSIST, rt_msg_handler, NULL); 175 event_add(&rt_msg_ev, NULL); 176 177 signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL); 178 signal_add(&sighup_ev, NULL); 179 180 log_info("started"); 181 } 182 183 void 184 sighup_handler(int fd, short event, void *arg) 185 { 186 log_info("reloading config"); 187 if (load_config() != 0) 188 log_warnx("unable to reload config"); 189 } 190 191 int 192 load_config(void) 193 { 194 if ((newconf = parse_config(configfile, opts)) == NULL) 195 return (-1); 196 if (conf != NULL) 197 clear_config(conf); 198 conf = newconf; 199 conf->always.entered = time(NULL); 200 fetch_state(); 201 external_evtimer_setup(&conf->always, IFSD_EVTIMER_ADD); 202 adjust_external_expressions(&conf->always); 203 eval_state(&conf->always); 204 if (conf->curstate != NULL) { 205 log_info("initial state: %s", conf->curstate->name); 206 conf->curstate->entered = time(NULL); 207 conf->nextstate = conf->curstate; 208 conf->curstate = NULL; 209 while (state_change()) 210 do_action(conf->curstate->always); 211 } 212 return (0); 213 } 214 215 void 216 rt_msg_handler(int fd, short event, void *arg) 217 { 218 char msg[2048]; 219 struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; 220 struct if_msghdr ifm; 221 int len; 222 223 len = read(fd, msg, sizeof(msg)); 224 225 /* XXX ignore errors? */ 226 if (len < sizeof(struct rt_msghdr)) 227 return; 228 229 if (rtm->rtm_version != RTM_VERSION) 230 return; 231 232 if (rtm->rtm_type != RTM_IFINFO) 233 return; 234 235 memcpy(&ifm, rtm, sizeof(ifm)); 236 scan_ifstate(ifm.ifm_index, ifm.ifm_data.ifi_link_state, 1); 237 } 238 239 void 240 sigchld_handler(int fd, short event, void *arg) 241 { 242 check_external_status(&conf->always); 243 if (conf->curstate != NULL) 244 check_external_status(conf->curstate); 245 } 246 247 void 248 external_handler(int fd, short event, void *arg) 249 { 250 struct ifsd_external *external = (struct ifsd_external *)arg; 251 struct timeval tv; 252 253 /* re-schedule */ 254 timerclear(&tv); 255 tv.tv_sec = external->frequency; 256 evtimer_set(&external->ev, external_handler, external); 257 evtimer_add(&external->ev, &tv); 258 259 /* execute */ 260 external_exec(external, 1); 261 } 262 263 void 264 external_exec(struct ifsd_external *external, int async) 265 { 266 char *argp[] = {"sh", "-c", NULL, NULL}; 267 pid_t pid; 268 int s; 269 270 if (external->pid > 0) { 271 log_debug("previous command %s [%d] still running, killing it", 272 external->command, external->pid); 273 kill(external->pid, SIGKILL); 274 waitpid(external->pid, &s, 0); 275 external->pid = 0; 276 } 277 278 argp[2] = external->command; 279 log_debug("running %s", external->command); 280 pid = fork(); 281 if (pid < 0) { 282 log_warn("fork error"); 283 } else if (pid == 0) { 284 execv("/bin/sh", argp); 285 _exit(1); 286 /* NOTREACHED */ 287 } else { 288 external->pid = pid; 289 } 290 if (!async) { 291 waitpid(external->pid, &s, 0); 292 external->pid = 0; 293 if (WIFEXITED(s)) 294 external->prevstatus = WEXITSTATUS(s); 295 } 296 } 297 298 void 299 adjust_external_expressions(struct ifsd_state *state) 300 { 301 struct ifsd_external *external; 302 struct ifsd_expression_list expressions; 303 304 TAILQ_INIT(&expressions); 305 TAILQ_FOREACH(external, &state->external_tests, entries) { 306 struct ifsd_expression *expression; 307 308 if (external->prevstatus == -1) 309 continue; 310 311 TAILQ_FOREACH(expression, &external->expressions, entries) { 312 TAILQ_INSERT_TAIL(&expressions, 313 expression, eval); 314 expression->truth = !external->prevstatus; 315 } 316 adjust_expressions(&expressions, conf->maxdepth); 317 } 318 } 319 320 void 321 check_external_status(struct ifsd_state *state) 322 { 323 struct ifsd_external *external, *end = NULL; 324 int status, s, changed = 0; 325 326 /* Do this manually; change ordering so the oldest is first */ 327 external = TAILQ_FIRST(&state->external_tests); 328 while (external != NULL && external != end) { 329 struct ifsd_external *newexternal; 330 331 newexternal = TAILQ_NEXT(external, entries); 332 333 if (external->pid <= 0) 334 goto loop; 335 336 if (wait4(external->pid, &s, WNOHANG, NULL) == 0) 337 goto loop; 338 339 external->pid = 0; 340 if (end == NULL) 341 end = external; 342 if (WIFEXITED(s)) 343 status = WEXITSTATUS(s); 344 else { 345 log_warnx("%s exited abnormally", external->command); 346 goto loop; 347 } 348 349 if (external->prevstatus != status && 350 (external->prevstatus != -1 || !opt_inhibit)) { 351 changed = 1; 352 external->prevstatus = status; 353 } 354 external->lastexec = time(NULL); 355 TAILQ_REMOVE(&state->external_tests, external, entries); 356 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 357 loop: 358 external = newexternal; 359 } 360 361 if (changed) { 362 adjust_external_expressions(state); 363 eval_state(state); 364 } 365 } 366 367 void 368 external_evtimer_setup(struct ifsd_state *state, int action) 369 { 370 struct ifsd_external *external; 371 int s; 372 373 if (state != NULL) { 374 switch (action) { 375 case IFSD_EVTIMER_ADD: 376 TAILQ_FOREACH(external, 377 &state->external_tests, entries) { 378 struct timeval tv; 379 380 /* run it once right away */ 381 external_exec(external, 0); 382 383 /* schedule it for later */ 384 timerclear(&tv); 385 tv.tv_sec = external->frequency; 386 evtimer_set(&external->ev, external_handler, 387 external); 388 evtimer_add(&external->ev, &tv); 389 } 390 break; 391 case IFSD_EVTIMER_DEL: 392 TAILQ_FOREACH(external, 393 &state->external_tests, entries) { 394 if (external->pid > 0) { 395 kill(external->pid, SIGKILL); 396 waitpid(external->pid, &s, 0); 397 external->pid = 0; 398 } 399 evtimer_del(&external->ev); 400 } 401 break; 402 } 403 } 404 } 405 406 #define LINK_STATE_IS_DOWN(_s) (!LINK_STATE_IS_UP((_s))) 407 408 int 409 scan_ifstate_single(int ifindex, int s, struct ifsd_state *state) 410 { 411 struct ifsd_ifstate *ifstate; 412 struct ifsd_expression_list expressions; 413 int changed = 0; 414 415 TAILQ_INIT(&expressions); 416 417 TAILQ_FOREACH(ifstate, &state->interface_states, entries) { 418 if (ifstate->ifindex == ifindex) { 419 if (ifstate->prevstate != s && 420 (ifstate->prevstate != -1 || !opt_inhibit)) { 421 struct ifsd_expression *expression; 422 int truth; 423 424 truth = 425 (ifstate->ifstate == IFSD_LINKUNKNOWN && 426 s == LINK_STATE_UNKNOWN) || 427 (ifstate->ifstate == IFSD_LINKDOWN && 428 LINK_STATE_IS_DOWN(s)) || 429 (ifstate->ifstate == IFSD_LINKUP && 430 LINK_STATE_IS_UP(s)); 431 432 TAILQ_FOREACH(expression, 433 &ifstate->expressions, entries) { 434 expression->truth = truth; 435 TAILQ_INSERT_TAIL(&expressions, 436 expression, eval); 437 changed = 1; 438 } 439 ifstate->prevstate = s; 440 } 441 } 442 } 443 444 if (changed) 445 adjust_expressions(&expressions, conf->maxdepth); 446 return (changed); 447 } 448 449 void 450 scan_ifstate(int ifindex, int s, int do_eval) 451 { 452 struct ifsd_state *state; 453 int cur_eval = 0; 454 455 if (scan_ifstate_single(ifindex, s, &conf->always) && do_eval) 456 eval_state(&conf->always); 457 TAILQ_FOREACH(state, &conf->states, entries) { 458 if (scan_ifstate_single(ifindex, s, state) && 459 (do_eval && state == conf->curstate)) 460 cur_eval = 1; 461 } 462 /* execute actions _after_ all expressions have been adjusted */ 463 if (cur_eval) 464 eval_state(conf->curstate); 465 } 466 467 /* 468 * Do a bottom-up ajustment of the expression tree's truth value, 469 * level-by-level to ensure that each expression's subexpressions have been 470 * evaluated. 471 */ 472 void 473 adjust_expressions(struct ifsd_expression_list *expressions, int depth) 474 { 475 struct ifsd_expression_list nexpressions; 476 struct ifsd_expression *expression; 477 478 TAILQ_INIT(&nexpressions); 479 while ((expression = TAILQ_FIRST(expressions)) != NULL) { 480 TAILQ_REMOVE(expressions, expression, eval); 481 if (expression->depth == depth) { 482 struct ifsd_expression *te; 483 484 switch (expression->type) { 485 case IFSD_OPER_AND: 486 expression->truth = expression->left->truth && 487 expression->right->truth; 488 break; 489 case IFSD_OPER_OR: 490 expression->truth = expression->left->truth || 491 expression->right->truth; 492 break; 493 case IFSD_OPER_NOT: 494 expression->truth = !expression->right->truth; 495 break; 496 default: 497 break; 498 } 499 if (expression->parent != NULL) { 500 if (TAILQ_EMPTY(&nexpressions)) 501 te = NULL; 502 TAILQ_FOREACH(te, &nexpressions, eval) 503 if (expression->parent == te) 504 break; 505 if (te == NULL) 506 TAILQ_INSERT_TAIL(&nexpressions, 507 expression->parent, eval); 508 } 509 } else 510 TAILQ_INSERT_TAIL(&nexpressions, expression, eval); 511 } 512 if (depth > 0) 513 adjust_expressions(&nexpressions, depth - 1); 514 } 515 516 void 517 eval_state(struct ifsd_state *state) 518 { 519 struct ifsd_external *external = TAILQ_FIRST(&state->external_tests); 520 if (external == NULL || external->lastexec >= state->entered || 521 external->lastexec == 0) { 522 do_action(state->always); 523 while (state_change()) 524 do_action(conf->curstate->always); 525 } 526 } 527 528 /* 529 *If a previous action included a state change, process it. 530 */ 531 int 532 state_change(void) 533 { 534 if (conf->nextstate != NULL && conf->curstate != conf->nextstate) { 535 log_info("changing state to %s", conf->nextstate->name); 536 if (conf->curstate != NULL) { 537 evtimer_del(&conf->curstate->ev); 538 external_evtimer_setup(conf->curstate, 539 IFSD_EVTIMER_DEL); 540 } 541 conf->curstate = conf->nextstate; 542 conf->nextstate = NULL; 543 conf->curstate->entered = time(NULL); 544 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD); 545 adjust_external_expressions(conf->curstate); 546 do_action(conf->curstate->init); 547 return (1); 548 } 549 return (0); 550 } 551 552 /* 553 * Run recursively through the tree of actions. 554 */ 555 void 556 do_action(struct ifsd_action *action) 557 { 558 struct ifsd_action *subaction; 559 560 switch (action->type) { 561 case IFSD_ACTION_COMMAND: 562 log_debug("running %s", action->act.command); 563 system(action->act.command); 564 break; 565 case IFSD_ACTION_CHANGESTATE: 566 conf->nextstate = action->act.nextstate; 567 break; 568 case IFSD_ACTION_CONDITION: 569 if ((action->act.c.expression != NULL && 570 action->act.c.expression->truth) || 571 action->act.c.expression == NULL) { 572 TAILQ_FOREACH(subaction, &action->act.c.actions, 573 entries) 574 do_action(subaction); 575 } 576 break; 577 default: 578 log_debug("do_action: unknown action %d", action->type); 579 break; 580 } 581 } 582 583 /* 584 * Fetch the current link states. 585 */ 586 void 587 fetch_state(void) 588 { 589 struct ifaddrs *ifap, *ifa; 590 char *oname = NULL; 591 int sock = socket(AF_INET, SOCK_DGRAM, 0); 592 593 if (getifaddrs(&ifap) != 0) 594 err(1, "getifaddrs"); 595 596 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 597 struct ifreq ifr; 598 struct if_data ifrdat; 599 600 if (oname && !strcmp(oname, ifa->ifa_name)) 601 continue; 602 oname = ifa->ifa_name; 603 604 strlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); 605 ifr.ifr_data = (caddr_t)&ifrdat; 606 607 if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1) 608 continue; 609 610 scan_ifstate(if_nametoindex(ifa->ifa_name), 611 ifrdat.ifi_link_state, 0); 612 } 613 freeifaddrs(ifap); 614 close(sock); 615 } 616 617 618 619 /* 620 * Clear the config. 621 */ 622 void 623 clear_config(struct ifsd_config *oconf) 624 { 625 struct ifsd_state *state; 626 627 external_evtimer_setup(&conf->always, IFSD_EVTIMER_DEL); 628 if (conf != NULL && conf->curstate != NULL) 629 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL); 630 while ((state = TAILQ_FIRST(&oconf->states)) != NULL) { 631 TAILQ_REMOVE(&oconf->states, state, entries); 632 remove_action(state->init, state); 633 remove_action(state->always, state); 634 free(state->name); 635 free(state); 636 } 637 remove_action(oconf->always.init, &oconf->always); 638 remove_action(oconf->always.always, &oconf->always); 639 free(oconf); 640 } 641 642 void 643 remove_action(struct ifsd_action *action, struct ifsd_state *state) 644 { 645 struct ifsd_action *subaction; 646 647 if (action == NULL || state == NULL) 648 return; 649 650 switch (action->type) { 651 case IFSD_ACTION_LOG: 652 free(action->act.logmessage); 653 break; 654 case IFSD_ACTION_COMMAND: 655 free(action->act.command); 656 break; 657 case IFSD_ACTION_CHANGESTATE: 658 break; 659 case IFSD_ACTION_CONDITION: 660 if (action->act.c.expression != NULL) 661 remove_expression(action->act.c.expression, state); 662 while ((subaction = 663 TAILQ_FIRST(&action->act.c.actions)) != NULL) { 664 TAILQ_REMOVE(&action->act.c.actions, 665 subaction, entries); 666 remove_action(subaction, state); 667 } 668 } 669 free(action); 670 } 671 672 void 673 remove_expression(struct ifsd_expression *expression, 674 struct ifsd_state *state) 675 { 676 switch (expression->type) { 677 case IFSD_OPER_IFSTATE: 678 TAILQ_REMOVE(&expression->u.ifstate->expressions, expression, 679 entries); 680 if (--expression->u.ifstate->refcount == 0) { 681 TAILQ_REMOVE(&state->interface_states, 682 expression->u.ifstate, entries); 683 free(expression->u.ifstate); 684 } 685 break; 686 case IFSD_OPER_EXTERNAL: 687 TAILQ_REMOVE(&expression->u.external->expressions, expression, 688 entries); 689 if (--expression->u.external->refcount == 0) { 690 TAILQ_REMOVE(&state->external_tests, 691 expression->u.external, entries); 692 free(expression->u.external->command); 693 event_del(&expression->u.external->ev); 694 free(expression->u.external); 695 } 696 break; 697 default: 698 if (expression->left != NULL) 699 remove_expression(expression->left, state); 700 if (expression->right != NULL) 701 remove_expression(expression->right, state); 702 break; 703 } 704 free(expression); 705 } 706