blob: f736798cf7fd39c5df45917aa19b37ac4de7f31c [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.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24
25#include <stdbool.h>
26#include <errno.h>
27
28#include <oping.h>
29
30#include <osmocom/core/utils.h>
31#include <osmocom/vty/vty.h>
32#include <osmocom/vty/command.h>
33
34#include "osysmon.h"
35#include "value_node.h"
36
37/***********************************************************************
38 * Data model
39 ***********************************************************************/
40
41#define BUFLEN 128
42
43struct ping_state {
44 pingobj_t *ping_handle;
45};
46
47/* FIXME: replace with ping_iterator_count() once we bump requirements to liboping 1.10.0+ */
48static unsigned iterator_count(pingobj_t *p)
49{
50 unsigned count = 0;
51 pingobj_iter_t *iter;
52 for (iter = ping_iterator_get(p); iter; iter = ping_iterator_next(iter))
53 count++;
54 return count;
55}
56
57/* Workaround for liboping issue https://github.com/octo/liboping/issues/43 */
58static int add_host(pingobj_t *pinger, const char *host)
59{
60 int num_host = iterator_count(pinger),
61 rc = ping_host_add(pinger, host);
62
63 if (rc < 0) {
64 if (num_host != iterator_count(pinger))
65 ping_host_remove(pinger, host);
66 }
67
68 return rc;
69}
70
71
72static bool add_drop(pingobj_iter_t *iter, struct value_node *vn_host)
73{
74 char *s = NULL;
75 uint32_t drop, seq;
76 size_t len = sizeof(drop);
77 int rc = ping_iterator_get_info(iter, PING_INFO_DROPPED, &drop, &len);
78 if (rc)
79 return false;
80
81 len = sizeof(seq);
82 rc = ping_iterator_get_info(iter, PING_INFO_SEQUENCE, &seq, &len);
83 if (rc)
84 return false;
85
86 osmo_talloc_asprintf(vn_host, s, "%u/%u", drop, seq);
87 value_node_add(vn_host, "dropped", s);
88
89 return true;
90}
91
92static bool add_ttl(pingobj_iter_t *iter, struct value_node *vn_host)
93{
94 int ttl, rc;
95 size_t len = sizeof(ttl);
96
97 rc = ping_iterator_get_info(iter, PING_INFO_RECV_TTL, &ttl, &len);
98 if (rc)
99 return false;
100
101 if (ttl > -1) {
102 char *s = NULL;
103 osmo_talloc_asprintf(vn_host, s, "%d", ttl);
104 value_node_add(vn_host, "TTL", s);
105 }
106
107 return true;
108}
109
110static bool add_latency(pingobj_iter_t *iter, struct value_node *vn_host)
111{
112 double latency;
113 size_t len = sizeof(latency);
114 int rc = ping_iterator_get_info(iter, PING_INFO_LATENCY, &latency, &len);
115 if (rc)
116 return false;
117
118 if (latency > -1) {
119 char *s = NULL;
120 osmo_talloc_asprintf(vn_host, s, "%.1lf ms", latency);
121 value_node_add(vn_host, "latency", s);
122 }
123
124 return true;
125}
126
127
128/***********************************************************************
129 * VTY
130 ***********************************************************************/
131
132static struct cmd_node ping_node = {
133 PING_NODE,
134 "%s(config-ping)# ",
135 1,
136};
137
138int osysmon_ping_go_parent(struct vty *vty)
139{
140 switch (vty->node) {
141 case PING_NODE:
142 vty->node = CONFIG_NODE;
143 vty->index = NULL;
144 break;
145 default:
146 break;
147 }
148 return vty->node;
149}
150
151#define PING_STR "Configure a host to be monitored/pinged\n"
152
153DEFUN(cfg_ping, cfg_ping_cmd,
154 "ping HOST",
155 PING_STR "Name of the host to ping\n")
156{
157 int rc = add_host(g_oss->pings->ping_handle, argv[0]);
158 if (rc < 0) {
159 vty_out(vty, "[%d] Couldn't add pinger for %s: %s%s",
160 iterator_count(g_oss->pings->ping_handle), argv[0],
161 ping_get_error(g_oss->pings->ping_handle), VTY_NEWLINE);
162
163 return CMD_WARNING;
164 }
165
166 return CMD_SUCCESS;
167}
168
169DEFUN(cfg_no_ping, cfg_no_ping_cmd,
170 "no ping HOST",
171 NO_STR PING_STR "Name of the host to ping\n")
172{
173 int rc = ping_host_remove(g_oss->pings->ping_handle, argv[0]);
174 if (rc < 0) {
175 vty_out(vty, "[%d] Couldn't remove %s pinger: %s%s",
176 iterator_count(g_oss->pings->ping_handle), argv[0],
177 ping_get_error(g_oss->pings->ping_handle), VTY_NEWLINE);
178
179 return CMD_WARNING;
180 }
181
182 return CMD_SUCCESS;
183}
184
185static int config_write_ping(struct vty *vty)
186{
187 char buf[BUFLEN];
188 pingobj_iter_t *iter;
189 for (iter = ping_iterator_get(g_oss->pings->ping_handle); iter; iter = ping_iterator_next(iter)) {
190 size_t len = BUFLEN;
191 /* hostname as it was supplied via vty 'ping' entry */
192 if (ping_iterator_get_info(iter, PING_INFO_USERNAME, buf, &len))
193 return CMD_WARNING;
194
195 vty_out(vty, "ping %s%s", buf, VTY_NEWLINE);
196 }
197
198 return CMD_SUCCESS;
199}
200
201
202/***********************************************************************
203 * Runtime Code
204 ***********************************************************************/
205
206/* called once on startup before config file parsing */
207int osysmon_ping_init()
208{
209 install_element(CONFIG_NODE, &cfg_ping_cmd);
210 install_element(CONFIG_NODE, &cfg_no_ping_cmd);
211 install_node(&ping_node, config_write_ping);
212
213 g_oss->pings = talloc_zero(NULL, struct ping_state);
214 if (!g_oss->pings)
215 return -ENOMEM;
216
217 g_oss->pings->ping_handle = ping_construct();
218
219 if (g_oss->pings->ping_handle)
220 return 0;
221
222 return -EINVAL;
223}
224
225/* called periodically */
226int osysmon_ping_poll(struct value_node *parent)
227{
228 char buf[BUFLEN];
229 struct value_node *vn_host;
230 int num_host = iterator_count(g_oss->pings->ping_handle);
231 pingobj_iter_t *iter;
232 struct value_node *vn_ping = value_node_add(parent, "ping", NULL);
233 if (!vn_ping)
234 return -ENOMEM;
235
236 for (iter = ping_iterator_get(g_oss->pings->ping_handle); iter; iter = ping_iterator_next(iter)) {
237 size_t len = BUFLEN;
238 int rc = ping_iterator_get_info(iter, PING_INFO_USERNAME, buf, &len);
239 if (rc)
240 return -EINVAL;
241
242 vn_host = value_node_find_or_add(vn_ping, talloc_strdup(vn_ping, buf));
243 if (!vn_host)
244 return -ENOMEM;
245
246 len = BUFLEN; /* IP address is looked up on-call, even 40 bytes should be enough */
247 rc = ping_iterator_get_info(iter, PING_INFO_ADDRESS, buf, &len);
248 if (rc)
249 return -EINVAL;
250
251 value_node_add(vn_host, "IP", buf);
252
253 add_drop(iter, vn_host);
254
255 /* Parameters below might be absent from output depending on the host reachability: */
256 add_latency(iter, vn_host);
257 add_ttl(iter, vn_host);
258 }
259
260 if (num_host)
261 return ping_send(g_oss->pings->ping_handle);
262
263 return 0;
264}