osmo-sysmon/src/osysmon_ping.c

261 lines
6.3 KiB
C

/* 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.
*/
#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;
if (!num_host)
return 0;
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);
}
return ping_send(g_oss->pings->ping_handle);
}