xref: /openbsd-src/usr.sbin/ifstated/ifstated.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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