<p>Max <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12763">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  Harald Welte: Looks good to me, approved

Objections:
  Pau Espin Pedrol: I would prefer this is not merged as is

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add OpenVPN probe<br><br>This adds support for OpenVPN status probe which uses OpenVPN's<br>management interface (configured via 'management 127.0.0.1 1234' in<br>OpenVPN's config).<br><br>The output looks as follows:<br>...<br>  OpenVPN<br>    127.0.0.1:1234<br>      status: CONNECTED<br>      tunnel: 10.8.0.15<br>      remote: 144.76.43.77:1194<br>    localhost:4242<br>      status: management interface incompatible<br>    127.0.0.1:4444<br>      status: management interface unavailable<br>...<br><br>We show tunnel's IP (if available) as well as remote (OpenVPN server<br>itself) address/port in addition to general connection status. If<br>management interface is unavailable it's reported as such. If we've<br>managed to establish connection with a given management interface but<br>are unable to obtain expected information than we report this<br>incompatibility as well.<br><br>Related: SYS#2655<br>Change-Id: I4493e19b9a09dcebd289457eacd1719f7f8cc31c<br>---<br>M configure.ac<br>M contrib/jenkins.sh<br>M src/Makefile.am<br>M src/client.c<br>M src/client.h<br>M src/osysmon.h<br>M src/osysmon_main.c<br>A src/osysmon_openvpn.c<br>8 files changed, 342 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configure.ac b/configure.ac</span><br><span>index d98de89..44a040c 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -42,6 +42,7 @@</span><br><span> PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)</span><br><span> PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)</span><br><span> PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)</span><br><span style="color: hsl(120, 100%, 40%);">+PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)</span><br><span> PKG_CHECK_MODULES(LIBMNL, libmnl)</span><br><span> dnl FIXME: bump to 1.10.0 once it's available on build slaves and remove workaround from osysmon_ping.c</span><br><span> PKG_CHECK_MODULES(LIBOPING, liboping >= 1.9.0)</span><br><span>diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh</span><br><span>index b1529c4..631f95a 100755</span><br><span>--- a/contrib/jenkins.sh</span><br><span>+++ b/contrib/jenkins.sh</span><br><span>@@ -25,6 +25,9 @@</span><br><span> export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"</span><br><span> export LD_LIBRARY_PATH="$inst/lib"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+osmo-build-dep.sh libosmo-abis</span><br><span style="color: hsl(120, 100%, 40%);">+osmo-build-dep.sh libosmo-netif "" '--disable-doxygen'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> set +x</span><br><span> echo</span><br><span> echo</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index f639023..f9b79f2 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -9,6 +9,7 @@</span><br><span>   -Wall \</span><br><span>      $(LIBOSMOCORE_CFLAGS) \</span><br><span>      $(LIBOSMOGSM_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(LIBOSMONETIF_CFLAGS) \</span><br><span>     $(NULL)</span><br><span> </span><br><span> AM_LDFLAGS = \</span><br><span>@@ -22,12 +23,13 @@</span><br><span> </span><br><span> noinst_LTLIBRARIES = libintern.la</span><br><span> libintern_la_SOURCES = simple_ctrl.c client.c</span><br><span style="color: hsl(0, 100%, 40%);">-libintern_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)</span><br><span style="color: hsl(120, 100%, 40%);">+libintern_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMONETIF_LIBS)</span><br><span> </span><br><span> osmo_sysmon_CFLAGS = $(LIBMNL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOPING_CFLAGS) $(AM_CFLAGS)</span><br><span> </span><br><span> osmo_sysmon_LDADD = $(LDADD) \</span><br><span>         $(LIBOSMOVTY_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMONETIF_LIBS) \</span><br><span>       $(LIBMNL_LIBS) \</span><br><span>     $(LIBOPING_LIBS) \</span><br><span>   $(NULL)</span><br><span>@@ -39,6 +41,7 @@</span><br><span>  osysmon_rtnl.c \</span><br><span>     osysmon_file.c \</span><br><span>     osysmon_ping.c \</span><br><span style="color: hsl(120, 100%, 40%);">+      osysmon_openvpn.c \</span><br><span>  osysmon_main.c \</span><br><span>     $(NULL)</span><br><span> </span><br><span>diff --git a/src/client.c b/src/client.c</span><br><span>index 6b37fc6..758884d 100644</span><br><span>--- a/src/client.c</span><br><span>+++ b/src/client.c</span><br><span>@@ -27,6 +27,7 @@</span><br><span> #include <talloc.h></span><br><span> </span><br><span> #include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/netif/stream.h></span><br><span> </span><br><span> #include "client.h"</span><br><span> </span><br><span>@@ -71,3 +72,24 @@</span><br><span> </span><br><span>    return talloc_asprintf(ctx, "%s:%u", cfg->remote_host, cfg->remote_port);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_stream_cli *make_tcp_client(struct host_cfg *cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_stream_cli *cl = osmo_stream_cli_create(cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cl) {</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_stream_cli_set_addr(cl, cfg->remote_host);</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_stream_cli_set_port(cl, cfg->remote_port);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return cl;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void update_name(struct host_cfg *cfg, const char *new_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_talloc_replace_string(cfg, (char **)&cfg->name, new_name);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void update_host(struct host_cfg *cfg, const char *new_host)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_talloc_replace_string(cfg, (char **)&cfg->remote_host, new_host);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/client.h b/src/client.h</span><br><span>index 605ddd7..d878450 100644</span><br><span>--- a/src/client.h</span><br><span>+++ b/src/client.h</span><br><span>@@ -23,3 +23,8 @@</span><br><span> struct host_cfg *host_cfg_alloc(void *ctx, const char *name, const char *host, uint16_t port);</span><br><span> bool match_config(const struct host_cfg *cfg, const char *match, enum match_kind k);</span><br><span> char *make_authority(void *ctx, const struct host_cfg *cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_stream_cli *make_tcp_client(struct host_cfg *cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void update_name(struct host_cfg *cfg, const char *new_name);</span><br><span style="color: hsl(120, 100%, 40%);">+void update_host(struct host_cfg *cfg, const char *new_host);</span><br><span>diff --git a/src/osysmon.h b/src/osysmon.h</span><br><span>index df8bf8d..2f82c47 100644</span><br><span>--- a/src/osysmon.h</span><br><span>+++ b/src/osysmon.h</span><br><span>@@ -15,6 +15,8 @@</span><br><span>   struct rtnl_client_state *rcs;</span><br><span>       /* list of 'struct ctrl client' */</span><br><span>   struct llist_head ctrl_clients;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* list of 'struct openvpn_client' */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct llist_head openvpn_clients;</span><br><span>   /* list of 'struct netdev' */</span><br><span>        struct llist_head netdevs;</span><br><span>   /* list of 'struct osysmon_file' */</span><br><span>@@ -30,6 +32,7 @@</span><br><span>      CTRL_CLIENT_NODE = _LAST_OSMOVTY_NODE + 1,</span><br><span>   CTRL_CLIENT_GETVAR_NODE,</span><br><span>     NETDEV_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+  OPENVPN_NODE,</span><br><span>        PING_NODE,</span><br><span> };</span><br><span> </span><br><span>@@ -48,5 +51,8 @@</span><br><span> int osysmon_ping_init();</span><br><span> int osysmon_ping_poll(struct value_node *parent);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int osysmon_openvpn_init();</span><br><span style="color: hsl(120, 100%, 40%);">+int osysmon_openvpn_poll(struct value_node *parent);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int osysmon_file_init();</span><br><span> int osysmon_file_poll(struct value_node *parent);</span><br><span>diff --git a/src/osysmon_main.c b/src/osysmon_main.c</span><br><span>index 91d5039..18f6299 100644</span><br><span>--- a/src/osysmon_main.c</span><br><span>+++ b/src/osysmon_main.c</span><br><span>@@ -199,6 +199,7 @@</span><br><span> </span><br><span>   g_oss = talloc_zero(NULL, struct osysmon_state);</span><br><span>     INIT_LLIST_HEAD(&g_oss->ctrl_clients);</span><br><span style="color: hsl(120, 100%, 40%);">+ INIT_LLIST_HEAD(&g_oss->openvpn_clients);</span><br><span>     INIT_LLIST_HEAD(&g_oss->netdevs);</span><br><span>     INIT_LLIST_HEAD(&g_oss->files);</span><br><span> </span><br><span>@@ -206,6 +207,7 @@</span><br><span>     handle_options(argc, argv);</span><br><span>  osysmon_sysinfo_init();</span><br><span>      osysmon_ctrl_init();</span><br><span style="color: hsl(120, 100%, 40%);">+  osysmon_openvpn_init();</span><br><span>      osysmon_rtnl_init();</span><br><span>         ping_init = osysmon_ping_init();</span><br><span>     osysmon_file_init();</span><br><span>@@ -231,6 +233,7 @@</span><br><span> </span><br><span>       while (1) {</span><br><span>          struct value_node *root = value_node_add(NULL, "root", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+               int vpns = osysmon_openvpn_poll(root);</span><br><span>               osysmon_sysinfo_poll(root);</span><br><span>          osysmon_ctrl_poll(root);</span><br><span>             osysmon_rtnl_poll(root);</span><br><span>@@ -242,6 +245,10 @@</span><br><span> </span><br><span>          display_update(root);</span><br><span>                value_node_del(root);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               if (vpns)</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_select_main(0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>               sleep(1);</span><br><span>    }</span><br><span> </span><br><span>diff --git a/src/osysmon_openvpn.c b/src/osysmon_openvpn.c</span><br><span>new file mode 100644</span><br><span>index 0000000..135a532</span><br><span>--- /dev/null</span><br><span>+++ b/src/osysmon_openvpn.c</span><br><span>@@ -0,0 +1,294 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Simple Osmocom System Monitor (osysmon): Support for OpenVPN monitoring */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2019 by sysmocom - s.f.m.c. GmbH.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Max Suraev</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ *  it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ *  the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ *  (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ *  GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ *  along with this program; if not, write to the Free Software</span><br><span style="color: hsl(120, 100%, 40%);">+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,</span><br><span style="color: hsl(120, 100%, 40%);">+ *  MA  02110-1301, USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdbool.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <ctype.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/netif/stream.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "osysmon.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "client.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "value_node.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/***********************************************************************</span><br><span style="color: hsl(120, 100%, 40%);">+ * Data model</span><br><span style="color: hsl(120, 100%, 40%);">+ ***********************************************************************/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define OVPN_LOG(ctx, vpn, fmt, args...)                           \</span><br><span style="color: hsl(120, 100%, 40%);">+     fprintf(stderr, "OpenVPN [%s]: " fmt, make_authority(ctx, vpn->cfg), ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* max number of csv in response */</span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_RESP_COMPONENTS 6</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* a single OpenVPN management interface client */</span><br><span style="color: hsl(120, 100%, 40%);">+struct openvpn_client {</span><br><span style="color: hsl(120, 100%, 40%);">+        /* links to osysmon.openvpn_clients */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct host_cfg *cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_stream_cli *mgmt;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* fields below are parsed from response to 'state' command on mgmt interface */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct host_cfg *rem_cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+     char *tun_ip;</span><br><span style="color: hsl(120, 100%, 40%);">+ bool connected;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *parse_state(struct msgb *msg, struct openvpn_client *vpn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      char tmp[128];</span><br><span style="color: hsl(120, 100%, 40%);">+        char *tok;</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int i = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t *m = msgb_data(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (msgb_length(msg) > 128)</span><br><span style="color: hsl(120, 100%, 40%);">+                OVPN_LOG(msg, vpn, "received message too long (%d > %u), truncating...\n", msgb_length(msg), 128);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (msgb_length(msg) > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!isdigit(m[0])) /* skip OpenVPN greetings and alike */</span><br><span style="color: hsl(120, 100%, 40%);">+                    return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              OVPN_LOG(msg, vpn, "received message is empty, ignoring...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_STRLCPY_ARRAY(tmp, (char *)m);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (tok = strtok(tmp, ","); tok && i < MAX_RESP_COMPONENTS; tok = strtok(NULL, ",")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                /* The string format is documented in https://openvpn.net/community-resources/management-interface/ */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (tok) { /* Parse csv string and pick interesting tokens while ignoring the rest. */</span><br><span style="color: hsl(120, 100%, 40%);">+                        switch (i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 1:</span><br><span style="color: hsl(120, 100%, 40%);">+                               update_name(vpn->rem_cfg, tok);</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 3:</span><br><span style="color: hsl(120, 100%, 40%);">+                               osmo_talloc_replace_string(vpn->rem_cfg, &vpn->tun_ip, tok);</span><br><span style="color: hsl(120, 100%, 40%);">+                                break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 4:</span><br><span style="color: hsl(120, 100%, 40%);">+                               update_host(vpn->rem_cfg, tok);</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case 5:</span><br><span style="color: hsl(120, 100%, 40%);">+                               vpn->rem_cfg->remote_port = atoi(tok);</span><br><span style="color: hsl(120, 100%, 40%);">+                          break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct openvpn_client *openvpn_client_find_or_make(const struct osysmon_state *os,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                    const char *host, uint16_t port)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct openvpn_client *vpn;</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry(vpn, &os->openvpn_clients, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (match_config(vpn->cfg, host, MATCH_HOST) && vpn->cfg->remote_port == port)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return vpn;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int connect_cb(struct osmo_stream_cli *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct openvpn_client *vpn = osmo_stream_cli_get_data(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        update_name(vpn->rem_cfg, "management interface incompatible");</span><br><span style="color: hsl(120, 100%, 40%);">+  vpn->connected = true; /* FIXME: there's no callback for lost connection to drop this flag */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int read_cb(struct osmo_stream_cli *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int bytes;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct openvpn_client *vpn = osmo_stream_cli_get_data(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+  struct msgb *msg = msgb_alloc(1024, "OpenVPN");</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           OVPN_LOG(conn, vpn, "unable to allocate message in callback\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   bytes = osmo_stream_cli_recv(conn, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (bytes < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+             OVPN_LOG(msg, vpn, "unable to receive message in callback\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      else</span><br><span style="color: hsl(120, 100%, 40%);">+          parse_state(msg, vpn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static bool openvpn_client_create(struct osysmon_state *os, const char *name, const char *host, uint16_t port)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct openvpn_client *vpn = openvpn_client_find_or_make(os, host, port);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (vpn)</span><br><span style="color: hsl(120, 100%, 40%);">+              return true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        vpn = talloc_zero(os, struct openvpn_client);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!vpn)</span><br><span style="color: hsl(120, 100%, 40%);">+             return false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       vpn->connected = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  vpn->cfg = host_cfg_alloc(vpn, name, host, port);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!vpn->cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+             goto dealloc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       vpn->rem_cfg = host_cfg_alloc(vpn, "management interface unavailable", NULL, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!vpn->rem_cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+         goto dealloc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       vpn->mgmt = make_tcp_client(vpn->cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!vpn->mgmt)      {</span><br><span style="color: hsl(120, 100%, 40%);">+             OVPN_LOG(vpn->rem_cfg, vpn, "failed to create TCP client\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            goto dealloc;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Wait for 1 minute before attempting to reconnect to management interface */</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_stream_cli_set_reconnect_timeout(vpn->mgmt, 60);</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_stream_cli_set_read_cb(vpn->mgmt, read_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_stream_cli_set_connect_cb(vpn->mgmt, connect_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (osmo_stream_cli_open(vpn->mgmt) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              OVPN_LOG(vpn->rem_cfg, vpn, "failed to connect to management interface\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto dealloc;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_stream_cli_set_data(vpn->mgmt, vpn);</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_add_tail(&vpn->list, &os->openvpn_clients);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+dealloc:</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_free(vpn);</span><br><span style="color: hsl(120, 100%, 40%);">+     return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void openvpn_client_destroy(struct openvpn_client *vpn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!vpn)</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_stream_cli_destroy(vpn->mgmt);</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_del(&vpn->list);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(vpn);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/***********************************************************************</span><br><span style="color: hsl(120, 100%, 40%);">+ * VTY</span><br><span style="color: hsl(120, 100%, 40%);">+ ***********************************************************************/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define OPENVPN_STR "Configure OpenVPN management interface address\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_openvpn, cfg_openvpn_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "openvpn HOST <1-65535>",</span><br><span style="color: hsl(120, 100%, 40%);">+      OPENVPN_STR "Name of the host to connect to\n" "Management interface port\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t port = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!openvpn_client_create(g_oss, "OpenVPN", argv[0], port)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              vty_out(vty, "Failed to create OpenVPN client for %s:%u%s", argv[0], port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+            return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_no_openvpn, cfg_no_openvpn_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "no openvpn HOST <1-65535>",</span><br><span style="color: hsl(120, 100%, 40%);">+      NO_STR OPENVPN_STR "Name of the host to connect to\n" "Management interface port\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint16_t port = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+        struct openvpn_client *vpn = openvpn_client_find_or_make(g_oss, argv[0], port);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!vpn) {</span><br><span style="color: hsl(120, 100%, 40%);">+           vty_out(vty, "OpenVPN client %s:%u doesn't exist%s", argv[0], port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+               return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   openvpn_client_destroy(vpn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/***********************************************************************</span><br><span style="color: hsl(120, 100%, 40%);">+ * Runtime Code</span><br><span style="color: hsl(120, 100%, 40%);">+ ***********************************************************************/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int openvpn_client_poll(struct openvpn_client *vpn, struct value_node *parent)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  char *remote = make_authority(parent, vpn->rem_cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+       struct value_node *vn_host = value_node_find_or_add(parent, make_authority(parent, vpn->cfg));</span><br><span style="color: hsl(120, 100%, 40%);">+     struct msgb *msg = msgb_alloc(128, "state");</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           value_node_add(vn_host, "msgb", "memory allocation failure");</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (vpn->rem_cfg->name)</span><br><span style="color: hsl(120, 100%, 40%);">+         value_node_add(vn_host, "status", vpn->rem_cfg->name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (vpn->tun_ip)</span><br><span style="color: hsl(120, 100%, 40%);">+           value_node_add(vn_host, "tunnel", vpn->tun_ip);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (remote)</span><br><span style="color: hsl(120, 100%, 40%);">+           value_node_add(vn_host, "remote", remote);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* FIXME: there's no way to check client state so this might be triggered even while it's reconnecting */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (vpn->connected) { /* re-trigger state command */</span><br><span style="color: hsl(120, 100%, 40%);">+               msgb_printf(msg, "state\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_stream_cli_send(vpn->mgmt, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* called once on startup before config file parsing */</span><br><span style="color: hsl(120, 100%, 40%);">+int osysmon_openvpn_init()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        install_element(CONFIG_NODE, &cfg_openvpn_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+   install_element(CONFIG_NODE, &cfg_no_openvpn_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* called periodically */</span><br><span style="color: hsl(120, 100%, 40%);">+int osysmon_openvpn_poll(struct value_node *parent)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int num_vpns = llist_count(&g_oss->openvpn_clients);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (num_vpns) {</span><br><span style="color: hsl(120, 100%, 40%);">+               struct value_node *vn_vpn = value_node_add(parent, "OpenVPN", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                struct openvpn_client *vpn;</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_for_each_entry(vpn, &g_oss->openvpn_clients, list)</span><br><span style="color: hsl(120, 100%, 40%);">+                       openvpn_client_poll(vpn, vn_vpn);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return num_vpns;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12763">change 12763</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/12763"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-sysmon </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I4493e19b9a09dcebd289457eacd1719f7f8cc31c </div>
<div style="display:none"> Gerrit-Change-Number: 12763 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: Max <msuraev@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Max <msuraev@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>