blob: a8e0fa8d7f4ddbc9d179ab48edd4cccefa4f733b [file] [log] [blame]
Harald Welte9e7fe002018-06-04 20:09:26 +02001/* Simple Osmocom System Monitor (osysmon): Support for monitoring net-devices */
2
3/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
Harald Welte9e7fe002018-06-04 20:09:26 +020017 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <string.h>
23#include <time.h>
24#include <arpa/inet.h>
25
26#include <linux/if.h>
27#include <linux/if_link.h>
28#include <linux/rtnetlink.h>
29
30#include <libmnl/libmnl.h>
31#include <talloc.h>
32
33#include "value_node.h"
34#include "osysmon.h"
35
36struct rtnl_client_state {
37 struct mnl_socket *nl;
38};
39
40
41struct netdev {
42 struct llist_head list;
43 struct {
44 const char *name;
45 } cfg;
46};
47
48static struct netdev *netdev_find(struct osysmon_state *os, const char *name)
49{
50 struct netdev *nd;
51 llist_for_each_entry(nd, &os->netdevs, list) {
52 if (!strcmp(name, nd->cfg.name))
53 return nd;
54 }
55 return NULL;
56}
57
58static struct netdev *netdev_create(struct osysmon_state *os, const char *name)
59{
60 struct netdev *nd;
61
62 if (netdev_find(os, name))
63 return NULL;
64
65 nd = talloc_zero(os, struct netdev);
66 if (!nd)
67 return NULL;
68 nd->cfg.name = talloc_strdup(os, name);
69 llist_add_tail(&nd->list, &os->netdevs);
70 return nd;
71}
72
73static void netdev_destroy(struct netdev *nd)
74{
75 llist_del(&nd->list);
76 talloc_free(nd);
77}
78
79/***********************************************************************
80 * VTY
81 ***********************************************************************/
82
83static struct cmd_node netdev_node = {
84 NETDEV_NODE,
85 "%s(config-netdev)# ",
86 1,
87};
88
89int osysmon_netdev_go_parent(struct vty *vty)
90{
91 switch (vty->node) {
92 case NETDEV_NODE:
93 vty->node = CONFIG_NODE;
94 vty->index = NULL;
95 break;
96 default:
97 break;
98 }
99 return vty->node;
100}
101
102DEFUN(cfg_netdev, cfg_netdev_cmd,
103 "netdev NAME",
104 "")
105{
106 struct netdev *nd;
107 nd = netdev_find(g_oss, argv[0]);
108 if (!nd)
109 nd = netdev_create(g_oss, argv[0]);
110 OSMO_ASSERT(nd);
111
112 return CMD_SUCCESS;
113}
114
115DEFUN(cfg_no_netdev, cfg_no_netdev_cmd,
116 "no netdev NAME",
117 "")
118{
119 struct netdev *nd;
120 nd = netdev_find(g_oss, argv[0]);
121 if (!nd) {
122 vty_out(vty, "Netdev %s doesn't exist in configuration%s", argv[0], VTY_NEWLINE);
123 return CMD_WARNING;
124 }
125 netdev_destroy(nd);
126 return CMD_SUCCESS;
127}
128
129static void write_one_netdev(struct vty *vty, struct netdev *nd)
130{
131 vty_out(vty, "netdev %s%s", nd->cfg.name, VTY_NEWLINE);
132}
133
134static int config_write_netdev(struct vty *vty)
135{
136 struct netdev *nd;
137
138 llist_for_each_entry(nd, &g_oss->netdevs, list)
139 write_one_netdev(vty, nd);
140 return CMD_SUCCESS;
141}
142
143static void osysmon_rtnl_vty_init(void)
144{
145 install_element(CONFIG_NODE, &cfg_netdev_cmd);
146 install_element(CONFIG_NODE, &cfg_no_netdev_cmd);
147 install_node(&netdev_node, config_write_netdev);
148}
149
150
151/***********************************************************************
152 * Interface Level
153 ***********************************************************************/
154
155static int if_attr_cb(const struct nlattr *attr, void *data)
156{
157 const struct nlattr **tb = data;
158 int type = mnl_attr_get_type(attr);
159
160 /* skip unsupported attribute in user-space */
161 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
162 return MNL_CB_OK;
163
164 switch(type) {
165 case IFLA_ADDRESS:
166 if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
167 perror("mnl_attr_validate");
168 return MNL_CB_ERROR;
169 }
170 break;
171 case IFLA_MTU:
172 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
173 perror("mnl_attr_validate");
174 return MNL_CB_ERROR;
175 }
176 break;
177 case IFLA_IFNAME:
178 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
179 perror("mnl_attr_validate");
180 return MNL_CB_ERROR;
181 }
182 break;
183 }
184 tb[type] = attr;
185 return MNL_CB_OK;
186}
187
188static int data_cb(const struct nlmsghdr *nlh, void *data)
189{
190 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
191 struct value_node *parent = data;
192 struct value_node *vn_if;
193 const char *name;
194 char buf[32];
195
196 struct nlattr *tb[IFLA_MAX+1] = {};
197 mnl_attr_parse(nlh, sizeof(*ifm), if_attr_cb, tb);
198 if (!tb[IFLA_IFNAME])
199 return MNL_CB_OK;
200 name = mnl_attr_get_str(tb[IFLA_IFNAME]);
201
202 /* skip any non-configured interface names */
203 if (!netdev_find(g_oss, name))
204 return MNL_CB_OK;
205
206 vn_if = value_node_find_or_add(parent, talloc_strdup(parent, name));
207 OSMO_ASSERT(vn_if);
208 vn_if->idx = ifm->ifi_index;
209
210 if (tb[IFLA_ADDRESS] && mnl_attr_get_payload_len(tb[IFLA_ADDRESS]) == 6) {
211 uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
212 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
213 hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
Maxf41973e2019-01-25 18:40:11 +0100214 value_node_add(vn_if, "hwaddr", buf);
Harald Welte9e7fe002018-06-04 20:09:26 +0200215 }
216 if (ifm->ifi_flags & IFF_RUNNING)
Maxf41973e2019-01-25 18:40:11 +0100217 value_node_add(vn_if, "running", "true");
Harald Welte9e7fe002018-06-04 20:09:26 +0200218 else
Maxf41973e2019-01-25 18:40:11 +0100219 value_node_add(vn_if, "running", "false");
Harald Welte9e7fe002018-06-04 20:09:26 +0200220
221 if (ifm->ifi_flags & IFF_UP)
Maxf41973e2019-01-25 18:40:11 +0100222 value_node_add(vn_if, "up", "true");
Harald Welte9e7fe002018-06-04 20:09:26 +0200223 else
Maxf41973e2019-01-25 18:40:11 +0100224 value_node_add(vn_if, "up", "false");
Harald Welte9e7fe002018-06-04 20:09:26 +0200225
226 return MNL_CB_OK;
227}
228
229static int rtnl_update_link(struct rtnl_client_state *rcs, struct value_node *parent)
230{
231 char buf[MNL_SOCKET_BUFFER_SIZE];
232 struct nlmsghdr *nlh;
233 struct rtgenmsg *rt;
234 int ret;
235 unsigned int seq, portid;
236
237 nlh = mnl_nlmsg_put_header(buf);
238 nlh->nlmsg_type = RTM_GETLINK;
239 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
240 nlh->nlmsg_seq = seq = time(NULL);
241 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
242 rt->rtgen_family = AF_PACKET;
243
244 portid = mnl_socket_get_portid(rcs->nl);
245
246 if (mnl_socket_sendto(rcs->nl, nlh, nlh->nlmsg_len) < 0) {
247 perror("mnl_socket_sendto");
248 exit(EXIT_FAILURE);
249 }
250
251 ret = mnl_socket_recvfrom(rcs->nl, buf, sizeof(buf));
252 while (ret > 0) {
253 ret = mnl_cb_run(buf, ret, seq, portid, data_cb, parent);
254 if (ret <= MNL_CB_STOP)
255 break;
256 ret = mnl_socket_recvfrom(rcs->nl, buf, sizeof(buf));
257 }
258 if (ret == -1) {
259 perror("error");
260 return -1;
261 }
262 return 0;
263}
264
265
266
267/***********************************************************************
268 * L3 Address Level
269 ***********************************************************************/
270
271static int inet_attr_cb(const struct nlattr *attr, void *data)
272{
273 const struct nlattr **tb = data;
274 int type = mnl_attr_get_type(attr);
275
276 /* skip unsupported attribute in user-space */
277 if (mnl_attr_type_valid(attr, IFA_MAX) < 0)
278 return MNL_CB_OK;
279
280 switch(type) {
281 case IFA_ADDRESS:
282 if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
283 perror("mnl_attr_validate");
284 return MNL_CB_ERROR;
285 }
286 break;
287 }
288 tb[type] = attr;
289 return MNL_CB_OK;
290}
291
292static int inet_data_cb(const struct nlmsghdr *nlh, void *data)
293{
294 struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh);
295 struct nlattr *tb[IFA_MAX + 1] = {};
296 struct value_node *parent = data;
297 struct value_node *vn_if;
298
299 vn_if = value_node_find_by_idx(parent, ifa->ifa_index);
300 if (!vn_if)
301 return MNL_CB_OK;
302
303 if (ifa->ifa_family != AF_INET)
304 return MNL_CB_OK;
305
306 mnl_attr_parse(nlh, sizeof(*ifa), inet_attr_cb, tb);
307 if (tb[IFA_ADDRESS]) {
308 struct in_addr *addr = mnl_attr_get_payload(tb[IFA_ADDRESS]);
309 char out[INET_ADDRSTRLEN+32];
310 snprintf(out, sizeof(out), "%s/%u", inet_ntoa(*addr), ifa->ifa_prefixlen);
Maxf41973e2019-01-25 18:40:11 +0100311 value_node_add(vn_if, "ip", out);
Harald Welte9e7fe002018-06-04 20:09:26 +0200312 }
313
314 return MNL_CB_OK;
315}
316
317static int rtnl_update_addr(struct rtnl_client_state *rcs, struct value_node *parent)
318{
319 char buf[MNL_SOCKET_BUFFER_SIZE];
320 struct nlmsghdr *nlh;
321 struct rtgenmsg *rt;
322 int ret;
323 unsigned int seq, portid;
324
325 nlh = mnl_nlmsg_put_header(buf);
326 nlh->nlmsg_type = RTM_GETADDR;
327 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
328 nlh->nlmsg_seq = seq = time(NULL);
329 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
330 rt->rtgen_family = AF_INET;
331
332 portid = mnl_socket_get_portid(rcs->nl);
333
334 if (mnl_socket_sendto(rcs->nl, nlh, nlh->nlmsg_len) < 0) {
335 perror("mnl_socket_sendto");
336 exit(EXIT_FAILURE);
337 }
338
339 ret = mnl_socket_recvfrom(rcs->nl, buf, sizeof(buf));
340 while (ret > 0) {
341 ret = mnl_cb_run(buf, ret, seq, portid, inet_data_cb, parent);
342 if (ret <= MNL_CB_STOP)
343 break;
344 ret = mnl_socket_recvfrom(rcs->nl, buf, sizeof(buf));
345 }
346 if (ret == -1) {
347 perror("error");
348 return -1;
349 }
350
351 return 0;
352}
353
354
355
356/***********************************************************************
357 * Common Code / API
358 ***********************************************************************/
359
360int osysmon_rtnl_init()
361{
362 osysmon_rtnl_vty_init();
363 return 0;
364}
365
366
367
368struct rtnl_client_state *rtnl_init(void *ctx)
369{
370 struct mnl_socket *nl;
371 struct rtnl_client_state *rcs;
372
373 nl = mnl_socket_open(NETLINK_ROUTE);
374 if (nl == NULL) {
375 perror("mnl_socket_open");
376 return NULL;
377 }
378 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
379 perror("mnl_socket_bind");
380 mnl_socket_close(nl);
381 return NULL;
382 }
383
384 rcs = talloc_zero(ctx, struct rtnl_client_state);
385 if (!rcs) {
386 mnl_socket_close(nl);
387 return NULL;
388 }
389
390 rcs->nl = nl;
391
392 return rcs;
393}
394
395int osysmon_rtnl_poll(struct value_node *parent)
396{
397 struct value_node *vn_net;
398
Daniel Willmann85d0fb22019-11-07 16:59:15 +0100399 if (llist_empty(&g_oss->netdevs))
400 return 0;
401
Harald Welte9e7fe002018-06-04 20:09:26 +0200402 if (!g_oss->rcs)
403 g_oss->rcs = rtnl_init(NULL);
404
Maxf41973e2019-01-25 18:40:11 +0100405 vn_net = value_node_add(parent, "netdev", NULL);
Harald Welte9e7fe002018-06-04 20:09:26 +0200406
407 if (!g_oss->rcs)
408 return -1;
409
410 rtnl_update_link(g_oss->rcs, vn_net);
411 rtnl_update_addr(g_oss->rcs, vn_net);
412
413 return 0;
414}