diff --git a/configure.ac b/configure.ac index 2d9ab05c8..1670b01ac 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index ed2236e04..964a19ec8 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -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 index 000000000..e5bde3f7a --- /dev/null +++ b/src/libcharon/plugins/counters/Makefile.am @@ -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 index 000000000..a32614e8c --- /dev/null +++ b/src/libcharon/plugins/counters/counters_listener.c @@ -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 . + * + * 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 +#include +#include + +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 index 000000000..6d9168f4c --- /dev/null +++ b/src/libcharon/plugins/counters/counters_listener.h @@ -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 . + * + * 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 + +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 index 000000000..48de92760 --- /dev/null +++ b/src/libcharon/plugins/counters/counters_plugin.c @@ -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 . + * + * 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 +#include + +#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 index 000000000..76fe2c236 --- /dev/null +++ b/src/libcharon/plugins/counters/counters_plugin.h @@ -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 . + * + * 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 + +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 index 000000000..f785a68c8 --- /dev/null +++ b/src/libcharon/plugins/counters/counters_query.h @@ -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 . + * + * 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 + +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_ @}*/ diff --git a/src/libcharon/plugins/stroke/Makefile.am b/src/libcharon/plugins/stroke/Makefile.am index 26edc3dcd..9456dd908 100644 --- a/src/libcharon/plugins/stroke/Makefile.am +++ b/src/libcharon/plugins/stroke/Makefile.am @@ -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}\" \ diff --git a/src/libcharon/plugins/stroke/stroke_counter.c b/src/libcharon/plugins/stroke/stroke_counter.c index e93fd4ef2..8eb9968e4 100644 --- a/src/libcharon/plugins/stroke/stroke_counter.c +++ b/src/libcharon/plugins/stroke/stroke_counter.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG * @@ -13,10 +16,11 @@ * for more details. */ +#include + #include "stroke_counter.h" -#include -#include +#include 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 + * Reference to query interface */ - uint64_t counter[COUNTER_MAX]; - - /** - * Counters for specific connection names, char* => entry_t - */ - hashtable_t *conns; - - /** - * Lock for counter values - */ - spinlock_t *lock; + counters_query_t *query; }; /** - * Counters for a specific connection name + * Make sure we have the query interface */ -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) +static inline bool ensure_query(private_stroke_counter_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) + if (this->query) { - 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_stroke_counter_t *this, - ike_sa_t *ike_sa, stroke_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->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)) { - if (streq(name, "all")) - { - return print_all(this, out); - } - return print_one(this, out, name); + fprintf(out, "\nNo counters available (plugin missing?)\n\n"); + return; } - return print_global(this, out); + if (name && streq(name, "all")) + { + return print_all(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) + if (!ensure_query(this)) { - entry_t *entry; - - entry = this->conns->remove(this->conns, name); - if (entry) - { - destroy_entry(entry); - } + return; } - else - { - memset(&this->counter, 0, sizeof(this->counter)); - } - 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; diff --git a/src/libcharon/plugins/stroke/stroke_counter.h b/src/libcharon/plugins/stroke/stroke_counter.h index fecf39f56..2ff431c88 100644 --- a/src/libcharon/plugins/stroke/stroke_counter.h +++ b/src/libcharon/plugins/stroke/stroke_counter.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG * @@ -21,68 +24,15 @@ #ifndef STROKE_COUNTER_H_ #define STROKE_COUNTER_H_ -#include +#include 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. * diff --git a/src/libcharon/plugins/stroke/stroke_plugin.c b/src/libcharon/plugins/stroke/stroke_plugin.c index 62095e368..0a34fb458 100644 --- a/src/libcharon/plugins/stroke/stroke_plugin.c +++ b/src/libcharon/plugins/stroke/stroke_plugin.c @@ -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), diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 65d345db3..c568440b7 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -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,