counters: Move IKE event counter collection from stroke to a separate plugin
authorTobias Brunner <tobias@strongswan.org>
Thu, 24 Aug 2017 15:02:37 +0000 (17:02 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 8 Nov 2017 15:28:28 +0000 (16:28 +0100)
13 files changed:
configure.ac
src/libcharon/Makefile.am
src/libcharon/plugins/counters/Makefile.am [new file with mode: 0644]
src/libcharon/plugins/counters/counters_listener.c [new file with mode: 0644]
src/libcharon/plugins/counters/counters_listener.h [new file with mode: 0644]
src/libcharon/plugins/counters/counters_plugin.c [new file with mode: 0644]
src/libcharon/plugins/counters/counters_plugin.h [new file with mode: 0644]
src/libcharon/plugins/counters/counters_query.h [new file with mode: 0644]
src/libcharon/plugins/stroke/Makefile.am
src/libcharon/plugins/stroke/stroke_counter.c
src/libcharon/plugins/stroke/stroke_counter.h
src/libcharon/plugins/stroke/stroke_plugin.c
src/libcharon/plugins/stroke/stroke_socket.c

index 2d9ab05..1670b01 100644 (file)
@@ -263,6 +263,7 @@ ARG_ENABL_SET([android-log],    [enable Android specific logger plugin.])
 ARG_ENABL_SET([bypass-lan],     [enable plugin to install bypass policies for local subnets.])
 ARG_ENABL_SET([certexpire],     [enable CSV export of expiration dates of used certificates.])
 ARG_ENABL_SET([connmark],       [enable connmark plugin using conntrack based marks to select return path SA.])
+ARG_ENABL_SET([counters],       [enable plugin that collects several performance counters.])
 ARG_ENABL_SET([forecast],       [enable forecast plugin forwarding broadcast/multicast messages.])
 ARG_ENABL_SET([duplicheck],     [advanced duplicate checking plugin using liveness checks.])
 ARG_ENABL_SET([error-notify],   [enable error notification plugin.])
@@ -490,6 +491,10 @@ if test x$ntru = xtrue -o x$bliss = xtrue; then
        mgf1=true
 fi
 
+if test x$stroke = xtrue; then
+       counters=true
+fi
+
 # ===========================================
 #  check required libraries and header files
 # ===========================================
@@ -1497,6 +1502,7 @@ ADD_PLUGIN([radattr],              [c charon])
 ADD_PLUGIN([uci],                  [c charon])
 ADD_PLUGIN([addrblock],            [c charon])
 ADD_PLUGIN([unity],                [c charon])
+ADD_PLUGIN([counters],             [c charon])
 
 AC_SUBST(charon_plugins)
 AC_SUBST(starter_plugins)
@@ -1673,6 +1679,7 @@ AM_CONDITIONAL(USE_UNITY, test x$unity = xtrue)
 AM_CONDITIONAL(USE_RESOLVE, test x$resolve = xtrue)
 AM_CONDITIONAL(USE_ATTR, test x$attr = xtrue)
 AM_CONDITIONAL(USE_ATTR_SQL, test x$attr_sql = xtrue)
+AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
 
 #  other options
 # ---------------
@@ -1929,6 +1936,7 @@ AC_CONFIG_FILES([
        src/libcharon/plugins/socket_win/Makefile
        src/libcharon/plugins/bypass_lan/Makefile
        src/libcharon/plugins/connmark/Makefile
+       src/libcharon/plugins/counters/Makefile
        src/libcharon/plugins/forecast/Makefile
        src/libcharon/plugins/farp/Makefile
        src/libcharon/plugins/smp/Makefile
index ed2236e..964a19e 100644 (file)
@@ -258,6 +258,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_COUNTERS
+  SUBDIRS += plugins/counters
+if MONOLITHIC
+  libcharon_la_LIBADD += plugins/counters/libstrongswan-counters.la
+endif
+endif
+
 if USE_STROKE
   SUBDIRS += plugins/stroke
 if MONOLITHIC
diff --git a/src/libcharon/plugins/counters/Makefile.am b/src/libcharon/plugins/counters/Makefile.am
new file mode 100644 (file)
index 0000000..e5bde3f
--- /dev/null
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = \
+       $(PLUGIN_CFLAGS)
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-counters.la
+else
+plugin_LTLIBRARIES = libstrongswan-counters.la
+endif
+
+libstrongswan_counters_la_SOURCES = \
+       counters_plugin.h counters_plugin.c \
+       counters_listener.h counters_listener.c \
+       counters_query.h
+
+libstrongswan_counters_la_LDFLAGS = -module -avoid-version
diff --git a/src/libcharon/plugins/counters/counters_listener.c b/src/libcharon/plugins/counters/counters_listener.c
new file mode 100644 (file)
index 0000000..a32614e
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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.
+ */
+
+#include "counters_listener.h"
+#include "counters_query.h"
+
+#include <threading/spinlock.h>
+#include <collections/hashtable.h>
+#include <collections/array.h>
+
+typedef struct private_counters_listener_t private_counters_listener_t;
+typedef struct private_counters_query_t private_counters_query_t;
+
+/**
+ * Query interface
+ */
+struct private_counters_query_t {
+
+       /**
+        * Public interface
+        */
+       counters_query_t public;
+
+       /**
+        * Reference to this
+        */
+       private_counters_listener_t *this;
+};
+
+/**
+ * Private data
+ */
+struct private_counters_listener_t {
+
+       /**
+        * Public interface
+        */
+       counters_listener_t public;
+
+       /**
+        * Query interface
+        */
+       private_counters_query_t query;
+
+       /**
+        * Global counter values
+        */
+       uint64_t counters[COUNTER_MAX];
+
+       /**
+        * Counters for specific connection names, char* => entry_t
+        */
+       hashtable_t *conns;
+
+       /**
+        * Lock for counter values
+        */
+       spinlock_t *lock;
+};
+
+/**
+ * Counters for a specific connection name
+ */
+typedef struct {
+       /** connection name */
+       char *name;
+       /** counter values for connection */
+       uint64_t counters[COUNTER_MAX];
+} entry_t;
+
+/**
+ * Destroy named entry
+ */
+static void destroy_entry(entry_t *this)
+{
+       free(this->name);
+       free(this);
+}
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(char *name)
+{
+       return chunk_hash(chunk_from_str(name));
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(char *a, char *b)
+{
+       return streq(a, b);
+}
+
+/**
+ * Get the name of an IKE_SA, but return NULL if it is not known yet
+ */
+static char *get_ike_sa_name(ike_sa_t *ike_sa)
+{
+       peer_cfg_t *peer_cfg;
+
+       if (ike_sa)
+       {
+               peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+               if (peer_cfg)
+               {
+                       return peer_cfg->get_name(peer_cfg);
+               }
+       }
+       return NULL;
+}
+
+/**
+ * Increase a counter for a named entry
+ */
+static void count_named(private_counters_listener_t *this,
+                                               ike_sa_t *ike_sa, counter_type_t type)
+{
+       entry_t *entry;
+       char *name;
+
+       name = get_ike_sa_name(ike_sa);
+       if (name)
+       {
+               entry = this->conns->get(this->conns, name);
+               if (!entry)
+               {
+                       INIT(entry,
+                               .name = strdup(name),
+                       );
+                       this->conns->put(this->conns, entry->name, entry);
+               }
+               entry->counters[type]++;
+       }
+}
+
+METHOD(listener_t, alert, bool,
+       private_counters_listener_t *this, ike_sa_t *ike_sa,
+       alert_t alert, va_list args)
+{
+       counter_type_t type;
+
+       switch (alert)
+       {
+               case ALERT_INVALID_IKE_SPI:
+                       type = COUNTER_IN_INVALID_IKE_SPI;
+                       break;
+               case ALERT_PARSE_ERROR_HEADER:
+               case ALERT_PARSE_ERROR_BODY:
+                       type = COUNTER_IN_INVALID;
+                       break;
+               default:
+                       return TRUE;
+       }
+
+       this->lock->lock(this->lock);
+       this->counters[type]++;
+       count_named(this, ike_sa, type);
+       this->lock->unlock(this->lock);
+
+       return TRUE;
+}
+
+METHOD(listener_t, ike_rekey, bool,
+       private_counters_listener_t *this, ike_sa_t *old, ike_sa_t *new)
+{
+       counter_type_t type;
+       ike_sa_id_t *id;
+
+       id = new->get_id(new);
+       if (id->is_initiator(id))
+       {
+               type = COUNTER_INIT_IKE_SA_REKEY;
+       }
+       else
+       {
+               type = COUNTER_RESP_IKE_SA_REKEY;
+       }
+
+       this->lock->lock(this->lock);
+       this->counters[type]++;
+       count_named(this, old, type);
+       this->lock->unlock(this->lock);
+
+       return TRUE;
+}
+
+METHOD(listener_t, child_rekey, bool,
+       private_counters_listener_t *this, ike_sa_t *ike_sa,
+       child_sa_t *old, child_sa_t *new)
+{
+       this->lock->lock(this->lock);
+       this->counters[COUNTER_CHILD_SA_REKEY]++;
+       count_named(this, ike_sa, COUNTER_CHILD_SA_REKEY);
+       this->lock->unlock(this->lock);
+
+       return TRUE;
+}
+
+METHOD(listener_t, message_hook, bool,
+       private_counters_listener_t *this, ike_sa_t *ike_sa, message_t *message,
+       bool incoming, bool plain)
+{
+       counter_type_t type;
+       bool request;
+
+       if ((incoming && !plain) || (!incoming && !plain))
+       {       /* handle each message only once */
+               return TRUE;
+       }
+
+       request = message->get_request(message);
+       switch (message->get_exchange_type(message))
+       {
+               case IKE_SA_INIT:
+                       if (incoming)
+                       {
+                               type = request ? COUNTER_IN_IKE_SA_INIT_REQ
+                                                          : COUNTER_IN_IKE_SA_INIT_RSP;
+                       }
+                       else
+                       {
+                               type = request ? COUNTER_OUT_IKE_SA_INIT_REQ
+                                                          : COUNTER_OUT_IKE_SA_INIT_RES;
+                       }
+                       break;
+               case IKE_AUTH:
+                       if (incoming)
+                       {
+                               type = request ? COUNTER_IN_IKE_AUTH_REQ
+                                                          : COUNTER_IN_IKE_AUTH_RSP;
+                       }
+                       else
+                       {
+                               type = request ? COUNTER_OUT_IKE_AUTH_REQ
+                                                          : COUNTER_OUT_IKE_AUTH_RSP;
+                       }
+                       break;
+               case CREATE_CHILD_SA:
+                       if (incoming)
+                       {
+                               type = request ? COUNTER_IN_CREATE_CHILD_SA_REQ
+                                                          : COUNTER_IN_CREATE_CHILD_SA_RSP;
+                       }
+                       else
+                       {
+                               type = request ? COUNTER_OUT_CREATE_CHILD_SA_REQ
+                                                          : COUNTER_OUT_CREATE_CHILD_SA_RSP;
+                       }
+                       break;
+               case INFORMATIONAL:
+                       if (incoming)
+                       {
+                               type = request ? COUNTER_IN_INFORMATIONAL_REQ
+                                                          : COUNTER_IN_INFORMATIONAL_RSP;
+                       }
+                       else
+                       {
+                               type = request ? COUNTER_OUT_INFORMATIONAL_REQ
+                                                          : COUNTER_OUT_INFORMATIONAL_RSP;
+                       }
+                       break;
+               default:
+                       return TRUE;
+       }
+
+       this->lock->lock(this->lock);
+       this->counters[type]++;
+       count_named(this, ike_sa, type);
+       this->lock->unlock(this->lock);
+
+       return TRUE;
+}
+
+CALLBACK(free_names, void,
+       array_t * names)
+{
+       array_destroy_function(names, (void*)free, NULL);
+}
+
+METHOD(counters_query_t, get_names, enumerator_t*,
+       private_counters_query_t *query)
+{
+       private_counters_listener_t *this = query->this;
+       enumerator_t *enumerator;
+       array_t *names;
+       char *name;
+
+       this->lock->lock(this->lock);
+       names = array_create(0, this->conns->get_count(this->conns));
+       enumerator = this->conns->create_enumerator(this->conns);
+       while (enumerator->enumerate(enumerator, &name, NULL))
+       {
+               array_insert(names, ARRAY_TAIL, strdup(name));
+       }
+       enumerator->destroy(enumerator);
+       this->lock->unlock(this->lock);
+
+       array_sort(names, (void*)strcmp, NULL);
+
+       return enumerator_create_cleaner(array_create_enumerator(names),
+                                                                        free_names, names);
+}
+
+METHOD(counters_query_t, get, bool,
+       private_counters_query_t *query, counter_type_t type, char *name,
+       uint64_t *value)
+{
+       private_counters_listener_t *this = query->this;
+       uint64_t *counters = this->counters;
+
+       this->lock->lock(this->lock);
+       if (name)
+       {
+               entry_t *entry;
+
+               entry = this->conns->get(this->conns, name);
+               if (!entry)
+               {
+                       this->lock->unlock(this->lock);
+                       return FALSE;
+               }
+               counters = entry->counters;
+       }
+       if (value)
+       {
+               *value = counters[type];
+       }
+       this->lock->unlock(this->lock);
+       return TRUE;
+}
+
+METHOD(counters_query_t, get_all, uint64_t*,
+       private_counters_query_t *query, char *name)
+{
+       private_counters_listener_t *this = query->this;
+       entry_t *entry;
+       uint64_t *result, *counters = this->counters;
+       counter_type_t i;
+
+       result = calloc(COUNTER_MAX, sizeof(uint64_t));
+
+       this->lock->lock(this->lock);
+       if (name)
+       {
+               entry = this->conns->get(this->conns, name);
+               if (!entry)
+               {
+                       this->lock->unlock(this->lock);
+                       free(result);
+                       return NULL;
+               }
+               counters = &entry->counters[0];
+       }
+       for (i = 0; i < countof(this->counters); i++)
+       {
+               result[i] = counters[i];
+       }
+       this->lock->unlock(this->lock);
+       return result;
+}
+
+METHOD(counters_query_t, reset, void,
+       private_counters_query_t *query, char *name)
+{
+       private_counters_listener_t *this = query->this;
+       entry_t *entry = NULL;
+
+       this->lock->lock(this->lock);
+       if (name)
+       {
+               entry = this->conns->remove(this->conns, name);
+       }
+       else
+       {
+               memset(&this->counters, 0, sizeof(this->counters));
+       }
+       this->lock->unlock(this->lock);
+
+       if (entry)
+       {
+               destroy_entry(entry);
+       }
+}
+
+METHOD(counters_query_t, reset_all, void,
+       private_counters_query_t *query)
+{
+       private_counters_listener_t *this = query->this;
+       hashtable_t *new_conns, *conns;
+
+       new_conns = hashtable_create((hashtable_hash_t)hash,
+                                                                (hashtable_equals_t)equals, 4);
+
+       this->lock->lock(this->lock);
+       conns = this->conns;
+       this->conns = new_conns;
+       this->lock->unlock(this->lock);
+
+       conns->destroy_function(conns, (void*)destroy_entry);
+}
+
+METHOD(counters_listener_t, destroy, void,
+       private_counters_listener_t *this)
+{
+       lib->set(lib, "counters", NULL);
+
+       this->conns->destroy_function(this->conns, (void*)destroy_entry);
+       this->lock->destroy(this->lock);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+counters_listener_t *counters_listener_create()
+{
+       private_counters_listener_t *this;
+
+       INIT(this,
+               .public = {
+                       .listener = {
+                               .alert = _alert,
+                               .ike_rekey = _ike_rekey,
+                               .child_rekey = _child_rekey,
+                               .message = _message_hook,
+                       },
+                       .destroy = _destroy,
+               },
+               .query = {
+                       .public = {
+                               .get_names = _get_names,
+                               .get = _get,
+                               .get_all = _get_all,
+                               .reset = _reset,
+                               .reset_all = _reset_all,
+                       },
+               },
+               .conns = hashtable_create((hashtable_hash_t)hash,
+                                                                 (hashtable_equals_t)equals, 4),
+               .lock = spinlock_create(),
+       );
+       this->query.this = this;
+
+       lib->set(lib, "counters", &this->query);
+
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/counters/counters_listener.h b/src/libcharon/plugins/counters/counters_listener.h
new file mode 100644 (file)
index 0000000..6d9168f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR 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.
+ */
+
+/**
+ * @defgroup counters_listener counters_listener
+ * @{ @ingroup counters
+ */
+
+#ifndef COUNTERS_LISTENER_H_
+#define COUNTERS_LISTENER_H_
+
+#include <bus/listeners/listener.h>
+
+typedef struct counters_listener_t counters_listener_t;
+
+/**
+ * Collect counter values for different IKE events.
+ */
+struct counters_listener_t {
+
+       /**
+        * Implements listener_t interface.
+        */
+       listener_t listener;
+
+       /**
+        * Destroy a counters_listener_t.
+        */
+       void (*destroy)(counters_listener_t *this);
+};
+
+/**
+ * Create a counters_listener_t instance.
+ */
+counters_listener_t *counters_listener_create();
+
+#endif /** COUNTERS_LISTENER_H_ @}*/
diff --git a/src/libcharon/plugins/counters/counters_plugin.c b/src/libcharon/plugins/counters/counters_plugin.c
new file mode 100644 (file)
index 0000000..48de927
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR 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.
+ */
+
+#include "counters_plugin.h"
+
+#include <daemon.h>
+#include <plugins/plugin_feature.h>
+
+#include "counters_listener.h"
+
+typedef struct private_counters_plugin_t private_counters_plugin_t;
+
+/**
+ * Private data
+ */
+struct private_counters_plugin_t {
+
+       /**
+        * Public interface
+        */
+       counters_plugin_t public;
+
+       /**
+        * Listener implementation
+        */
+       counters_listener_t *listener;
+};
+
+METHOD(plugin_t, get_name, char*,
+       private_counters_plugin_t *this)
+{
+       return "counters";
+}
+
+/**
+ * Register listener
+ */
+static bool plugin_cb(private_counters_plugin_t *this,
+                                         plugin_feature_t *feature, bool reg, void *cb_data)
+{
+       if (reg)
+       {
+               charon->bus->add_listener(charon->bus, &this->listener->listener);
+       }
+       else
+       {
+               charon->bus->remove_listener(charon->bus, &this->listener->listener);
+       }
+       return TRUE;
+}
+
+METHOD(plugin_t, get_features, int,
+       private_counters_plugin_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
+                       PLUGIN_PROVIDE(CUSTOM, "counters"),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+       private_counters_plugin_t *this)
+{
+       this->listener->destroy(this->listener);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+plugin_t *counters_plugin_create()
+{
+       private_counters_plugin_t *this;
+
+       INIT(this,
+               .public = {
+                       .plugin = {
+                               .get_name = _get_name,
+                               .get_features = _get_features,
+                               .destroy = _destroy,
+                       },
+               },
+               .listener = counters_listener_create(),
+       );
+
+       return &this->public.plugin;
+}
diff --git a/src/libcharon/plugins/counters/counters_plugin.h b/src/libcharon/plugins/counters/counters_plugin.h
new file mode 100644 (file)
index 0000000..76fe2c2
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR 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.
+ */
+
+/**
+ * @defgroup counters counters
+ * @ingroup cplugins
+ *
+ * @defgroup counters_plugin counters_plugin
+ * @{ @ingroup counters
+ */
+
+#ifndef COUNTERS_PLUGIN_H_
+#define COUNTERS_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct counters_plugin_t counters_plugin_t;
+
+/**
+ * Plugin collecting several IKE event counters.
+ *
+ * Interested components can query individual counters via the 'counters'
+ * object registered on lib that implements the counters_query.h interface.
+ */
+struct counters_plugin_t {
+
+       /**
+        * Implements plugin_t. interface.
+        */
+       plugin_t plugin;
+};
+
+#endif /** COUNTERS_PLUGIN_H_ @}*/
diff --git a/src/libcharon/plugins/counters/counters_query.h b/src/libcharon/plugins/counters/counters_query.h
new file mode 100644 (file)
index 0000000..f785a68
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup counters_query counters_query
+ * @{ @ingroup counters
+ */
+
+#ifndef COUNTERS_QUERY_H_
+#define COUNTERS_QUERY_H_
+
+#include <bus/listeners/listener.h>
+
+typedef struct counters_query_t counters_query_t;
+typedef enum counter_type_t counter_type_t;
+
+enum counter_type_t {
+       /** initiated IKE_SA rekeyings */
+       COUNTER_INIT_IKE_SA_REKEY,
+       /** responded IKE_SA rekeyings */
+       COUNTER_RESP_IKE_SA_REKEY,
+       /** completed CHILD_SA rekeyings */
+       COUNTER_CHILD_SA_REKEY,
+       /** messages with invalid types, length, or a value out of range */
+       COUNTER_IN_INVALID,
+       /** messages with an invalid IKE SPI */
+       COUNTER_IN_INVALID_IKE_SPI,
+       /** received IKE_SA_INIT requests */
+       COUNTER_IN_IKE_SA_INIT_REQ,
+       /** received IKE_SA_INIT responses */
+       COUNTER_IN_IKE_SA_INIT_RSP,
+       /** sent IKE_SA_INIT requests */
+       COUNTER_OUT_IKE_SA_INIT_REQ,
+       /** sent IKE_SA_INIT responses */
+       COUNTER_OUT_IKE_SA_INIT_RES,
+       /** received IKE_AUTH requests */
+       COUNTER_IN_IKE_AUTH_REQ,
+       /** received IKE_AUTH responses */
+       COUNTER_IN_IKE_AUTH_RSP,
+       /** sent IKE_AUTH requests */
+       COUNTER_OUT_IKE_AUTH_REQ,
+       /** sent IKE_AUTH responses */
+       COUNTER_OUT_IKE_AUTH_RSP,
+       /** received CREATE_CHILD_SA requests */
+       COUNTER_IN_CREATE_CHILD_SA_REQ,
+       /** received CREATE_CHILD_SA responses */
+       COUNTER_IN_CREATE_CHILD_SA_RSP,
+       /** sent CREATE_CHILD_SA requests */
+       COUNTER_OUT_CREATE_CHILD_SA_REQ,
+       /** sent CREATE_CHILD_SA responses */
+       COUNTER_OUT_CREATE_CHILD_SA_RSP,
+       /** received INFORMATIONAL requests */
+       COUNTER_IN_INFORMATIONAL_REQ,
+       /** received INFORMATIONAL responses */
+       COUNTER_IN_INFORMATIONAL_RSP,
+       /** sent INFORMATIONAL requests */
+       COUNTER_OUT_INFORMATIONAL_REQ,
+       /** sent INFORMATIONAL responses */
+       COUNTER_OUT_INFORMATIONAL_RSP,
+       /** number of counter types */
+       COUNTER_MAX
+};
+
+/**
+ * Query counter values for different IKE events.
+ */
+struct counters_query_t {
+
+       /**
+        * Enumerate all connection names for which counters are currently recorded.
+        *
+        * @return                              enumerator over names (char *)
+        */
+       enumerator_t *(*get_names)(counters_query_t *this);
+
+       /**
+        * Get a current global or connection-specific counter value.
+        *
+        * @param type                  counter to query
+        * @param name                  connection name to get counter for, NULL for global
+        * @param[out] value    counter value
+        * @return                              TRUE if value found and returned
+        */
+       bool (*get)(counters_query_t *this, counter_type_t type, char *name,
+                               uint64_t *value);
+
+       /**
+        * Get all global or connection-specific counter values.
+        *
+        * @param name                  connection name to get counters for, NULL for global
+        * @return                              array of counters (has to be freed), NULL if named
+        *                                              connection is not found
+        */
+       uint64_t *(*get_all)(counters_query_t *this, char *name);
+
+       /**
+        * Reset all global or connection-specific counters.
+        *
+        * @param name                  connection name to reset counters, NULL for global
+        */
+       void (*reset)(counters_query_t *this, char *name);
+
+       /**
+        * Reset counters for all connections, global counters are unaffected.
+        */
+       void (*reset_all)(counters_query_t *this);
+};
+
+#endif /** COUNTERS_QUERY_H_ @}*/
index 26edc3d..9456dd9 100644 (file)
@@ -1,5 +1,6 @@
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libcharon/plugins/counters \
        -I$(top_srcdir)/src/libcharon \
        -I$(top_srcdir)/src/stroke \
        -DIPSEC_CONFDIR=\"${sysconfdir}\" \
index e93fd4e..8eb9968 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
  * Copyright (C) 2012 Martin Willi
  * Copyright (C) 2012 revosec AG
  *
  * for more details.
  */
 
+#include <inttypes.h>
+
 #include "stroke_counter.h"
 
-#include <threading/spinlock.h>
-#include <collections/hashtable.h>
+#include <counters_query.h>
 
 ENUM(stroke_counter_type_names,
        COUNTER_INIT_IKE_SA_REKEY, COUNTER_OUT_INFORMATIONAL_RSP,
@@ -56,277 +60,51 @@ struct private_stroke_counter_t {
        stroke_counter_t public;
 
        /**
-        * Global counter values
-        */
-       uint64_t counter[COUNTER_MAX];
-
-       /**
-        * Counters for specific connection names, char* => entry_t
-        */
-       hashtable_t *conns;
-
-       /**
-        * Lock for counter values
+        * Reference to query interface
         */
-       spinlock_t *lock;
+       counters_query_t *query;
 };
 
 /**
- * Counters for a specific connection name
- */
-typedef struct {
-       /** connection name */
-       char *name;
-       /** counter values for connection */
-       uint64_t counter[COUNTER_MAX];
-} entry_t;
-
-/**
- * Destroy named entry
- */
-static void destroy_entry(entry_t *this)
-{
-       free(this->name);
-       free(this);
-}
-
-/**
- * Hashtable hash function
- */
-static u_int hash(char *name)
-{
-       return chunk_hash(chunk_from_str(name));
-}
-
-/**
- * Hashtable equals function
- */
-static bool equals(char *a, char *b)
-{
-       return streq(a, b);
-}
-
-/**
- * Get the name of an IKE_SA, but return NULL if it is not known yet
- */
-static char *get_ike_sa_name(ike_sa_t *ike_sa)
-{
-       peer_cfg_t *peer_cfg;
-
-       if (ike_sa)
-       {
-               peer_cfg = ike_sa->get_peer_cfg(ike_sa);
-               if (peer_cfg)
-               {
-                       return peer_cfg->get_name(peer_cfg);
-               }
-       }
-       return NULL;
-}
-
-/**
- * Increase a counter for a named entry
+ * Make sure we have the query interface
  */
-static void count_named(private_stroke_counter_t *this,
-                                               ike_sa_t *ike_sa, stroke_counter_type_t type)
+static inline bool ensure_query(private_stroke_counter_t *this)
 {
-       entry_t *entry;
-       char *name;
-
-       name = get_ike_sa_name(ike_sa);
-       if (name)
+       if (this->query)
        {
-               entry = this->conns->get(this->conns, name);
-               if (!entry)
-               {
-                       INIT(entry,
-                               .name = strdup(name),
-                       );
-                       this->conns->put(this->conns, entry->name, entry);
-               }
-               entry->counter[type]++;
-       }
-}
-
-METHOD(listener_t, alert, bool,
-       private_stroke_counter_t *this, ike_sa_t *ike_sa,
-       alert_t alert, va_list args)
-{
-       stroke_counter_type_t type;
-
-       switch (alert)
-       {
-               case ALERT_INVALID_IKE_SPI:
-                       type = COUNTER_IN_INVALID_IKE_SPI;
-                       break;
-               case ALERT_PARSE_ERROR_HEADER:
-               case ALERT_PARSE_ERROR_BODY:
-                       type = COUNTER_IN_INVALID;
-                       break;
-               default:
-                       return TRUE;
-       }
-
-       this->lock->lock(this->lock);
-       this->counter[type]++;
-       count_named(this, ike_sa, type);
-       this->lock->unlock(this->lock);
-
-       return TRUE;
-}
-
-METHOD(listener_t, ike_rekey, bool,
-       private_stroke_counter_t *this, ike_sa_t *old, ike_sa_t *new)
-{
-       stroke_counter_type_t type;
-       ike_sa_id_t *id;
-
-       id = new->get_id(new);
-       if (id->is_initiator(id))
-       {
-               type = COUNTER_INIT_IKE_SA_REKEY;
-       }
-       else
-       {
-               type = COUNTER_RESP_IKE_SA_REKEY;
-       }
-
-       this->lock->lock(this->lock);
-       this->counter[type]++;
-       count_named(this, old, type);
-       this->lock->unlock(this->lock);
-
-       return TRUE;
-}
-
-METHOD(listener_t, child_rekey, bool,
-       private_stroke_counter_t *this, ike_sa_t *ike_sa,
-       child_sa_t *old, child_sa_t *new)
-{
-       this->lock->lock(this->lock);
-       this->counter[COUNTER_CHILD_SA_REKEY]++;
-       count_named(this, ike_sa, COUNTER_CHILD_SA_REKEY);
-       this->lock->unlock(this->lock);
-
-       return TRUE;
-}
-
-METHOD(listener_t, message_hook, bool,
-       private_stroke_counter_t *this, ike_sa_t *ike_sa, message_t *message,
-       bool incoming, bool plain)
-{
-       stroke_counter_type_t type;
-       bool request;
-
-       if ((incoming && !plain) || (!incoming && !plain))
-       {       /* handle each message only once */
                return TRUE;
        }
-
-       request = message->get_request(message);
-       switch (message->get_exchange_type(message))
-       {
-               case IKE_SA_INIT:
-                       if (incoming)
-                       {
-                               type = request ? COUNTER_IN_IKE_SA_INIT_REQ
-                                                          : COUNTER_IN_IKE_SA_INIT_RSP;
-                       }
-                       else
-                       {
-                               type = request ? COUNTER_OUT_IKE_SA_INIT_REQ
-                                                          : COUNTER_OUT_IKE_SA_INIT_RES;
-                       }
-                       break;
-               case IKE_AUTH:
-                       if (incoming)
-                       {
-                               type = request ? COUNTER_IN_IKE_AUTH_REQ
-                                                          : COUNTER_IN_IKE_AUTH_RSP;
-                       }
-                       else
-                       {
-                               type = request ? COUNTER_OUT_IKE_AUTH_REQ
-                                                          : COUNTER_OUT_IKE_AUTH_RSP;
-                       }
-                       break;
-               case CREATE_CHILD_SA:
-                       if (incoming)
-                       {
-                               type = request ? COUNTER_IN_CREATE_CHILD_SA_REQ
-                                                          : COUNTER_IN_CREATE_CHILD_SA_RSP;
-                       }
-                       else
-                       {
-                               type = request ? COUNTER_OUT_CREATE_CHILD_SA_REQ
-                                                          : COUNTER_OUT_CREATE_CHILD_SA_RSP;
-                       }
-                       break;
-               case INFORMATIONAL:
-                       if (incoming)
-                       {
-                               type = request ? COUNTER_IN_INFORMATIONAL_REQ
-                                                          : COUNTER_IN_INFORMATIONAL_RSP;
-                       }
-                       else
-                       {
-                               type = request ? COUNTER_OUT_INFORMATIONAL_REQ
-                                                          : COUNTER_OUT_INFORMATIONAL_RSP;
-                       }
-                       break;
-               default:
-                       return TRUE;
-       }
-
-       this->lock->lock(this->lock);
-       this->counter[type]++;
-       count_named(this, ike_sa, type);
-       this->lock->unlock(this->lock);
-
-       return TRUE;
+       return (this->query = lib->get(lib, "counters")) != NULL;
 }
 
 /**
- * Print a single counter value to out
- */
-static void print_counter(FILE *out, stroke_counter_type_t type,
-                                                 uint64_t counter)
-{
-       fprintf(out, "%-18N %12llu\n", stroke_counter_type_names, type, counter);
-}
-
-/**
- * Print IKE counters for a specific connection
+ * Print global or connection-specific IKE counters
  */
 static void print_one(private_stroke_counter_t *this, FILE *out, char *name)
 {
-       uint64_t counter[COUNTER_MAX];
-       entry_t *entry;
-       int i;
+       uint64_t *counters;
+       counter_type_t i;
 
-       this->lock->lock(this->lock);
-       entry = this->conns->get(this->conns, name);
-       if (entry)
+       counters = this->query->get_all(this->query, name);
+       if (!counters)
        {
-               for (i = 0; i < countof(this->counter); i++)
-               {
-                       counter[i] = entry->counter[i];
-               }
+               fprintf(out, "No IKE counters found for '%s'\n", name);
+               return;
        }
-       this->lock->unlock(this->lock);
-
-       if (entry)
+       if (name)
        {
                fprintf(out, "\nList of IKE counters for '%s':\n\n", name);
-               for (i = 0; i < countof(this->counter); i++)
-               {
-                       print_counter(out, i, counter[i]);
-               }
        }
        else
        {
-               fprintf(out, "No IKE counters found for '%s'\n", name);
+               fprintf(out, "\nList of IKE counters:\n\n");
+       }
+       for (i = 0; i < COUNTER_MAX; i++)
+       {
+               fprintf(out, "%-18N %12"PRIu64"\n", stroke_counter_type_names, i,
+                               counters[i]);
        }
+       free(counters);
 }
 
 /**
@@ -335,104 +113,44 @@ static void print_one(private_stroke_counter_t *this, FILE *out, char *name)
 static void print_all(private_stroke_counter_t *this, FILE *out)
 {
        enumerator_t *enumerator;
-       entry_t *entry;
-       linked_list_t *list;
        char *name;
 
-       list = linked_list_create();
-
-       this->lock->lock(this->lock);
-       enumerator = this->conns->create_enumerator(this->conns);
-       while (enumerator->enumerate(enumerator, &name, &entry))
-       {
-               list->insert_last(list, strdup(name));
-       }
-       enumerator->destroy(enumerator);
-       this->lock->unlock(this->lock);
-
-       enumerator = list->create_enumerator(list);
+       enumerator = this->query->get_names(this->query);
        while (enumerator->enumerate(enumerator, &name))
        {
                print_one(this, out, name);
        }
        enumerator->destroy(enumerator);
-
-       list->destroy_function(list, free);
-}
-
-/**
- * Print global counters
- */
-static void print_global(private_stroke_counter_t *this, FILE *out)
-{
-       uint64_t counter[COUNTER_MAX];
-       int i;
-
-       this->lock->lock(this->lock);
-       for (i = 0; i < countof(this->counter); i++)
-       {
-               counter[i] = this->counter[i];
-       }
-       this->lock->unlock(this->lock);
-
-       fprintf(out, "\nList of IKE counters:\n\n");
-
-       for (i = 0; i < countof(this->counter); i++)
-       {
-               print_counter(out, i, counter[i]);
-       }
 }
 
 METHOD(stroke_counter_t, print, void,
        private_stroke_counter_t *this, FILE *out, char *name)
 {
-       if (name)
+       if (!ensure_query(this))
+       {
+               fprintf(out, "\nNo counters available (plugin missing?)\n\n");
+               return;
+       }
+       if (name && streq(name, "all"))
        {
-               if (streq(name, "all"))
-               {
-                       return print_all(this, out);
-               }
-               return print_one(this, out, name);
+               return print_all(this, out);
        }
-       return print_global(this, out);
+       return print_one(this, out, name);
 }
 
 METHOD(stroke_counter_t, reset, void,
        private_stroke_counter_t *this, char *name)
 {
-       this->lock->lock(this->lock);
-       if (name)
-       {
-               entry_t *entry;
-
-               entry = this->conns->remove(this->conns, name);
-               if (entry)
-               {
-                       destroy_entry(entry);
-               }
-       }
-       else
+       if (!ensure_query(this))
        {
-               memset(&this->counter, 0, sizeof(this->counter));
+               return;
        }
-       this->lock->unlock(this->lock);
+       this->query->reset(this->query, name);
 }
 
 METHOD(stroke_counter_t, destroy, void,
        private_stroke_counter_t *this)
 {
-       enumerator_t *enumerator;
-       char *name;
-       entry_t *entry;
-
-       enumerator = this->conns->create_enumerator(this->conns);
-       while (enumerator->enumerate(enumerator, &name, &entry))
-       {
-               destroy_entry(entry);
-       }
-       enumerator->destroy(enumerator);
-       this->conns->destroy(this->conns);
-       this->lock->destroy(this->lock);
        free(this);
 }
 
@@ -445,19 +163,10 @@ stroke_counter_t *stroke_counter_create()
 
        INIT(this,
                .public = {
-                       .listener = {
-                               .alert = _alert,
-                               .ike_rekey = _ike_rekey,
-                               .child_rekey = _child_rekey,
-                               .message = _message_hook,
-                       },
                        .print = _print,
                        .reset = _reset,
                        .destroy = _destroy,
                },
-               .conns = hashtable_create((hashtable_hash_t)hash,
-                                                                 (hashtable_equals_t)equals, 4),
-               .lock = spinlock_create(),
        );
 
        return &this->public;
index fecf39f..2ff431c 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2017 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
  * Copyright (C) 2012 Martin Willi
  * Copyright (C) 2012 revosec AG
  *
 #ifndef STROKE_COUNTER_H_
 #define STROKE_COUNTER_H_
 
-#include <bus/listeners/listener.h>
+#include <library.h>
 
 typedef struct stroke_counter_t stroke_counter_t;
-typedef enum stroke_counter_type_t stroke_counter_type_t;
-
-enum stroke_counter_type_t {
-       /** initiated IKE_SA rekeyings */
-       COUNTER_INIT_IKE_SA_REKEY,
-       /** responded IKE_SA rekeyings */
-       COUNTER_RESP_IKE_SA_REKEY,
-       /** completed CHILD_SA rekeyings */
-       COUNTER_CHILD_SA_REKEY,
-       /** messages with invalid types, length, or a value out of range */
-       COUNTER_IN_INVALID,
-       /** messages with an invalid IKE SPI */
-       COUNTER_IN_INVALID_IKE_SPI,
-       /** received IKE_SA_INIT requests */
-       COUNTER_IN_IKE_SA_INIT_REQ,
-       /** received IKE_SA_INIT responses */
-       COUNTER_IN_IKE_SA_INIT_RSP,
-       /** sent IKE_SA_INIT requests */
-       COUNTER_OUT_IKE_SA_INIT_REQ,
-       /** sent IKE_SA_INIT responses */
-       COUNTER_OUT_IKE_SA_INIT_RES,
-       /** received IKE_AUTH requests */
-       COUNTER_IN_IKE_AUTH_REQ,
-       /** received IKE_AUTH responses */
-       COUNTER_IN_IKE_AUTH_RSP,
-       /** sent IKE_AUTH requests */
-       COUNTER_OUT_IKE_AUTH_REQ,
-       /** sent IKE_AUTH responses */
-       COUNTER_OUT_IKE_AUTH_RSP,
-       /** received CREATE_CHILD_SA requests */
-       COUNTER_IN_CREATE_CHILD_SA_REQ,
-       /** received CREATE_CHILD_SA responses */
-       COUNTER_IN_CREATE_CHILD_SA_RSP,
-       /** sent CREATE_CHILD_SA requests */
-       COUNTER_OUT_CREATE_CHILD_SA_REQ,
-       /** sent CREATE_CHILD_SA responses */
-       COUNTER_OUT_CREATE_CHILD_SA_RSP,
-       /** received INFORMATIONAL requests */
-       COUNTER_IN_INFORMATIONAL_REQ,
-       /** received INFORMATIONAL responses */
-       COUNTER_IN_INFORMATIONAL_RSP,
-       /** sent INFORMATIONAL requests */
-       COUNTER_OUT_INFORMATIONAL_REQ,
-       /** sent INFORMATIONAL responses */
-       COUNTER_OUT_INFORMATIONAL_RSP,
-       /** number of counter types */
-       COUNTER_MAX
-};
 
 /**
- * Collection of counter values for different IKE events.
+ * Interface for counter values for different IKE events.
  */
 struct stroke_counter_t {
 
        /**
-        * Implements listener_t.
-        */
-       listener_t listener;
-
-       /**
         * Print counter values to an output stream.
         *
         * @param out           output stream to write to
index 62095e3..0a34fb4 100644 (file)
@@ -66,6 +66,7 @@ METHOD(plugin_t, get_features, int,
        static plugin_feature_t f[] = {
                PLUGIN_CALLBACK((plugin_feature_callback_t)register_stroke, NULL),
                        PLUGIN_PROVIDE(CUSTOM, "stroke"),
+                               PLUGIN_SDEPEND(CUSTOM, "counters"),
                                PLUGIN_SDEPEND(PRIVKEY, KEY_RSA),
                                PLUGIN_SDEPEND(PRIVKEY, KEY_ECDSA),
                                PLUGIN_SDEPEND(PRIVKEY, KEY_DSA),
index 65d345d..c568440 100644 (file)
@@ -744,7 +744,6 @@ METHOD(stroke_socket_t, destroy, void,
                                                                                &this->attribute->provider);
        charon->attributes->remove_handler(charon->attributes,
                                                                           &this->handler->handler);
-       charon->bus->remove_listener(charon->bus, &this->counter->listener);
        this->cred->destroy(this->cred);
        this->ca->destroy(this->ca);
        this->config->destroy(this->config);
@@ -789,7 +788,7 @@ stroke_socket_t *stroke_socket_create()
                                                                         &this->attribute->provider);
        charon->attributes->add_handler(charon->attributes,
                                                                        &this->handler->handler);
-       charon->bus->add_listener(charon->bus, &this->counter->listener);
+
 
        max_concurrent = lib->settings->get_int(lib->settings,
                                "%s.plugins.stroke.max_concurrent", MAX_CONCURRENT_DEFAULT,