blob: de20791b6eea442e1322621e8067844441749065 [file] [log] [blame]
Max8066a412019-01-28 23:59:03 +01001/* Simple Osmocom System Monitor (osysmon): Support for ping probe */
2
3/* (C) 2018 by sysmocom - s.f.m.c. GmbH.
4 * Author: Max Suraev
5 * All Rights Reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
Max8066a412019-01-28 23:59:03 +010018 */
19
20#include <stdbool.h>
21#include <errno.h>
22
23#include <oping.h>
24
25#include <osmocom/core/utils.h>
26#include <osmocom/vty/vty.h>
27#include <osmocom/vty/command.h>
28
29#include "osysmon.h"
30#include "value_node.h"
31
32/***********************************************************************
33 * Data model
34 ***********************************************************************/
35
36#define BUFLEN 128
37
38struct ping_state {
39 pingobj_t *ping_handle;
40};
41
42/* FIXME: replace with ping_iterator_count() once we bump requirements to liboping 1.10.0+ */
43static unsigned iterator_count(pingobj_t *p)
44{
45 unsigned count = 0;
46 pingobj_iter_t *iter;
47 for (iter = ping_iterator_get(p); iter; iter = ping_iterator_next(iter))
48 count++;
49 return count;
50}
51
52/* Workaround for liboping issue https://github.com/octo/liboping/issues/43 */
53static int add_host(pingobj_t *pinger, const char *host)
54{
55 int num_host = iterator_count(pinger),
56 rc = ping_host_add(pinger, host);
57
58 if (rc < 0) {
59 if (num_host != iterator_count(pinger))
60 ping_host_remove(pinger, host);
61 }
62
63 return rc;
64}
65
66
67static bool add_drop(pingobj_iter_t *iter, struct value_node *vn_host)
68{
69 char *s = NULL;
70 uint32_t drop, seq;
71 size_t len = sizeof(drop);
72 int rc = ping_iterator_get_info(iter, PING_INFO_DROPPED, &drop, &len);
73 if (rc)
74 return false;
75
76 len = sizeof(seq);
77 rc = ping_iterator_get_info(iter, PING_INFO_SEQUENCE, &seq, &len);
78 if (rc)
79 return false;
80
81 osmo_talloc_asprintf(vn_host, s, "%u/%u", drop, seq);
82 value_node_add(vn_host, "dropped", s);
83
84 return true;
85}
86
87static bool add_ttl(pingobj_iter_t *iter, struct value_node *vn_host)
88{
89 int ttl, rc;
90 size_t len = sizeof(ttl);
91
92 rc = ping_iterator_get_info(iter, PING_INFO_RECV_TTL, &ttl, &len);
93 if (rc)
94 return false;
95
96 if (ttl > -1) {
97 char *s = NULL;
98 osmo_talloc_asprintf(vn_host, s, "%d", ttl);
99 value_node_add(vn_host, "TTL", s);
100 }
101
102 return true;
103}
104
105static bool add_latency(pingobj_iter_t *iter, struct value_node *vn_host)
106{
107 double latency;
108 size_t len = sizeof(latency);
109 int rc = ping_iterator_get_info(iter, PING_INFO_LATENCY, &latency, &len);
110 if (rc)
111 return false;
112
113 if (latency > -1) {
114 char *s = NULL;
115 osmo_talloc_asprintf(vn_host, s, "%.1lf ms", latency);
116 value_node_add(vn_host, "latency", s);
117 }
118
119 return true;
120}
121
122
123/***********************************************************************
124 * VTY
125 ***********************************************************************/
126
127static struct cmd_node ping_node = {
128 PING_NODE,
129 "%s(config-ping)# ",
130 1,
131};
132
133int osysmon_ping_go_parent(struct vty *vty)
134{
135 switch (vty->node) {
136 case PING_NODE:
137 vty->node = CONFIG_NODE;
138 vty->index = NULL;
139 break;
140 default:
141 break;
142 }
143 return vty->node;
144}
145
146#define PING_STR "Configure a host to be monitored/pinged\n"
147
148DEFUN(cfg_ping, cfg_ping_cmd,
149 "ping HOST",
150 PING_STR "Name of the host to ping\n")
151{
152 int rc = add_host(g_oss->pings->ping_handle, argv[0]);
153 if (rc < 0) {
154 vty_out(vty, "[%d] Couldn't add pinger for %s: %s%s",
155 iterator_count(g_oss->pings->ping_handle), argv[0],
156 ping_get_error(g_oss->pings->ping_handle), VTY_NEWLINE);
157
158 return CMD_WARNING;
159 }
160
161 return CMD_SUCCESS;
162}
163
164DEFUN(cfg_no_ping, cfg_no_ping_cmd,
165 "no ping HOST",
166 NO_STR PING_STR "Name of the host to ping\n")
167{
168 int rc = ping_host_remove(g_oss->pings->ping_handle, argv[0]);
169 if (rc < 0) {
170 vty_out(vty, "[%d] Couldn't remove %s pinger: %s%s",
171 iterator_count(g_oss->pings->ping_handle), argv[0],
172 ping_get_error(g_oss->pings->ping_handle), VTY_NEWLINE);
173
174 return CMD_WARNING;
175 }
176
177 return CMD_SUCCESS;
178}
179
180static int config_write_ping(struct vty *vty)
181{
182 char buf[BUFLEN];
183 pingobj_iter_t *iter;
184 for (iter = ping_iterator_get(g_oss->pings->ping_handle); iter; iter = ping_iterator_next(iter)) {
185 size_t len = BUFLEN;
186 /* hostname as it was supplied via vty 'ping' entry */
187 if (ping_iterator_get_info(iter, PING_INFO_USERNAME, buf, &len))
188 return CMD_WARNING;
189
190 vty_out(vty, "ping %s%s", buf, VTY_NEWLINE);
191 }
192
193 return CMD_SUCCESS;
194}
195
196
197/***********************************************************************
198 * Runtime Code
199 ***********************************************************************/
200
201/* called once on startup before config file parsing */
202int osysmon_ping_init()
203{
204 install_element(CONFIG_NODE, &cfg_ping_cmd);
205 install_element(CONFIG_NODE, &cfg_no_ping_cmd);
206 install_node(&ping_node, config_write_ping);
207
208 g_oss->pings = talloc_zero(NULL, struct ping_state);
209 if (!g_oss->pings)
210 return -ENOMEM;
211
212 g_oss->pings->ping_handle = ping_construct();
213
214 if (g_oss->pings->ping_handle)
215 return 0;
216
217 return -EINVAL;
218}
219
220/* called periodically */
221int osysmon_ping_poll(struct value_node *parent)
222{
223 char buf[BUFLEN];
224 struct value_node *vn_host;
225 int num_host = iterator_count(g_oss->pings->ping_handle);
226 pingobj_iter_t *iter;
Daniel Willmann85d0fb22019-11-07 16:59:15 +0100227
228 if (!num_host)
229 return 0;
230
Max8066a412019-01-28 23:59:03 +0100231 struct value_node *vn_ping = value_node_add(parent, "ping", NULL);
232 if (!vn_ping)
233 return -ENOMEM;
234
235 for (iter = ping_iterator_get(g_oss->pings->ping_handle); iter; iter = ping_iterator_next(iter)) {
236 size_t len = BUFLEN;
237 int rc = ping_iterator_get_info(iter, PING_INFO_USERNAME, buf, &len);
238 if (rc)
239 return -EINVAL;
240
241 vn_host = value_node_find_or_add(vn_ping, talloc_strdup(vn_ping, buf));
242 if (!vn_host)
243 return -ENOMEM;
244
245 len = BUFLEN; /* IP address is looked up on-call, even 40 bytes should be enough */
246 rc = ping_iterator_get_info(iter, PING_INFO_ADDRESS, buf, &len);
247 if (rc)
248 return -EINVAL;
249
250 value_node_add(vn_host, "IP", buf);
251
252 add_drop(iter, vn_host);
253
254 /* Parameters below might be absent from output depending on the host reachability: */
255 add_latency(iter, vn_host);
256 add_ttl(iter, vn_host);
257 }
258
Daniel Willmann85d0fb22019-11-07 16:59:15 +0100259 return ping_send(g_oss->pings->ping_handle);
Max8066a412019-01-28 23:59:03 +0100260}