mirror of https://gerrit.osmocom.org/osmo-sysmon
261 lines
6.3 KiB
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);
|
|
}
|