aboutsummaryrefslogtreecommitdiffstats
path: root/programs/starter/starter.c
diff options
context:
space:
mode:
Diffstat (limited to 'programs/starter/starter.c')
-rw-r--r--programs/starter/starter.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/programs/starter/starter.c b/programs/starter/starter.c
new file mode 100644
index 000000000..0b2c83369
--- /dev/null
+++ b/programs/starter/starter.c
@@ -0,0 +1,571 @@
+/* strongSwan IPsec starter
+ * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: starter.c,v 1.23 2006/02/15 18:37:46 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "confread.h"
+#include "files.h"
+#include "starterwhack.h"
+#include "invokepluto.h"
+#include "klips.h"
+#include "netkey.h"
+#include "cmp.h"
+#include "interfaces.h"
+
+#define FLAG_ACTION_START_PLUTO 0x01
+#define FLAG_ACTION_UPDATE 0x02
+#define FLAG_ACTION_RELOAD 0x04
+#define FLAG_ACTION_QUIT 0x08
+#define FLAG_ACTION_LISTEN 0x10
+
+static unsigned int _action_ = 0;
+
+static void
+fsig(int signal)
+{
+ switch (signal)
+ {
+ case SIGCHLD:
+ {
+ int status;
+ pid_t pid;
+ char *name = NULL;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ {
+ if (pid == starter_pluto_pid())
+ name = " (Pluto)";
+ if (WIFSIGNALED(status))
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has been killed by sig %d\n",
+ pid, name?name:"", WTERMSIG(status))
+ )
+ else if (WIFSTOPPED(status))
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has been stopped by sig %d\n",
+ pid, name?name:"", WSTOPSIG(status))
+ )
+ else if (WIFEXITED(status))
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has quit (exit code %d)\n",
+ pid, name?name:"", WEXITSTATUS(status))
+ )
+ else
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has quit", pid, name?name:"")
+ )
+
+ if (pid == starter_pluto_pid())
+ starter_pluto_sigchild(pid);
+ }
+ }
+ break;
+
+ case SIGPIPE:
+ /** ignore **/
+ break;
+
+ case SIGALRM:
+ _action_ |= FLAG_ACTION_START_PLUTO;
+ break;
+
+ case SIGHUP:
+ _action_ |= FLAG_ACTION_UPDATE;
+ break;
+
+ case SIGTERM:
+ case SIGQUIT:
+ case SIGINT:
+ _action_ |= FLAG_ACTION_QUIT;
+ break;
+
+ case SIGUSR1:
+ _action_ |= FLAG_ACTION_RELOAD;
+ _action_ |= FLAG_ACTION_UPDATE;
+ break;
+
+ default:
+ plog("fsig(): unknown signal %d -- investigate", signal);
+ break;
+ }
+}
+
+static void
+usage(char *name)
+{
+ fprintf(stderr, "Usage: starter [--nofork] [--auto-update <sec>] "
+ "[--debug|--debug-more|--debug-all]\n");
+ exit(1);
+}
+
+int main (int argc, char **argv)
+{
+ starter_config_t *cfg = NULL;
+ starter_config_t *new_cfg;
+ starter_conn_t *conn, *conn2;
+ starter_ca_t *ca, *ca2;
+
+ struct stat stb;
+
+ char *err = NULL;
+ int i;
+ int id = 1;
+ struct timeval tv;
+ unsigned long auto_update = 0;
+ time_t last_reload;
+ bool has_netkey;
+ bool no_fork = FALSE;
+
+ /* global variables defined in log.h */
+ log_to_stderr = TRUE;
+ base_debugging = DBG_NONE;
+
+ /* parse command line */
+ for (i = 1; i < argc; i++)
+ {
+ if (streq(argv[i], "--debug"))
+ {
+ base_debugging |= DBG_CONTROL;
+ }
+ else if (streq(argv[i], "--debug-more"))
+ {
+ base_debugging |= DBG_CONTROLMORE;
+ }
+ else if (streq(argv[i], "--debug-all"))
+ {
+ base_debugging |= DBG_ALL;
+ }
+ else if (streq(argv[i], "--nofork"))
+ {
+ no_fork = TRUE;
+ }
+ else if (streq(argv[i], "--auto-update") && i+1 < argc)
+ {
+ auto_update = atoi(argv[++i]);
+ if (!auto_update)
+ usage(argv[0]);
+ }
+ else
+ {
+ usage(argv[0]);
+ }
+ }
+
+ /* Init */
+ init_log("ipsec_starter");
+ cur_debugging = base_debugging;
+
+ signal(SIGHUP, fsig);
+ signal(SIGCHLD, fsig);
+ signal(SIGPIPE, fsig);
+ signal(SIGINT, fsig);
+ signal(SIGTERM, fsig);
+ signal(SIGQUIT, fsig);
+ signal(SIGALRM, fsig);
+ signal(SIGUSR1, fsig);
+
+ /* verify that we can start */
+ if (getuid() != 0)
+ {
+ plog("permission denied (must be superuser)");
+ exit(1);
+ }
+
+ if (stat(PID_FILE, &stb) == 0)
+ {
+ plog("pluto is already running (%s exists) -- aborting", PID_FILE);
+ exit(1);
+ }
+
+ if (stat(DEV_RANDOM, &stb) != 0)
+ {
+ plog("unable to start strongSwan IPsec -- no %s!", DEV_RANDOM);
+ exit(1);
+ }
+
+ if (stat(DEV_URANDOM, &stb)!= 0)
+ {
+ plog("unable to start strongSwan IPsec -- no %s!", DEV_URANDOM);
+ exit(1);
+ }
+
+ cfg = confread_load(CONFIG_FILE);
+ if (!cfg)
+ {
+ plog("unable to start strongSwan -- errors in config");
+ exit(1);
+ }
+
+ /* determine if we have a native netkey IPsec stack */
+ has_netkey = starter_netkey_init();
+
+ if (!has_netkey)
+ {
+ /* determine if we have a KLIPS IPsec stack instead */
+ if (starter_klips_init())
+ {
+ starter_klips_set_config(cfg);
+ starter_ifaces_init();
+ starter_ifaces_clear();
+ }
+ else
+ {
+ plog("neither netkey nor KLIPS IPSec stack detected");
+ exit(1);
+ }
+ }
+
+ last_reload = time(NULL);
+
+ plog("Starting strongSwan IPsec %s [starter]...", ipsec_version_code());
+
+ /* fork if we're not debugging stuff */
+ if (!no_fork)
+ {
+ log_to_stderr = FALSE;
+
+ switch (fork())
+ {
+ case 0:
+ {
+ int fnull = open("/dev/null", O_RDWR);
+
+ if (fnull >= 0)
+ {
+ dup2(fnull, STDIN_FILENO);
+ dup2(fnull, STDOUT_FILENO);
+ dup2(fnull, STDERR_FILENO);
+ close(fnull);
+ }
+ }
+ break;
+ case -1:
+ plog("can't fork: %s", strerror(errno));
+ break;
+ default:
+ exit(0);
+ }
+ }
+
+ /* save pid file in /var/run/starter.pid */
+ {
+ FILE *fd = fopen(MY_PID_FILE, "w");
+
+ if (fd)
+ {
+ fprintf(fd, "%u\n", getpid());
+ fclose(fd);
+ }
+ }
+
+ if (!has_netkey)
+ {
+ starter_ifaces_load(cfg->setup.interfaces
+ , cfg->setup.overridemtu
+ , cfg->setup.nat_traversal
+ , &cfg->defaultroute);
+ }
+
+ _action_ = FLAG_ACTION_START_PLUTO;
+
+ for (;;)
+ {
+ /*
+ * Stop pluto (if started) and exit
+ */
+ if (_action_ & FLAG_ACTION_QUIT)
+ {
+ if (starter_pluto_pid())
+ starter_stop_pluto();
+ if (has_netkey)
+ starter_netkey_cleanup();
+ else
+ {
+ starter_ifaces_clear();
+ starter_klips_cleanup();
+ }
+ confread_free(cfg);
+ unlink(MY_PID_FILE);
+ unlink(INFO_FILE);
+#ifdef LEAK_DETECTIVE
+ report_leaks();
+#endif /* LEAK_DETECTIVE */
+ close_log();
+ plog("ipsec starter stopped");
+ exit(0);
+ }
+
+ /*
+ * Delete all connections. Will be added below
+ */
+ if (_action_ & FLAG_ACTION_RELOAD)
+ {
+ if (starter_pluto_pid())
+ {
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ {
+ starter_whack_del_conn(conn);
+ conn->state = STATE_TO_ADD;
+ }
+ }
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ {
+ starter_whack_del_ca(ca);
+ ca->state = STATE_TO_ADD;
+ }
+ }
+ }
+ _action_ &= ~FLAG_ACTION_RELOAD;
+ }
+
+ /*
+ * Update configuration
+ */
+ if (_action_ & FLAG_ACTION_UPDATE)
+ {
+ err = NULL;
+ DBG(DBG_CONTROL,
+ DBG_log("Reloading config...")
+ )
+ new_cfg = confread_load(CONFIG_FILE);
+
+ if (new_cfg)
+ {
+ /* Switch to new config. New conn will be loaded below */
+ if (has_netkey)
+ {
+ if (!starter_cmp_defaultroute(&new_cfg->defaultroute
+ , &cfg->defaultroute))
+ {
+ _action_ |= FLAG_ACTION_LISTEN;
+ }
+ }
+ else
+ {
+ if (!starter_cmp_klips(cfg, new_cfg))
+ {
+ plog("KLIPS has changed");
+ starter_klips_set_config(new_cfg);
+ }
+
+ if (starter_ifaces_load(new_cfg->setup.interfaces
+ , new_cfg->setup.overridemtu
+ , new_cfg->setup.nat_traversal
+ , &new_cfg->defaultroute))
+ {
+ _action_ |= FLAG_ACTION_LISTEN;
+ }
+ }
+
+ if (!starter_cmp_pluto(cfg, new_cfg))
+ {
+ plog("Pluto has changed");
+ if (starter_pluto_pid())
+ starter_stop_pluto();
+ _action_ &= ~FLAG_ACTION_LISTEN;
+ _action_ |= FLAG_ACTION_START_PLUTO;
+ }
+ else
+ {
+ /* Only reload conn and ca sections if pluto is not killed */
+
+ /* Look for new connections that are already loaded */
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ {
+ for (conn2 = new_cfg->conn_first; conn2; conn2 = conn2->next)
+ {
+ if (conn2->state == STATE_TO_ADD
+ && starter_cmp_conn(conn, conn2))
+ {
+ conn->state = STATE_REPLACED;
+ conn2->state = STATE_ADDED;
+ conn2->id = conn->id;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Remove conn sections that have become unused */
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ starter_whack_del_conn(conn);
+ }
+
+ /* Look for new ca sections that are already loaded */
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ {
+ for (ca2 = new_cfg->ca_first; ca2; ca2 = ca2->next)
+ {
+ if (ca2->state == STATE_TO_ADD
+ && starter_cmp_ca(ca, ca2))
+ {
+ ca->state = STATE_REPLACED;
+ ca2->state = STATE_ADDED;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Remove ca sections that have become unused */
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ starter_whack_del_ca(ca);
+ }
+ }
+ confread_free(cfg);
+ cfg = new_cfg;
+ }
+ else
+ {
+ plog("can't reload config file: %s -- keeping old one");
+ }
+ _action_ &= ~FLAG_ACTION_UPDATE;
+ last_reload = time(NULL);
+ }
+
+ /*
+ * Start pluto
+ */
+ if (_action_ & FLAG_ACTION_START_PLUTO)
+ {
+ if (starter_pluto_pid() == 0)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("Attempting to start pluto...")
+ )
+ if (!has_netkey)
+ starter_klips_clear();
+
+ if (starter_start_pluto(cfg, no_fork) == 0)
+ {
+ starter_whack_listen();
+ }
+ else
+ {
+ /* schedule next try */
+ alarm(PLUTO_RESTART_DELAY);
+ }
+ }
+ _action_ &= ~FLAG_ACTION_START_PLUTO;
+
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ ca->state = STATE_TO_ADD;
+ }
+
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ conn->state = STATE_TO_ADD;
+ }
+ }
+
+ /*
+ * Tell pluto to reread its interfaces
+ */
+ if (_action_ & FLAG_ACTION_LISTEN)
+ {
+ starter_whack_listen();
+ _action_ &= ~FLAG_ACTION_LISTEN;
+ }
+
+ /*
+ * Add stale conn and ca sections
+ */
+ if (starter_pluto_pid() != 0)
+ {
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_TO_ADD)
+ {
+ starter_whack_add_ca(ca);
+ ca->state = STATE_ADDED;
+ }
+ }
+
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_TO_ADD)
+ {
+ if (conn->id == 0)
+ {
+ /* affect new unique id */
+ conn->id = id++;
+ }
+ starter_whack_add_conn(conn);
+ conn->state = STATE_ADDED;
+ if (conn->startup == STARTUP_START)
+ starter_whack_initiate_conn(conn);
+ else if (conn->startup == STARTUP_ROUTE)
+ starter_whack_route_conn(conn);
+ }
+ }
+ }
+
+ /*
+ * If auto_update activated, when to stop select
+ */
+ if (auto_update)
+ {
+ time_t now = time(NULL);
+ tv.tv_sec = (now < last_reload + auto_update)
+ ? (last_reload + auto_update-now) : 0;
+ tv.tv_usec = 0;
+ }
+
+ /*
+ * Wait for something to happen
+ */
+ if (select(0, NULL, NULL, NULL, auto_update ? &tv : NULL) == 0)
+ {
+ /* timeout -> auto_update */
+ _action_ |= FLAG_ACTION_UPDATE;
+ }
+ }
+
+ return 0;
+}
+