Hi,
On Mon, Dec 27, 2010 at 11:24:20PM +0100, Sebastian Benoit wrote:
> 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.
>
I remember you were explaining the problem at EuroBSDCon ;-). And now
you have a diff - cool, thanks!
> Here is a patch that adds an "exec" option to the router section like this:
>
I would prefer to call it "run" (it is a full word and similar to the
same keyword in ifstated.conf).
> 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?
>
Yes, it makes sense. But we can give it a fairly high default value.
I like your diff and what it does. Please give me some time to look
at it in detail, maybe not before next year...
reyk
> /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],