aboutsummaryrefslogtreecommitdiffstats
path: root/src/charon/plugins/updown
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2008-11-17 09:29:27 +0000
committerMartin Willi <martin@strongswan.org>2008-11-17 09:29:27 +0000
commit49653b6bc36b692bfa46fad2454bcd103c2f863e (patch)
treee5be9402b3879d489da42b82c00ed29e02b73523 /src/charon/plugins/updown
parent08c6ed9f14d358603a4bb84534080de8684be8b6 (diff)
downloadstrongswan-49653b6bc36b692bfa46fad2454bcd103c2f863e.tar.bz2
strongswan-49653b6bc36b692bfa46fad2454bcd103c2f863e.tar.xz
separated updown listener to its own class4.2.9
caching interface names to properly remove rules if interface has changed
Diffstat (limited to 'src/charon/plugins/updown')
-rw-r--r--src/charon/plugins/updown/Makefile.am4
-rw-r--r--src/charon/plugins/updown/updown_listener.c320
-rw-r--r--src/charon/plugins/updown/updown_listener.h51
-rw-r--r--src/charon/plugins/updown/updown_plugin.c206
4 files changed, 380 insertions, 201 deletions
diff --git a/src/charon/plugins/updown/Makefile.am b/src/charon/plugins/updown/Makefile.am
index 9fc226b5e..de60d9fbf 100644
--- a/src/charon/plugins/updown/Makefile.am
+++ b/src/charon/plugins/updown/Makefile.am
@@ -4,7 +4,9 @@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
plugin_LTLIBRARIES = libstrongswan-updown.la
-libstrongswan_updown_la_SOURCES = updown_plugin.h updown_plugin.c
+libstrongswan_updown_la_SOURCES = \
+ updown_plugin.h updown_plugin.c \
+ updown_listener.h updown_listener.c
libstrongswan_updown_la_LDFLAGS = -module
diff --git a/src/charon/plugins/updown/updown_listener.c b/src/charon/plugins/updown/updown_listener.c
new file mode 100644
index 000000000..7dfb874cb
--- /dev/null
+++ b/src/charon/plugins/updown/updown_listener.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+
+#include "updown_listener.h"
+
+#include <daemon.h>
+#include <config/child_cfg.h>
+
+typedef struct private_updown_listener_t private_updown_listener_t;
+
+/**
+ * Private data of an updown_listener_t object.
+ */
+struct private_updown_listener_t {
+
+ /**
+ * Public updown_listener_t interface.
+ */
+ updown_listener_t public;
+
+ /**
+ * List of cached interface names
+ */
+ linked_list_t *iface_cache;
+};
+
+typedef struct cache_entry_t cache_entry_t;
+
+/**
+ * Cache line in the interface name cache.
+ */
+struct cache_entry_t {
+ /** requid of the CHILD_SA */
+ u_int32_t reqid;
+ /** cached interface name */
+ char *iface;
+};
+
+/**
+ * Insert an interface name to the cache
+ */
+static void cache_iface(private_updown_listener_t *this, u_int32_t reqid,
+ char *iface)
+{
+ cache_entry_t *entry = malloc_thing(cache_entry_t);
+
+ entry->reqid = reqid;
+ entry->iface = strdup(iface);
+
+ this->iface_cache->insert_first(this->iface_cache, entry);
+}
+
+/**
+ * Remove a cached interface name and return it.
+ */
+static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid)
+{
+ enumerator_t *enumerator;
+ cache_entry_t *entry;
+ char *iface = NULL;
+
+ enumerator = this->iface_cache->create_enumerator(this->iface_cache);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ if (entry->reqid == reqid)
+ {
+ this->iface_cache->remove_at(this->iface_cache, enumerator);
+ iface = entry->iface;
+ free(entry);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return iface;
+}
+
+/**
+ * Run the up/down script
+ */
+static void updown(private_updown_listener_t *this, ike_sa_t *ike_sa,
+ child_sa_t *child_sa, bool up)
+{
+ traffic_selector_t *my_ts, *other_ts;
+ enumerator_t *enumerator;
+ child_cfg_t *config;
+ host_t *vip, *me, *other;
+ char *script;
+
+ config = child_sa->get_config(child_sa);
+ vip = ike_sa->get_virtual_ip(ike_sa, TRUE);
+ script = config->get_updown(config);
+ me = ike_sa->get_my_host(ike_sa);
+ other = ike_sa->get_other_host(ike_sa);
+
+ if (script == NULL)
+ {
+ return;
+ }
+
+ enumerator = child_sa->create_policy_enumerator(child_sa);
+ while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+ {
+ char command[1024];
+ char *my_client, *other_client, *my_client_mask, *other_client_mask;
+ char *pos, *virtual_ip, *iface;
+ FILE *shell;
+
+ /* get subnet/bits from string */
+ if (asprintf(&my_client, "%R", my_ts) < 0)
+ {
+ my_client = NULL;
+ }
+ pos = strchr(my_client, '/');
+ *pos = '\0';
+ my_client_mask = pos + 1;
+ pos = strchr(my_client_mask, '[');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+ if (asprintf(&other_client, "%R", other_ts) < 0)
+ {
+ other_client = NULL;
+ }
+ pos = strchr(other_client, '/');
+ *pos = '\0';
+ other_client_mask = pos + 1;
+ pos = strchr(other_client_mask, '[');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+
+ if (vip)
+ {
+ if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0)
+ {
+ virtual_ip = NULL;
+ }
+ }
+ else
+ {
+ if (asprintf(&virtual_ip, "") < 0)
+ {
+ virtual_ip = NULL;
+ }
+ }
+
+ if (up)
+ {
+ iface = charon->kernel_interface->get_interface(
+ charon->kernel_interface, me);
+ if (iface)
+ {
+ cache_iface(this, child_sa->get_reqid(child_sa), iface);
+ }
+ }
+ else
+ {
+ iface = uncache_iface(this, child_sa->get_reqid(child_sa));
+ }
+
+ /* build the command with all env variables.
+ * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
+ */
+ snprintf(command, sizeof(command),
+ "2>&1 "
+ "PLUTO_VERSION='1.1' "
+ "PLUTO_VERB='%s%s%s' "
+ "PLUTO_CONNECTION='%s' "
+ "PLUTO_INTERFACE='%s' "
+ "PLUTO_REQID='%u' "
+ "PLUTO_ME='%H' "
+ "PLUTO_MY_ID='%D' "
+ "PLUTO_MY_CLIENT='%s/%s' "
+ "PLUTO_MY_CLIENT_NET='%s' "
+ "PLUTO_MY_CLIENT_MASK='%s' "
+ "PLUTO_MY_PORT='%u' "
+ "PLUTO_MY_PROTOCOL='%u' "
+ "PLUTO_PEER='%H' "
+ "PLUTO_PEER_ID='%D' "
+ "PLUTO_PEER_CLIENT='%s/%s' "
+ "PLUTO_PEER_CLIENT_NET='%s' "
+ "PLUTO_PEER_CLIENT_MASK='%s' "
+ "PLUTO_PEER_PORT='%u' "
+ "PLUTO_PEER_PROTOCOL='%u' "
+ "%s"
+ "%s"
+ "%s",
+ up ? "up" : "down",
+ my_ts->is_host(my_ts, me) ? "-host" : "-client",
+ me->get_family(me) == AF_INET ? "" : "-v6",
+ config->get_name(config),
+ iface ? iface : "unknown",
+ child_sa->get_reqid(child_sa),
+ me, ike_sa->get_my_id(ike_sa),
+ my_client, my_client_mask,
+ my_client, my_client_mask,
+ my_ts->get_from_port(my_ts),
+ my_ts->get_protocol(my_ts),
+ other, ike_sa->get_other_id(ike_sa),
+ other_client, other_client_mask,
+ other_client, other_client_mask,
+ other_ts->get_from_port(other_ts),
+ other_ts->get_protocol(other_ts),
+ virtual_ip,
+ config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
+ script);
+ free(my_client);
+ free(other_client);
+ free(virtual_ip);
+ free(iface);
+
+ DBG3(DBG_CHD, "running updown script: %s", command);
+ shell = popen(command, "r");
+
+ if (shell == NULL)
+ {
+ DBG1(DBG_CHD, "could not execute updown script '%s'", script);
+ return;
+ }
+
+ while (TRUE)
+ {
+ char resp[128];
+
+ if (fgets(resp, sizeof(resp), shell) == NULL)
+ {
+ if (ferror(shell))
+ {
+ DBG1(DBG_CHD, "error reading output from updown script");
+ return;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ char *e = resp + strlen(resp);
+ if (e > resp && e[-1] == '\n')
+ { /* trim trailing '\n' */
+ e[-1] = '\0';
+ }
+ DBG1(DBG_CHD, "updown: %s", resp);
+ }
+ }
+ pclose(shell);
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Listener implementation
+ */
+static bool child_state_change(private_updown_listener_t *this, ike_sa_t *ike_sa,
+ child_sa_t *child_sa, child_sa_state_t state)
+{
+ child_sa_state_t old;
+
+ if (ike_sa)
+ {
+ old = child_sa->get_state(child_sa);
+
+ if ((old == CHILD_INSTALLED && state != CHILD_REKEYING ) ||
+ (old == CHILD_DELETING && state == CHILD_DESTROYING))
+ {
+ updown(this, ike_sa, child_sa, FALSE);
+ }
+ else if (state == CHILD_INSTALLED)
+ {
+ updown(this, ike_sa, child_sa, TRUE);
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Implementation of updown_listener_t.destroy.
+ */
+static void destroy(private_updown_listener_t *this)
+{
+ this->iface_cache->destroy(this->iface_cache);
+ free(this);
+}
+
+/**
+ * See header
+ */
+updown_listener_t *updown_listener_create()
+{
+ private_updown_listener_t *this = malloc_thing(private_updown_listener_t);
+
+ memset(&this->public.listener, 0, sizeof(listener_t));
+ this->public.listener.child_state_change = (void*)child_state_change;
+ this->public.destroy = (void(*)(updown_listener_t*))destroy;
+
+ this->iface_cache = linked_list_create();
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/updown/updown_listener.h b/src/charon/plugins/updown/updown_listener.h
new file mode 100644
index 000000000..569d5817e
--- /dev/null
+++ b/src/charon/plugins/updown/updown_listener.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup updown_listener updown_listener
+ * @{ @ingroup updown
+ */
+
+#ifndef UPDOWN_LISTENER_H_
+#define UPDOWN_LISTENER_H_
+
+#include <bus/bus.h>
+
+typedef struct updown_listener_t updown_listener_t;
+
+/**
+ * Listener which invokes the scripts on CHILD_SA up/down.
+ */
+struct updown_listener_t {
+
+ /**
+ * Implements listener_t.
+ */
+ listener_t listener;
+
+ /**
+ * Destroy a updown_listener_t.
+ */
+ void (*destroy)(updown_listener_t *this);
+};
+
+/**
+ * Create a updown_listener instance.
+ */
+updown_listener_t *updown_listener_create();
+
+#endif /* UPDOWN_LISTENER_ @}*/
diff --git a/src/charon/plugins/updown/updown_plugin.c b/src/charon/plugins/updown/updown_plugin.c
index f358026f7..2e5884222 100644
--- a/src/charon/plugins/updown/updown_plugin.c
+++ b/src/charon/plugins/updown/updown_plugin.c
@@ -15,13 +15,10 @@
* $Id$
*/
-#define _GNU_SOURCE
-#include <stdio.h>
-
#include "updown_plugin.h"
+#include "updown_listener.h"
#include <daemon.h>
-#include <config/child_cfg.h>
typedef struct private_updown_plugin_t private_updown_plugin_t;
@@ -38,205 +35,16 @@ struct private_updown_plugin_t {
/**
* Listener interface, listens to CHILD_SA state changes
*/
- listener_t listener;
+ updown_listener_t *listener;
};
/**
- * Run the up/down script
- */
-static void updown(ike_sa_t *ike_sa, child_sa_t *child_sa, bool up)
-{
- traffic_selector_t *my_ts, *other_ts;
- enumerator_t *enumerator;
- child_cfg_t *config;
- host_t *vip, *me, *other;
- char *script;
-
- config = child_sa->get_config(child_sa);
- vip = ike_sa->get_virtual_ip(ike_sa, TRUE);
- script = config->get_updown(config);
- me = ike_sa->get_my_host(ike_sa);
- other = ike_sa->get_other_host(ike_sa);
-
- if (script == NULL)
- {
- return;
- }
-
- enumerator = child_sa->create_policy_enumerator(child_sa);
- while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
- {
- char command[1024];
- char *my_client, *other_client, *my_client_mask, *other_client_mask;
- char *pos, *virtual_ip, *iface;
- FILE *shell;
-
- /* get subnet/bits from string */
- if (asprintf(&my_client, "%R", my_ts) < 0)
- {
- my_client = NULL;
- }
- pos = strchr(my_client, '/');
- *pos = '\0';
- my_client_mask = pos + 1;
- pos = strchr(my_client_mask, '[');
- if (pos)
- {
- *pos = '\0';
- }
- if (asprintf(&other_client, "%R", other_ts) < 0)
- {
- other_client = NULL;
- }
- pos = strchr(other_client, '/');
- *pos = '\0';
- other_client_mask = pos + 1;
- pos = strchr(other_client_mask, '[');
- if (pos)
- {
- *pos = '\0';
- }
-
- if (vip)
- {
- if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0)
- {
- virtual_ip = NULL;
- }
- }
- else
- {
- if (asprintf(&virtual_ip, "") < 0)
- {
- virtual_ip = NULL;
- }
- }
-
- iface = charon->kernel_interface->get_interface(
- charon->kernel_interface, me);
-
- /* build the command with all env variables.
- * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
- */
- snprintf(command, sizeof(command),
- "2>&1 "
- "PLUTO_VERSION='1.1' "
- "PLUTO_VERB='%s%s%s' "
- "PLUTO_CONNECTION='%s' "
- "PLUTO_INTERFACE='%s' "
- "PLUTO_REQID='%u' "
- "PLUTO_ME='%H' "
- "PLUTO_MY_ID='%D' "
- "PLUTO_MY_CLIENT='%s/%s' "
- "PLUTO_MY_CLIENT_NET='%s' "
- "PLUTO_MY_CLIENT_MASK='%s' "
- "PLUTO_MY_PORT='%u' "
- "PLUTO_MY_PROTOCOL='%u' "
- "PLUTO_PEER='%H' "
- "PLUTO_PEER_ID='%D' "
- "PLUTO_PEER_CLIENT='%s/%s' "
- "PLUTO_PEER_CLIENT_NET='%s' "
- "PLUTO_PEER_CLIENT_MASK='%s' "
- "PLUTO_PEER_PORT='%u' "
- "PLUTO_PEER_PROTOCOL='%u' "
- "%s"
- "%s"
- "%s",
- up ? "up" : "down",
- my_ts->is_host(my_ts, me) ? "-host" : "-client",
- me->get_family(me) == AF_INET ? "" : "-v6",
- config->get_name(config),
- iface ? iface : "unknown",
- child_sa->get_reqid(child_sa),
- me, ike_sa->get_my_id(ike_sa),
- my_client, my_client_mask,
- my_client, my_client_mask,
- my_ts->get_from_port(my_ts),
- my_ts->get_protocol(my_ts),
- other, ike_sa->get_other_id(ike_sa),
- other_client, other_client_mask,
- other_client, other_client_mask,
- other_ts->get_from_port(other_ts),
- other_ts->get_protocol(other_ts),
- virtual_ip,
- config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
- script);
- free(my_client);
- free(other_client);
- free(virtual_ip);
- free(iface);
-
- DBG3(DBG_CHD, "running updown script: %s", command);
- shell = popen(command, "r");
-
- if (shell == NULL)
- {
- DBG1(DBG_CHD, "could not execute updown script '%s'", script);
- return;
- }
-
- while (TRUE)
- {
- char resp[128];
-
- if (fgets(resp, sizeof(resp), shell) == NULL)
- {
- if (ferror(shell))
- {
- DBG1(DBG_CHD, "error reading output from updown script");
- return;
- }
- else
- {
- break;
- }
- }
- else
- {
- char *e = resp + strlen(resp);
- if (e > resp && e[-1] == '\n')
- { /* trim trailing '\n' */
- e[-1] = '\0';
- }
- DBG1(DBG_CHD, "updown: %s", resp);
- }
- }
- pclose(shell);
- }
- enumerator->destroy(enumerator);
-}
-
-/**
- * Listener implementation
- */
-static bool child_state_change(listener_t *this, ike_sa_t *ike_sa,
- child_sa_t *child_sa, child_sa_state_t state)
-{
- child_sa_state_t old;
-
- if (ike_sa)
- {
- old = child_sa->get_state(child_sa);
-
- if ((old == CHILD_INSTALLED && state != CHILD_REKEYING ) ||
- (old == CHILD_DELETING && state == CHILD_DESTROYING))
- {
- updown(ike_sa, child_sa, FALSE);
- }
- else if (state == CHILD_INSTALLED)
- {
- updown(ike_sa, child_sa, TRUE);
- }
- }
- return TRUE;
-}
-
-/**
* Implementation of plugin_t.destroy
*/
static void destroy(private_updown_plugin_t *this)
{
- charon->bus->remove_listener(charon->bus, &this->listener);
+ charon->bus->remove_listener(charon->bus, &this->listener->listener);
+ this->listener->destroy(this->listener);
free(this);
}
@@ -249,10 +57,8 @@ plugin_t *plugin_create()
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
- memset(&this->listener, 0, sizeof(listener_t));
- this->listener.child_state_change = child_state_change;
-
- charon->bus->add_listener(charon->bus, &this->listener);
+ this->listener = updown_listener_create();
+ charon->bus->add_listener(charon->bus, &this->listener->listener);
return &this->public.plugin;
}