diff options
author | Martin Willi <martin@strongswan.org> | 2008-11-17 09:29:27 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2008-11-17 09:29:27 +0000 |
commit | 49653b6bc36b692bfa46fad2454bcd103c2f863e (patch) | |
tree | e5be9402b3879d489da42b82c00ed29e02b73523 /src/charon/plugins | |
parent | 08c6ed9f14d358603a4bb84534080de8684be8b6 (diff) | |
download | strongswan-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')
-rw-r--r-- | src/charon/plugins/updown/Makefile.am | 4 | ||||
-rw-r--r-- | src/charon/plugins/updown/updown_listener.c | 320 | ||||
-rw-r--r-- | src/charon/plugins/updown/updown_listener.h | 51 | ||||
-rw-r--r-- | src/charon/plugins/updown/updown_plugin.c | 206 |
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; } |