separated updown listener to its own class 4.2.9
authorMartin Willi <martin@strongswan.org>
Mon, 17 Nov 2008 09:29:27 +0000 (09:29 -0000)
committerMartin Willi <martin@strongswan.org>
Mon, 17 Nov 2008 09:29:27 +0000 (09:29 -0000)
caching interface names to properly remove rules if interface has changed

src/charon/plugins/updown/Makefile.am
src/charon/plugins/updown/updown_listener.c [new file with mode: 0644]
src/charon/plugins/updown/updown_listener.h [new file with mode: 0644]
src/charon/plugins/updown/updown_plugin.c

index 9fc226b..de60d9f 100644 (file)
@@ -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 (file)
index 0000000..7dfb874
--- /dev/null
@@ -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 (file)
index 0000000..569d581
--- /dev/null
@@ -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_ @}*/
index f358026..2e58842 100644 (file)
  * $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;
 }