relayd: exec program on gateway change

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Sebastian Benoit
Date: Monday, December 27, 2010 - 3:24 pm

Hi,

i am using relayd in "router" mode for a cable-modem link that sometimes
does not work.

I need to run a programm to load/unload pf-rules and to restart a
proxy with a different config whenever this happens.

Here is a patch that adds an "exec" option to the router section like this:

router "uplinks" {
        route 0.0.0.0/0
        forward to <gateways> check icmp
        exec "/usr/local/sbin/relayd_test" timeout 10
}

The code that does the exec is taken from check_script.c.

One thing i'm not quite sure about: is the timeout useful or should relayd
just start the program and forget about it?

/Benno


Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/parse.y,v
retrieving revision 1.149
diff -u -r1.149 parse.y
--- parse.y	26 Oct 2010 15:04:37 -0000	1.149
+++ parse.y	2 Dec 2010 20:53:54 -0000
@@ -141,7 +141,7 @@
 %}
 
 %token	ALL APPEND BACKLOG BACKUP BUFFER CA CACHE CHANGE CHECK
-%token	CIPHERS CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT
+%token	CIPHERS CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXEC EXPECT
 %token	EXTERNAL FILENAME FILTER FORWARD FROM HASH HEADER HOST ICMP
 %token	INCLUDE INET INET6 INTERFACE INTERVAL IP LABEL LISTEN
 %token	LOADBALANCE LOG LOOKUP MARK MARKED MODE NAT NO
@@ -1523,6 +1523,18 @@
 			}
 			free($2);
 		}
+		| EXEC STRING TIMEOUT timeout {
+			bcopy(&$4, &table->conf.timeout,
+			    sizeof(struct timeval));
+			if (strlcpy(router->rt_conf.exec, $2,
+			    sizeof(router->rt_conf.exec)) >=
+			    sizeof(router->rt_conf.exec)) {
+				yyerror("exec command truncated");
+				free($2);
+				YYERROR;
+			}
+			free($2);
+		}
 		| DISABLE		{ rlay->rl_conf.flags |= F_DISABLE; }
 		| include
 		;
@@ -1719,6 +1731,7 @@
 		{ "digest",		DIGEST },
 		{ "disable",		DISABLE },
 		{ "error",		ERROR },
+		{ "exec",               EXEC },
 		{ "expect",		EXPECT },
 		{ "external",		EXTERNAL },
 		{ "file",		FILENAME },
Index: pfe_route.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/pfe_route.c,v
retrieving revision 1.1
diff -u -r1.1 pfe_route.c
--- pfe_route.c	13 Aug 2009 13:51:21 -0000	1.1
+++ pfe_route.c	2 Dec 2010 20:53:54 -0000
@@ -32,6 +32,10 @@
 #include <string.h>
 #include <errno.h>
 
+#include <sys/wait.h>
+#include <signal.h>
+#include <pwd.h>
+
 #include <openssl/ssl.h>
 
 #include "relayd.h"
@@ -56,6 +60,9 @@
 	}			 rm_u;
 };
 
+pid_t route_exec_child = -1;
+void pfe_route_exec_sig_alarm(int);
+
 void
 init_routes(struct relayd *env)
 {
@@ -237,4 +244,115 @@
 	    errno, strerror(errno));
 
 	return (-1);
+}
+
+/* code from check_script.c */
+
+void
+pfe_route_exec_sig_alarm(int sig)
+{
+	int save_errno = errno;
+
+	if (route_exec_child != -1)
+		kill(route_exec_child, SIGKILL);
+	errno = save_errno;
+}
+
+int
+pfe_route_exec(struct relayd *env, struct ctl_netroute *crt)
+{
+	struct netroute		*nr;
+
+	int			 status = 0, ret = 0;
+	sig_t			 save_quit, save_int, save_chld;
+	struct itimerval	 it;
+	struct timeval		*tv;
+	const char		*file, *arg_host, *arg_action;
+	struct host		*host;
+	struct passwd		*pw;
+
+	if ((nr = route_find(env, crt->id)) == NULL ||
+	    (host = host_find(env, crt->hostid)) == NULL) {
+		log_debug("pfe_route: invalid host or route id");
+		return (-1);
+	}
+
+	arg_host = host->conf.name;
+	arg_action = HOST_ISUP(crt->up) ? "added" : "deleted";
+	file = nr->nr_router->rt_conf.exec;
+	tv = &nr->nr_router->rt_conf.exec_timeout;
+
+	log_info("pfe_route_exec: %s %s %s",
+		 file,
+		 arg_host,
+		 arg_action);
+
+	save_quit = signal(SIGQUIT, SIG_IGN);
+	save_int = signal(SIGINT, SIG_IGN);
+	save_chld = signal(SIGCHLD, SIG_DFL);
+
+	switch (route_exec_child = fork()) {
+	case -1:
+		ret = -1;
+		goto done;
+	case 0:
+		signal(SIGQUIT, SIG_DFL);
+		signal(SIGINT, SIG_DFL);
+		signal(SIGCHLD, SIG_DFL);
+
+		if ((pw = getpwnam(RELAYD_USER)) == NULL)
+			fatal("pfe_route_exec: getpwnam");
+		if (chdir("/") == -1)
+			fatal("pfe_route_exec: chdir(\"/\")");
+		if (setgroups(1, &pw->pw_gid) ||
+		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+			fatal("pfe_route_exec: can't drop privileges");
+
+		/*
+		 * close fds before executing an external program, to
+		 * prevent access to internal fds, eg. IMSG connections
+		 * of internal processes.
+		 */
+		closefrom(STDERR_FILENO + 1);
+
+		execlp(file, file, arg_host, arg_action, (char *)NULL);
+		_exit(0);
+		break;
+	default:
+		/* Kill the process after a timeout */
+		signal(SIGALRM, pfe_route_exec_sig_alarm);
+		bzero(&it, sizeof(it));
+		bcopy(tv, &it.it_value, sizeof(it.it_value));
+		setitimer(ITIMER_REAL, &it, NULL);
+
+		waitpid(route_exec_child, &status, 0);
+		break;
+	}
+
+	switch (ret) {
+	case -1:
+		ret = -1;
+		break;
+	default:
+		if (WIFEXITED(status))
+			ret = WEXITSTATUS(status);
+		else
+			ret = -1;
+	}
+
+ done:
+	/* Disable the process timeout timer */
+	bzero(&it, sizeof(it));
+	setitimer(ITIMER_REAL, &it, NULL);
+	route_exec_child = -1;
+
+	signal(SIGQUIT, save_quit);
+	signal(SIGINT, save_int);
+	signal(SIGCHLD, save_chld);
+	signal(SIGALRM, SIG_DFL);
+
+	log_info("pfe_route_exec ret=%i",ret);
+
+	return (ret);
 }
Index: relayd.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v
retrieving revision 1.98
diff -u -r1.98 relayd.c
--- relayd.c	2 Sep 2010 14:03:22 -0000	1.98
+++ relayd.c	2 Dec 2010 20:54:27 -0000
@@ -664,6 +664,7 @@
 				    "invalid size of rtmsg request");
 			memcpy(&crt, imsg.data, sizeof(crt));
 			pfe_route(env, &crt);
+			pfe_route_exec(env, &crt);
 			break;
 		case IMSG_CTL_RELOAD:
 			/*
Index: relayd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v
retrieving revision 1.116
diff -u -r1.116 relayd.conf.5
--- relayd.conf.5	26 Oct 2010 15:26:58 -0000	1.116
+++ relayd.conf.5	2 Dec 2010 20:54:29 -0000
@@ -1113,7 +1113,18 @@
 .It Ic rtlabel Ar label
 Add the routes with the specified
 .Ar label
-to the kernel routing table.
+to the kernel routing table XXXX.
+.It Xo
+.Ic exec
+.Ar path
+.Ic timeout Ar number
+.Xc
+The program
+.Ar path
+will be run (with the two arguments gateway and "added" or "deleted")
+when a route is added or deleted. The program is killed after
+.Ar number
+seconds.
 .El
 .Sh FILES
 .Bl -tag -width "/etc/ssl/private/address.keyXX" -compact
Index: relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.138
diff -u -r1.138 relayd.h
--- relayd.h	26 Oct 2010 15:04:37 -0000	1.138
+++ relayd.h	2 Dec 2010 20:54:31 -0000
@@ -613,6 +613,8 @@
 	objid_t			 gwtable;
 	in_port_t		 gwport;
 	int			 rtable;
+        char                     exec[MAXPATHLEN];
+        struct timeval           exec_timeout;
 };
 
 struct router {
@@ -828,6 +830,7 @@
 void	 init_routes(struct relayd *);
 void	 sync_routes(struct relayd *, struct router *);
 int	 pfe_route(struct relayd *, struct ctl_netroute *);
+int	 pfe_route_exec(struct relayd *, struct ctl_netroute *);
 
 /* hce.c */
 pid_t	 hce(struct relayd *, int [2], int [2], int [RELAY_MAXPROC][2],
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
relayd: exec program on gateway change, Sebastian Benoit, (Mon Dec 27, 3:24 pm)
Re: relayd: exec program on gateway change, Reyk Floeter, (Wed Dec 29, 10:22 am)