| /* Simple Osmocom System Monitor (osysmon): Support for ping probe */ |
| |
| /* (C) 2018 by sysmocom - s.f.m.c. GmbH. |
| * Author: Max Suraev |
| * All Rights Reserved. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| * |
| * 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. |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| * MA 02110-1301, USA. |
| */ |
| |
| #include <stdbool.h> |
| #include <errno.h> |
| |
| #include <oping.h> |
| |
| #include <osmocom/core/utils.h> |
| #include <osmocom/vty/vty.h> |
| #include <osmocom/vty/command.h> |
| |
| #include "osysmon.h" |
| #include "value_node.h" |
| |
| /*********************************************************************** |
| * Data model |
| ***********************************************************************/ |
| |
| #define BUFLEN 128 |
| |
| struct ping_state { |
| pingobj_t *ping_handle; |
| }; |
| |
| /* FIXME: replace with ping_iterator_count() once we bump requirements to liboping 1.10.0+ */ |
| static unsigned iterator_count(pingobj_t *p) |
| { |
| unsigned count = 0; |
| pingobj_iter_t *iter; |
| for (iter = ping_iterator_get(p); iter; iter = ping_iterator_next(iter)) |
| count++; |
| return count; |
| } |
| |
| /* Workaround for liboping issue https://github.com/octo/liboping/issues/43 */ |
| static int add_host(pingobj_t *pinger, const char *host) |
| { |
| int num_host = iterator_count(pinger), |
| rc = ping_host_add(pinger, host); |
| |
| if (rc < 0) { |
| if (num_host != iterator_count(pinger)) |
| ping_host_remove(pinger, host); |
| } |
| |
| return rc; |
| } |
| |
| |
| static bool add_drop(pingobj_iter_t *iter, struct value_node *vn_host) |
| { |
| char *s = NULL; |
| uint32_t drop, seq; |
| size_t len = sizeof(drop); |
| int rc = ping_iterator_get_info(iter, PING_INFO_DROPPED, &drop, &len); |
| if (rc) |
| return false; |
| |
| len = sizeof(seq); |
| rc = ping_iterator_get_info(iter, PING_INFO_SEQUENCE, &seq, &len); |
| if (rc) |
| return false; |
| |
| osmo_talloc_asprintf(vn_host, s, "%u/%u", drop, seq); |
| value_node_add(vn_host, "dropped", s); |
| |
| return true; |
| } |
| |
| static bool add_ttl(pingobj_iter_t *iter, struct value_node *vn_host) |
| { |
| int ttl, rc; |
| size_t len = sizeof(ttl); |
| |
| rc = ping_iterator_get_info(iter, PING_INFO_RECV_TTL, &ttl, &len); |
| if (rc) |
| return false; |
| |
| if (ttl > -1) { |
| char *s = NULL; |
| osmo_talloc_asprintf(vn_host, s, "%d", ttl); |
| value_node_add(vn_host, "TTL", s); |
| } |
| |
| return true; |
| } |
| |
| static bool add_latency(pingobj_iter_t *iter, struct value_node *vn_host) |
| { |
| double latency; |
| size_t len = sizeof(latency); |
| int rc = ping_iterator_get_info(iter, PING_INFO_LATENCY, &latency, &len); |
| if (rc) |
| return false; |
| |
| if (latency > -1) { |
| char *s = NULL; |
| osmo_talloc_asprintf(vn_host, s, "%.1lf ms", latency); |
| value_node_add(vn_host, "latency", s); |
| } |
| |
| return true; |
| } |
| |
| |
| /*********************************************************************** |
| * VTY |
| ***********************************************************************/ |
| |
| static struct cmd_node ping_node = { |
| PING_NODE, |
| "%s(config-ping)# ", |
| 1, |
| }; |
| |
| int osysmon_ping_go_parent(struct vty *vty) |
| { |
| switch (vty->node) { |
| case PING_NODE: |
| vty->node = CONFIG_NODE; |
| vty->index = NULL; |
| break; |
| default: |
| break; |
| } |
| return vty->node; |
| } |
| |
| #define PING_STR "Configure a host to be monitored/pinged\n" |
| |
| DEFUN(cfg_ping, cfg_ping_cmd, |
| "ping HOST", |
| PING_STR "Name of the host to ping\n") |
| { |
| int rc = add_host(g_oss->pings->ping_handle, argv[0]); |
| if (rc < 0) { |
| vty_out(vty, "[%d] Couldn't add pinger for %s: %s%s", |
| iterator_count(g_oss->pings->ping_handle), argv[0], |
| ping_get_error(g_oss->pings->ping_handle), VTY_NEWLINE); |
| |
| return CMD_WARNING; |
| } |
| |
| return CMD_SUCCESS; |
| } |
| |
| DEFUN(cfg_no_ping, cfg_no_ping_cmd, |
| "no ping HOST", |
| NO_STR PING_STR "Name of the host to ping\n") |
| { |
| int rc = ping_host_remove(g_oss->pings->ping_handle, argv[0]); |
| if (rc < 0) { |
| vty_out(vty, "[%d] Couldn't remove %s pinger: %s%s", |
| iterator_count(g_oss->pings->ping_handle), argv[0], |
| ping_get_error(g_oss->pings->ping_handle), VTY_NEWLINE); |
| |
| return CMD_WARNING; |
| } |
| |
| return CMD_SUCCESS; |
| } |
| |
| static int config_write_ping(struct vty *vty) |
| { |
| char buf[BUFLEN]; |
| pingobj_iter_t *iter; |
| for (iter = ping_iterator_get(g_oss->pings->ping_handle); iter; iter = ping_iterator_next(iter)) { |
| size_t len = BUFLEN; |
| /* hostname as it was supplied via vty 'ping' entry */ |
| if (ping_iterator_get_info(iter, PING_INFO_USERNAME, buf, &len)) |
| return CMD_WARNING; |
| |
| vty_out(vty, "ping %s%s", buf, VTY_NEWLINE); |
| } |
| |
| return CMD_SUCCESS; |
| } |
| |
| |
| /*********************************************************************** |
| * Runtime Code |
| ***********************************************************************/ |
| |
| /* called once on startup before config file parsing */ |
| int osysmon_ping_init() |
| { |
| install_element(CONFIG_NODE, &cfg_ping_cmd); |
| install_element(CONFIG_NODE, &cfg_no_ping_cmd); |
| install_node(&ping_node, config_write_ping); |
| |
| g_oss->pings = talloc_zero(NULL, struct ping_state); |
| if (!g_oss->pings) |
| return -ENOMEM; |
| |
| g_oss->pings->ping_handle = ping_construct(); |
| |
| if (g_oss->pings->ping_handle) |
| return 0; |
| |
| return -EINVAL; |
| } |
| |
| /* called periodically */ |
| int osysmon_ping_poll(struct value_node *parent) |
| { |
| char buf[BUFLEN]; |
| struct value_node *vn_host; |
| int num_host = iterator_count(g_oss->pings->ping_handle); |
| pingobj_iter_t *iter; |
| struct value_node *vn_ping = value_node_add(parent, "ping", NULL); |
| if (!vn_ping) |
| return -ENOMEM; |
| |
| for (iter = ping_iterator_get(g_oss->pings->ping_handle); iter; iter = ping_iterator_next(iter)) { |
| size_t len = BUFLEN; |
| int rc = ping_iterator_get_info(iter, PING_INFO_USERNAME, buf, &len); |
| if (rc) |
| return -EINVAL; |
| |
| vn_host = value_node_find_or_add(vn_ping, talloc_strdup(vn_ping, buf)); |
| if (!vn_host) |
| return -ENOMEM; |
| |
| len = BUFLEN; /* IP address is looked up on-call, even 40 bytes should be enough */ |
| rc = ping_iterator_get_info(iter, PING_INFO_ADDRESS, buf, &len); |
| if (rc) |
| return -EINVAL; |
| |
| value_node_add(vn_host, "IP", buf); |
| |
| add_drop(iter, vn_host); |
| |
| /* Parameters below might be absent from output depending on the host reachability: */ |
| add_latency(iter, vn_host); |
| add_ttl(iter, vn_host); |
| } |
| |
| if (num_host) |
| return ping_send(g_oss->pings->ping_handle); |
| |
| return 0; |
| } |