From: Tobias Brunner Date: Wed, 18 Feb 2015 11:48:28 +0000 (+0100) Subject: connect: Add simple plugin to initiate/terminate existing connections X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Fconnect-plugin;p=thirdparty%2Fstrongswan.git connect: Add simple plugin to initiate/terminate existing connections --- diff --git a/configure.ac b/configure.ac index f9b235093c..87f6fa3051 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # -# Copyright (C) 2007-2014 Tobias Brunner +# Copyright (C) 2007-2015 Tobias Brunner # Copyright (C) 2006-2014 Andreas Steffen # Copyright (C) 2006-2014 Martin Willi # Hochschule fuer Technik Rapperswil @@ -212,6 +212,7 @@ ARG_DISBL_SET([socket-default], [disable default socket implementation for charo ARG_ENABL_SET([socket-dynamic], [enable dynamic socket implementation for charon]) ARG_ENABL_SET([socket-win], [enable Winsock2 based socket implementation for charon]) # configuration/control plugins +ARG_ENABL_SET([connect], [simple plugin that allows to initiate/terminate existing connections.]) ARG_DISBL_SET([stroke], [disable charons stroke configuration backend.]) ARG_ENABL_SET([smp], [enable SMP configuration and control interface. Requires libxml.]) ARG_ENABL_SET([sql], [enable SQL database configuration backend.]) @@ -1270,6 +1271,7 @@ ADD_PLUGIN([socket-default], [c charon nm cmd]) ADD_PLUGIN([socket-dynamic], [c charon cmd]) ADD_PLUGIN([socket-win], [c charon]) ADD_PLUGIN([farp], [c charon]) +ADD_PLUGIN([connect], [c charon]) ADD_PLUGIN([stroke], [c charon]) ADD_PLUGIN([vici], [c charon]) ADD_PLUGIN([smp], [c charon]) @@ -1404,6 +1406,7 @@ AM_CONDITIONAL(USE_BLISS, test x$bliss = xtrue) # charon plugins # ---------------- +AM_CONDITIONAL(USE_CONNECT, test x$connect = xtrue) AM_CONDITIONAL(USE_STROKE, test x$stroke = xtrue) AM_CONDITIONAL(USE_VICI, test x$vici = xtrue) AM_CONDITIONAL(USE_MEDSRV, test x$medsrv = xtrue) @@ -1743,6 +1746,7 @@ AC_CONFIG_FILES([ src/libcharon/plugins/android_dns/Makefile src/libcharon/plugins/android_log/Makefile src/libcharon/plugins/maemo/Makefile + src/libcharon/plugins/connect/Makefile src/libcharon/plugins/stroke/Makefile src/libcharon/plugins/vici/Makefile src/libcharon/plugins/vici/ruby/Makefile diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index e98f5e1376..14b879f782 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -210,6 +210,13 @@ if MONOLITHIC endif endif +if USE_CONNECT + SUBDIRS += plugins/connect +if MONOLITHIC + libcharon_la_LIBADD += plugins/connect/libstrongswan-connect.la +endif +endif + if USE_STROKE SUBDIRS += plugins/stroke if MONOLITHIC diff --git a/src/libcharon/plugins/connect/.gitignore b/src/libcharon/plugins/connect/.gitignore new file mode 100644 index 0000000000..1bd5775c42 --- /dev/null +++ b/src/libcharon/plugins/connect/.gitignore @@ -0,0 +1 @@ +connect diff --git a/src/libcharon/plugins/connect/Makefile.am b/src/libcharon/plugins/connect/Makefile.am new file mode 100644 index 0000000000..b597263e06 --- /dev/null +++ b/src/libcharon/plugins/connect/Makefile.am @@ -0,0 +1,23 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon \ + -DIPSEC_PIDDIR=\"${piddir}\" + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-connect.la +else +plugin_LTLIBRARIES = libstrongswan-connect.la +endif + +libstrongswan_connect_la_SOURCES = connect_plugin.h connect_plugin.c \ + connect_socket.h connect_socket.c connect_msg.h + +libstrongswan_connect_la_LDFLAGS = -module -avoid-version + +ipsec_PROGRAMS = connect +connect_SOURCES = connect.c +connect_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \ No newline at end of file diff --git a/src/libcharon/plugins/connect/connect.c b/src/libcharon/plugins/connect/connect.c new file mode 100644 index 0000000000..a7719c5d48 --- /dev/null +++ b/src/libcharon/plugins/connect/connect.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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 "connect_msg.h" + +#include + +#include + +/** + * Send a message + */ +static int send_msg(int type, char *name) +{ + connect_msg_t msg = { + .type = htonl(type), + }; + stream_t *stream; + char *uri, buffer[512]; + int count; + + uri = lib->settings->get_str(lib->settings, "charon.plugins.connect.socket", + "unix://" CONNECT_SOCKET); + stream = lib->streams->connect(lib->streams, uri); + if (!stream) + { + fprintf(stderr, "failed to connect to socket '%s'\n", uri); + return 2; + } + + snprintf(msg.name, sizeof(msg.name), "%s", name); + if (!stream->write_all(stream, &msg, sizeof(msg))) + { + fprintf(stderr, "sending message failed\n"); + stream->destroy(stream); + return 2; + } + + while ((count = stream->read(stream, buffer, sizeof(buffer)-1, TRUE)) > 0) + { + buffer[count] = '\0'; + printf("%s", buffer); + } + if (count < 0) + { + fprintf(stderr, "reading response failed\n"); + } + stream->destroy(stream); + return 0; +} + +int main(int argc, char *argv[]) +{ + library_init(NULL, "connect"); + atexit(library_deinit); + + if (argc == 3 && strcmp(argv[1], "up") == 0) + { + return send_msg(CONNECT_INITIATE, argv[2]); + } + if (argc == 3 && strcmp(argv[1], "down") == 0) + { + return send_msg(CONNECT_TERMINATE, argv[2]); + } + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s up \n", argv[0]); + fprintf(stderr, " %s down \n", argv[0]); + return 1; +} diff --git a/src/libcharon/plugins/connect/connect_msg.h b/src/libcharon/plugins/connect/connect_msg.h new file mode 100644 index 0000000000..0bd8d89226 --- /dev/null +++ b/src/libcharon/plugins/connect/connect_msg.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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 connect_msg connect_msg + * @{ @ingroup connect + */ + +#ifndef CONNECT_MSG_H_ +#define CONNECT_MSG_H_ + +#define CONNECT_SOCKET IPSEC_PIDDIR "/charon.cnct" + +typedef struct connect_msg_t connect_msg_t; + +/** + * Message type + */ +enum { + /* initiate a connection */ + CONNECT_INITIATE = 1, + /* terminate a connection */ + CONNECT_TERMINATE = 2, +}; + +/** + * Message to exchange over socket + */ +struct connect_msg_t { + /** message type */ + int type; + /** null terminated connection name */ + char name[128]; +} __attribute__((packed)); + +#endif /** CONNECT_MSG_H_ @}*/ diff --git a/src/libcharon/plugins/connect/connect_plugin.c b/src/libcharon/plugins/connect/connect_plugin.c new file mode 100644 index 0000000000..2c21d84526 --- /dev/null +++ b/src/libcharon/plugins/connect/connect_plugin.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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 "connect_plugin.h" + +#include "connect_socket.h" + +#include + +typedef struct private_connect_plugin_t private_connect_plugin_t; + +/** + * Private data of connect plugin + */ +struct private_connect_plugin_t { + + /** + * Public interface + */ + connect_plugin_t public; + + /** + * Control socket + */ + connect_socket_t *socket; +}; + +METHOD(plugin_t, get_name, char*, + private_connect_plugin_t *this) +{ + return "connect"; +} + +/** + * Register listener + */ +static bool plugin_cb(private_connect_plugin_t *this, + plugin_feature_t *feature, bool reg, void *cb_data) +{ + if (reg) + { + this->socket = connect_socket_create(); + return this->socket != NULL; + } + else + { + DESTROY_IF(this->socket); + return TRUE; + } +} + +METHOD(plugin_t, get_features, int, + private_connect_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL), + PLUGIN_PROVIDE(CUSTOM, "connect"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_connect_plugin_t *this) +{ + free(this); +} + +/** + * Plugin constructor + */ +plugin_t *connect_plugin_create() +{ + private_connect_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/connect/connect_plugin.h b/src/libcharon/plugins/connect/connect_plugin.h new file mode 100644 index 0000000000..442ec93f78 --- /dev/null +++ b/src/libcharon/plugins/connect/connect_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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 connect connect + * @ingroup cplugins + * + * @defgroup connect_plugin connect_plugin + * @{ @ingroup connect + */ + +#ifndef CONNECT_PLUGIN_H_ +#define CONNECT_PLUGIN_H_ + +#include + +typedef struct connect_plugin_t connect_plugin_t; + +/** + * Simple plugin to initiate/termiante connections. + */ +struct connect_plugin_t { + + /** + * Implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** CONNECT_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/connect/connect_socket.c b/src/libcharon/plugins/connect/connect_socket.c new file mode 100644 index 0000000000..1c11c50ac5 --- /dev/null +++ b/src/libcharon/plugins/connect/connect_socket.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013-2015 Tobias Brunner + * 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 . + * + * 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 "connect_socket.h" + +#include +#include + +#include "connect_msg.h" + +#define CONNECT_LOG_LEVEL 1 + +typedef struct private_connect_socket_t private_connect_socket_t; + +/** + * Private data of a connect_socket_t object + */ +struct private_connect_socket_t { + + /** + * Public interface + */ + connect_socket_t public; + + /** + * Connect stream service + */ + stream_service_t *service; +}; + +/** + * Logging to the connect socket + */ +static bool connect_log(FILE *out, debug_t group, level_t level, + ike_sa_t *ike_sa, char *message) +{ + if (level <= CONNECT_LOG_LEVEL) + { + if (fprintf(out, "%s", message) < 0 || + fprintf(out, "\n") < 0 || + fflush(out) != 0) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Get the child_cfg with the same name as the peer cfg + */ +static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name) +{ + child_cfg_t *current, *found = NULL; + enumerator_t *enumerator; + + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * Initiate the connection with the given config + */ +static void perform_initiate(private_connect_socket_t *this, char *name, + peer_cfg_t *peer_cfg, child_cfg_t *child_cfg, FILE *out) +{ + status_t status; + + status = charon->controller->initiate(charon->controller, peer_cfg, + child_cfg, (controller_cb_t)connect_log, out, 0); + switch (status) + { + case SUCCESS: + fprintf(out, "connection '%s' established successfully\n", name); + break; + default: + case FAILED: + fprintf(out, "establishing connection '%s' failed\n", name); + break; + } +} + +/** + * Initiate the connection with the given name + */ +static void initiate(private_connect_socket_t *this, char *name, FILE *out) +{ + child_cfg_t *child_cfg = NULL; + peer_cfg_t *peer_cfg; + enumerator_t *enumerator; + bool empty = TRUE; + + DBG1(DBG_CFG, "connect: initiate '%s'", name); + + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name); + if (peer_cfg) + { + child_cfg = get_child_from_peer(peer_cfg, name); + if (child_cfg == NULL) + { + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, &child_cfg)) + { + empty = FALSE; + perform_initiate(this, name, peer_cfg->get_ref(peer_cfg), + child_cfg->get_ref(child_cfg), out); + } + enumerator->destroy(enumerator); + + if (empty) + { + DBG1(DBG_CFG, "no child config named '%s'", name); + fprintf(out, "no child config named '%s'\n", name); + } + peer_cfg->destroy(peer_cfg); + return; + } + } + else + { + enumerator = charon->backends->create_peer_cfg_enumerator( + charon->backends, NULL, NULL, NULL, NULL, IKE_ANY); + while (enumerator->enumerate(enumerator, &peer_cfg)) + { + child_cfg = get_child_from_peer(peer_cfg, name); + if (child_cfg) + { + peer_cfg->get_ref(peer_cfg); + break; + } + } + enumerator->destroy(enumerator); + + if (child_cfg == NULL) + { + DBG1(DBG_CFG, "no config named '%s'", name); + fprintf(out, "no config named '%s'\n", name); + return; + } + } + perform_initiate(this, name, peer_cfg, child_cfg, out); +} + +/** + * Terminate the connection with the given name + */ +static void terminate(private_connect_socket_t *this, char *name, FILE *out) +{ + ike_sa_t *ike_sa; + enumerator_t *enumerator; + array_t *ike_sas; + status_t status; + u_int32_t id, *current; + + DBG1(DBG_CFG, "connect: terminate '%s'", name); + + ike_sas = array_create(sizeof(u_int32_t), 1); + enumerator = charon->controller->create_ike_sa_enumerator( + charon->controller, TRUE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (streq(name, ike_sa->get_name(ike_sa))) + { + id = ike_sa->get_unique_id(ike_sa); + array_insert(ike_sas, ARRAY_TAIL, &id); + } + } + enumerator->destroy(enumerator); + + enumerator = array_create_enumerator(ike_sas); + while (enumerator->enumerate(enumerator, ¤t)) + { + status = charon->controller->terminate_ike(charon->controller, + *current, (controller_cb_t)connect_log, out, 0); + switch (status) + { + case SUCCESS: + fprintf(out, "IKE_SA[%d] closed successfully\n", *current); + break; + default: + case FAILED: + fprintf(out, "closing IKE_SA[%d] failed\n", *current); + break; + } + } + enumerator->destroy(enumerator); + + if (!array_count(ike_sas)) + { + DBG1(DBG_CFG, "no IKE_SA named '%s'", name); + fprintf(out, "no IKE_SA named '%s'\n", name); + } + array_destroy(ike_sas); +} + +/** + * Dispatch a received message + */ +static bool on_accept(private_connect_socket_t *this, stream_t *stream) +{ + connect_msg_t msg; + FILE *out; + + if (stream->read_all(stream, &msg, sizeof(msg))) + { + msg.name[sizeof(msg.name) - 1] = '\0'; + out = stream->get_file(stream); + if (!out) + { + DBG1(DBG_CFG, "creating connect output stream failed"); + return FALSE; + } + switch (ntohl(msg.type)) + { + case CONNECT_INITIATE: + initiate(this, msg.name, out); + break; + case CONNECT_TERMINATE: + terminate(this, msg.name, out); + break; + default: + DBG1(DBG_CFG, "received unknown connect command"); + break; + } + fclose(out); + } + return FALSE; +} + +METHOD(connect_socket_t, destroy, void, + private_connect_socket_t *this) +{ + this->service->destroy(this->service); + free(this); +} + +/** + * See header + */ +connect_socket_t *connect_socket_create() +{ + private_connect_socket_t *this; + char *uri; + + INIT(this, + .public = { + .destroy = _destroy, + }, + ); + + uri = lib->settings->get_str(lib->settings, + "%s.plugins.connect.socket", "unix://" CONNECT_SOCKET, + lib->ns); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (!this->service) + { + DBG1(DBG_CFG, "creating connect socket failed"); + free(this); + return NULL; + } + + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 0); + + return &this->public; +} diff --git a/src/libcharon/plugins/connect/connect_socket.h b/src/libcharon/plugins/connect/connect_socket.h new file mode 100644 index 0000000000..afaa710351 --- /dev/null +++ b/src/libcharon/plugins/connect/connect_socket.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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 connect_socket connect_socket + * @{ @ingroup connect + */ + +#ifndef CONNECT_SOCKET_H_ +#define CONNECT_SOCKET_H_ + +typedef struct connect_socket_t connect_socket_t; + +/** + * Control socket + */ +struct connect_socket_t { + + /** + * Destroy a connect_socket_t + */ + void (*destroy)(connect_socket_t *this); +}; + +/** + * Create a connect_socket instance + */ +connect_socket_t *connect_socket_create(); + +#endif /** CONNECT_SOCKET_H_ @}*/