When compiling bmon with the Linaro 2014.01 ARM toolchain (gcc-linaro-arm-linux-gnueabihf-4.8-2014.01_linux) the following compile error occurs: in_netlink.c: In function ‘do_link’: in_netlink.c:688:53: error: ‘IFF_UP’ undeclared (first use in this function) Fix it by explicitly including linux/if.h, where IFF_UP is defined. Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
874 lines
20 KiB
C
874 lines
20 KiB
C
/*
|
|
* in_netlink.c Netlink input
|
|
*
|
|
* Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
|
|
* Copyright (c) 2013 Red Hat, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <bmon/bmon.h>
|
|
#include <bmon/input.h>
|
|
#include <bmon/element.h>
|
|
#include <bmon/attr.h>
|
|
#include <bmon/conf.h>
|
|
#include <bmon/input.h>
|
|
#include <bmon/utils.h>
|
|
|
|
#ifndef SYS_BSD
|
|
|
|
static int c_notc = 0;
|
|
static struct element_group *grp;
|
|
static struct bmon_module netlink_ops;
|
|
|
|
#include <linux/if.h>
|
|
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/cache.h>
|
|
#include <netlink/utils.h>
|
|
#include <netlink/route/link.h>
|
|
#include <netlink/route/tc.h>
|
|
#include <netlink/route/qdisc.h>
|
|
#include <netlink/route/class.h>
|
|
#include <netlink/route/classifier.h>
|
|
#include <netlink/route/qdisc/htb.h>
|
|
|
|
static struct attr_map link_attrs[] = {
|
|
{
|
|
.name = "bytes",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_BYTE,
|
|
.description = "Bytes",
|
|
.rxid = RTNL_LINK_RX_BYTES,
|
|
.txid = RTNL_LINK_TX_BYTES,
|
|
},
|
|
{
|
|
.name = "packets",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Packets",
|
|
.rxid = RTNL_LINK_RX_PACKETS,
|
|
.txid = RTNL_LINK_TX_PACKETS,
|
|
},
|
|
{
|
|
.name = "errors",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Errors",
|
|
.rxid = RTNL_LINK_RX_ERRORS,
|
|
.txid = RTNL_LINK_TX_ERRORS,
|
|
},
|
|
{
|
|
.name = "drop",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Dropped",
|
|
.rxid = RTNL_LINK_RX_DROPPED,
|
|
.txid = RTNL_LINK_TX_DROPPED,
|
|
},
|
|
{
|
|
.name = "compressed",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Compressed",
|
|
.rxid = RTNL_LINK_RX_COMPRESSED,
|
|
.txid = RTNL_LINK_TX_COMPRESSED,
|
|
},
|
|
{
|
|
.name = "fifoerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "FIFO Error",
|
|
.rxid = RTNL_LINK_RX_FIFO_ERR,
|
|
.txid = RTNL_LINK_TX_FIFO_ERR,
|
|
},
|
|
{
|
|
.name = "lenerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Length Error",
|
|
.rxid = RTNL_LINK_RX_LEN_ERR,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "overerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Over Error",
|
|
.rxid = RTNL_LINK_RX_OVER_ERR,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "crcerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "CRC Error",
|
|
.rxid = RTNL_LINK_RX_CRC_ERR,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "frameerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Frame Error",
|
|
.rxid = RTNL_LINK_RX_FRAME_ERR,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "misserr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Missed Error",
|
|
.rxid = RTNL_LINK_RX_MISSED_ERR,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "aborterr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Abort Error",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_TX_ABORT_ERR,
|
|
},
|
|
{
|
|
.name = "carrerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Carrier Error",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_TX_CARRIER_ERR,
|
|
},
|
|
{
|
|
.name = "hbeaterr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Heartbeat Error",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_TX_HBEAT_ERR,
|
|
},
|
|
{
|
|
.name = "winerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Window Error",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_TX_WIN_ERR,
|
|
},
|
|
{
|
|
.name = "coll",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Collisions",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_COLLISIONS,
|
|
},
|
|
{
|
|
.name = "mcast",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Multicast",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_MULTICAST,
|
|
},
|
|
{
|
|
.name = "ip6pkts",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6Pkts",
|
|
.rxid = RTNL_LINK_IP6_INPKTS,
|
|
.txid = RTNL_LINK_IP6_OUTPKTS,
|
|
},
|
|
{
|
|
.name = "ip6discards",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6Discards",
|
|
.rxid = RTNL_LINK_IP6_INDISCARDS,
|
|
.txid = RTNL_LINK_IP6_OUTDISCARDS,
|
|
},
|
|
{
|
|
.name = "ip6octets",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_BYTE,
|
|
.description = "Ip6Octets",
|
|
.rxid = RTNL_LINK_IP6_INOCTETS,
|
|
.txid = RTNL_LINK_IP6_OUTOCTETS,
|
|
},
|
|
{
|
|
.name = "ip6bcastp",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Broadcast Packets",
|
|
.rxid = RTNL_LINK_IP6_INBCASTPKTS,
|
|
.txid = RTNL_LINK_IP6_OUTBCASTPKTS,
|
|
},
|
|
{
|
|
.name = "ip6bcast",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_BYTE,
|
|
.description = "Ip6 Broadcast",
|
|
.rxid = RTNL_LINK_IP6_INBCASTOCTETS,
|
|
.txid = RTNL_LINK_IP6_OUTBCASTOCTETS,
|
|
},
|
|
{
|
|
.name = "ip6mcastp",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Multicast Packets",
|
|
.rxid = RTNL_LINK_IP6_INMCASTPKTS,
|
|
.txid = RTNL_LINK_IP6_OUTMCASTPKTS,
|
|
},
|
|
{
|
|
.name = "ip6mcast",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_BYTE,
|
|
.description = "Ip6 Multicast",
|
|
.rxid = RTNL_LINK_IP6_INMCASTOCTETS,
|
|
.txid = RTNL_LINK_IP6_OUTMCASTOCTETS,
|
|
},
|
|
{
|
|
.name = "ip6noroute",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 No Route",
|
|
.rxid = RTNL_LINK_IP6_INNOROUTES,
|
|
.txid = RTNL_LINK_IP6_OUTNOROUTES,
|
|
},
|
|
{
|
|
.name = "ip6forward",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Forwarded",
|
|
.rxid = -1,
|
|
.txid = RTNL_LINK_IP6_OUTFORWDATAGRAMS,
|
|
},
|
|
{
|
|
.name = "ip6delivers",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Delivers",
|
|
.rxid = RTNL_LINK_IP6_INDELIVERS,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "icmp6",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "ICMPv6",
|
|
.rxid = RTNL_LINK_ICMP6_INMSGS,
|
|
.txid = RTNL_LINK_ICMP6_OUTMSGS,
|
|
},
|
|
{
|
|
.name = "icmp6err",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "ICMPv6 Errors",
|
|
.rxid = RTNL_LINK_ICMP6_INERRORS,
|
|
.txid = RTNL_LINK_ICMP6_OUTERRORS,
|
|
},
|
|
{
|
|
.name = "ip6inhdrerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Header Error",
|
|
.rxid = RTNL_LINK_IP6_INHDRERRORS,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "ip6toobigerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Too Big Error",
|
|
.rxid = RTNL_LINK_IP6_INTOOBIGERRORS,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "ip6trunc",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Truncated Packets",
|
|
.rxid = RTNL_LINK_IP6_INTRUNCATEDPKTS,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "ip6unkproto",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Unknown Protocol Error",
|
|
.rxid = RTNL_LINK_IP6_INUNKNOWNPROTOS,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "ip6addrerr",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Address Error",
|
|
.rxid = RTNL_LINK_IP6_INADDRERRORS,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "ip6reasmtimeo",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Reassembly Timeouts",
|
|
.rxid = RTNL_LINK_IP6_REASMTIMEOUT,
|
|
.txid = -1,
|
|
},
|
|
{
|
|
.name = "ip6fragok",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Reasm/Frag OK",
|
|
.rxid = RTNL_LINK_IP6_REASMOKS,
|
|
.txid = RTNL_LINK_IP6_FRAGOKS,
|
|
},
|
|
{
|
|
.name = "ip6fragfail",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Reasm/Frag Failures",
|
|
.rxid = RTNL_LINK_IP6_REASMFAILS,
|
|
.txid = RTNL_LINK_IP6_FRAGFAILS,
|
|
},
|
|
{
|
|
.name = "ip6fragcreate",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Ip6 Reasm/Frag Requests",
|
|
.rxid = RTNL_LINK_IP6_REASMREQDS,
|
|
.txid = RTNL_LINK_IP6_FRAGCREATES,
|
|
}
|
|
};
|
|
|
|
static struct attr_map tc_attrs[] = {
|
|
{
|
|
.name = "tc_bytes",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_BYTE,
|
|
.description = "Bytes",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_BYTES,
|
|
},
|
|
{
|
|
.name = "tc_packets",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Packets",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_PACKETS,
|
|
},
|
|
{
|
|
.name = "tc_overlimits",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Overlimits",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_OVERLIMITS,
|
|
},
|
|
{
|
|
.name = "tc_drop",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Dropped",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_DROPS,
|
|
},
|
|
{
|
|
.name = "tc_bps",
|
|
.type = ATTR_TYPE_RATE,
|
|
.unit = UNIT_BYTE,
|
|
.description = "Byte Rate/s",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_RATE_BPS,
|
|
},
|
|
{
|
|
.name = "tc_pps",
|
|
.type = ATTR_TYPE_RATE,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Packet Rate/s",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_RATE_PPS,
|
|
},
|
|
{
|
|
.name = "tc_qlen",
|
|
.type = ATTR_TYPE_RATE,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Queue Length",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_QLEN,
|
|
},
|
|
{
|
|
.name = "tc_backlog",
|
|
.type = ATTR_TYPE_RATE,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Backlog",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_BACKLOG,
|
|
},
|
|
{
|
|
.name = "tc_requeues",
|
|
.type = ATTR_TYPE_COUNTER,
|
|
.unit = UNIT_NUMBER,
|
|
.description = "Requeues",
|
|
.rxid = -1,
|
|
.txid = RTNL_TC_REQUEUES,
|
|
}
|
|
};
|
|
|
|
struct rdata {
|
|
struct element * parent;
|
|
int level;
|
|
};
|
|
|
|
static struct nl_sock *sock;
|
|
static struct nl_cache *link_cache, *qdisc_cache, *class_cache;
|
|
|
|
static void update_tc_attrs(struct element *e, struct rtnl_tc *tc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tc_attrs); i++) {
|
|
uint64_t c_tx = rtnl_tc_get_stat(tc, tc_attrs[i].txid);
|
|
attr_update(e, tc_attrs[i].attrid, 0, c_tx, UPDATE_FLAG_TX);
|
|
}
|
|
}
|
|
|
|
static void update_tc_infos(struct element *e, struct rtnl_tc *tc)
|
|
{
|
|
char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_mtu(tc));
|
|
element_update_info(e, "MTU", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_mpu(tc));
|
|
element_update_info(e, "MPU", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_overhead(tc));
|
|
element_update_info(e, "Overhead", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%#x", rtnl_tc_get_handle(tc));
|
|
element_update_info(e, "Id", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%#x", rtnl_tc_get_parent(tc));
|
|
element_update_info(e, "Parent", buf);
|
|
|
|
}
|
|
|
|
static void handle_qdisc(struct nl_object *obj, void *);
|
|
static void find_classes(uint32_t, struct rdata *);
|
|
static void find_qdiscs(uint32_t, struct rdata *);
|
|
|
|
static struct element *handle_tc_obj(struct rtnl_tc *tc, const char *prefix,
|
|
struct rdata *rdata)
|
|
{
|
|
char buf[IFNAME_MAX], name[IFNAME_MAX];
|
|
uint32_t id = rtnl_tc_get_handle(tc);
|
|
struct element *e;
|
|
|
|
rtnl_tc_handle2str(id, buf, sizeof(buf));
|
|
snprintf(name, sizeof(name), "%s %s (%s)",
|
|
prefix, buf, rtnl_tc_get_kind(tc));
|
|
|
|
if (!(e = element_lookup(grp, name, id, rdata ? rdata->parent : NULL, ELEMENT_CREAT)))
|
|
return NULL;
|
|
|
|
if (e->e_flags & ELEMENT_FLAG_CREATED) {
|
|
e->e_level = rdata ? rdata->level : 0;
|
|
|
|
if (element_set_key_attr(e, "tc_bytes", "tc_packets") ||
|
|
element_set_usage_attr(e, "tc_bytes"))
|
|
BUG();
|
|
|
|
update_tc_infos(e, tc);
|
|
|
|
e->e_flags &= ~ELEMENT_FLAG_CREATED;
|
|
}
|
|
|
|
update_tc_attrs(e, tc);
|
|
|
|
element_notify_update(e, NULL);
|
|
element_lifesign(e, 1);
|
|
|
|
return e;
|
|
}
|
|
|
|
static void handle_cls(struct nl_object *obj, void *arg)
|
|
{
|
|
struct rtnl_cls *cls = (struct rtnl_cls *) obj;
|
|
struct rdata *rdata = arg;
|
|
|
|
handle_tc_obj((struct rtnl_tc *) cls, "cls", rdata);
|
|
}
|
|
|
|
static void handle_class(struct nl_object *obj, void *arg)
|
|
{
|
|
struct rtnl_tc *tc = (struct rtnl_tc *) obj;
|
|
struct element *e;
|
|
struct rdata *rdata = arg;
|
|
struct rdata ndata = {
|
|
.level = rdata->level + 1,
|
|
};
|
|
|
|
if (!(e = handle_tc_obj(tc, "class", rdata)))
|
|
return;
|
|
|
|
ndata.parent = e;
|
|
|
|
if (!strcmp(rtnl_tc_get_kind(tc), "htb"))
|
|
element_set_txmax(e, rtnl_htb_get_rate((struct rtnl_class *) tc));
|
|
|
|
find_classes(rtnl_tc_get_handle(tc), &ndata);
|
|
find_qdiscs(rtnl_tc_get_handle(tc), &ndata);
|
|
}
|
|
|
|
static void find_qdiscs(uint32_t parent, struct rdata *rdata)
|
|
{
|
|
struct rtnl_qdisc *filter;
|
|
|
|
if (!(filter = rtnl_qdisc_alloc()))
|
|
return;
|
|
|
|
rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);
|
|
|
|
nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter),
|
|
handle_qdisc, rdata);
|
|
|
|
rtnl_qdisc_put(filter);
|
|
}
|
|
|
|
static void find_cls(int ifindex, uint32_t parent, struct rdata *rdata)
|
|
{
|
|
struct nl_cache *cls_cache;
|
|
|
|
if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
|
|
return;
|
|
|
|
nl_cache_foreach(cls_cache, handle_cls, rdata);
|
|
|
|
nl_cache_free(cls_cache);
|
|
}
|
|
|
|
static void find_classes(uint32_t parent, struct rdata *rdata)
|
|
{
|
|
struct rtnl_class *filter;
|
|
|
|
if (!(filter = rtnl_class_alloc()))
|
|
return;
|
|
|
|
rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);
|
|
|
|
nl_cache_foreach_filter(class_cache, OBJ_CAST(filter),
|
|
handle_class, rdata);
|
|
|
|
rtnl_class_put(filter);
|
|
}
|
|
|
|
static void handle_qdisc(struct nl_object *obj, void *arg)
|
|
{
|
|
struct rtnl_tc *tc = (struct rtnl_tc *) obj;
|
|
struct element *e;
|
|
struct rdata *rdata = arg;
|
|
struct rdata ndata = {
|
|
.level = rdata->level + 1,
|
|
};
|
|
|
|
if (!(e = handle_tc_obj(tc, "qdisc", rdata)))
|
|
return;
|
|
|
|
ndata.parent = e;
|
|
|
|
find_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc), &ndata);
|
|
|
|
if (rtnl_tc_get_parent(tc) == TC_H_ROOT) {
|
|
find_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT, &ndata);
|
|
find_classes(TC_H_ROOT, &ndata);
|
|
}
|
|
|
|
find_classes(rtnl_tc_get_handle(tc), &ndata);
|
|
}
|
|
|
|
static void handle_tc(struct element *e, struct rtnl_link *link)
|
|
{
|
|
struct rtnl_qdisc *qdisc;
|
|
int ifindex = rtnl_link_get_ifindex(link);
|
|
struct rdata rdata = {
|
|
.level = 1,
|
|
.parent = e,
|
|
};
|
|
|
|
if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0)
|
|
return;
|
|
|
|
qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT);
|
|
if (qdisc) {
|
|
handle_qdisc(OBJ_CAST(qdisc), &rdata);
|
|
rtnl_qdisc_put(qdisc);
|
|
}
|
|
|
|
qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0);
|
|
if (qdisc) {
|
|
handle_qdisc(OBJ_CAST(qdisc), &rdata);
|
|
rtnl_qdisc_put(qdisc);
|
|
}
|
|
|
|
qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS);
|
|
if (qdisc) {
|
|
handle_qdisc(OBJ_CAST(qdisc), &rdata);
|
|
rtnl_qdisc_put(qdisc);
|
|
}
|
|
|
|
nl_cache_free(class_cache);
|
|
}
|
|
|
|
static void update_link_infos(struct element *e, struct rtnl_link *link)
|
|
{
|
|
char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_mtu(link));
|
|
element_update_info(e, "MTU", buf);
|
|
|
|
rtnl_link_flags2str(rtnl_link_get_flags(link), buf, sizeof(buf));
|
|
element_update_info(e, "Flags", buf);
|
|
|
|
rtnl_link_operstate2str(rtnl_link_get_operstate(link),
|
|
buf, sizeof(buf));
|
|
element_update_info(e, "Operstate", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_ifindex(link));
|
|
element_update_info(e, "IfIndex", buf);
|
|
|
|
nl_addr2str(rtnl_link_get_addr(link), buf, sizeof(buf));
|
|
element_update_info(e, "Address", buf);
|
|
|
|
nl_addr2str(rtnl_link_get_broadcast(link), buf, sizeof(buf));
|
|
element_update_info(e, "Broadcast", buf);
|
|
|
|
rtnl_link_mode2str(rtnl_link_get_linkmode(link),
|
|
buf, sizeof(buf));
|
|
element_update_info(e, "Mode", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_txqlen(link));
|
|
element_update_info(e, "TXQlen", buf);
|
|
|
|
nl_af2str(rtnl_link_get_family(link), buf, sizeof(buf));
|
|
element_update_info(e, "Family", buf);
|
|
|
|
element_update_info(e, "Alias",
|
|
rtnl_link_get_ifalias(link) ? : "");
|
|
|
|
element_update_info(e, "Qdisc",
|
|
rtnl_link_get_qdisc(link) ? : "");
|
|
|
|
if (rtnl_link_get_link(link)) {
|
|
snprintf(buf, sizeof(buf), "%u", rtnl_link_get_link(link));
|
|
element_update_info(e, "SlaveOfIndex", buf);
|
|
}
|
|
}
|
|
|
|
static void do_link(struct nl_object *obj, void *arg)
|
|
{
|
|
struct rtnl_link *link = (struct rtnl_link *) obj;
|
|
struct element *e, *e_parent = NULL;
|
|
int i, master_ifindex;
|
|
|
|
if (!cfg_show_all && !(rtnl_link_get_flags(link) & IFF_UP)) {
|
|
/* FIXME: delete element */
|
|
return;
|
|
}
|
|
|
|
/* Check if the interface is a slave of another interface */
|
|
if ((master_ifindex = rtnl_link_get_link(link))) {
|
|
char parent[IFNAMSIZ+1];
|
|
|
|
rtnl_link_i2name(link_cache, master_ifindex,
|
|
parent, sizeof(parent));
|
|
|
|
e_parent = element_lookup(grp, parent, master_ifindex, NULL, 0);
|
|
}
|
|
|
|
if (!(e = element_lookup(grp, rtnl_link_get_name(link),
|
|
rtnl_link_get_ifindex(link), e_parent, ELEMENT_CREAT)))
|
|
return;
|
|
|
|
if (e->e_flags & ELEMENT_FLAG_CREATED) {
|
|
if (e->e_parent)
|
|
e->e_level = e->e_parent->e_level + 1;
|
|
|
|
if (element_set_key_attr(e, "bytes", "packets") ||
|
|
element_set_usage_attr(e, "bytes"))
|
|
BUG();
|
|
|
|
/* FIXME: Update link infos every 1s or so */
|
|
update_link_infos(e, link);
|
|
|
|
e->e_flags &= ~ELEMENT_FLAG_CREATED;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(link_attrs); i++) {
|
|
struct attr_map *m = &link_attrs[i];
|
|
uint64_t c_rx = 0, c_tx = 0;
|
|
int flags = 0;
|
|
|
|
if (m->rxid >= 0) {
|
|
c_rx = rtnl_link_get_stat(link, m->rxid);
|
|
flags |= UPDATE_FLAG_RX;
|
|
}
|
|
|
|
if (m->txid >= 0) {
|
|
c_tx = rtnl_link_get_stat(link, m->txid);
|
|
flags |= UPDATE_FLAG_TX;
|
|
}
|
|
|
|
attr_update(e, m->attrid, c_rx, c_tx, flags);
|
|
}
|
|
|
|
if (!c_notc)
|
|
handle_tc(e, link);
|
|
|
|
element_notify_update(e, NULL);
|
|
element_lifesign(e, 1);
|
|
}
|
|
|
|
static void netlink_read(void)
|
|
{
|
|
int err;
|
|
|
|
if ((err = nl_cache_resync(sock, link_cache, NULL, NULL)) < 0) {
|
|
fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err));
|
|
goto disable;
|
|
}
|
|
|
|
if ((err = nl_cache_resync(sock, qdisc_cache, NULL, NULL)) < 0) {
|
|
fprintf(stderr, "Unable to resync qdisc cache: %s\n", nl_geterror(err));
|
|
goto disable;
|
|
}
|
|
|
|
nl_cache_foreach(link_cache, do_link, NULL);
|
|
|
|
return;
|
|
|
|
disable:
|
|
netlink_ops.m_flags &= ~BMON_MODULE_ENABLED;
|
|
}
|
|
|
|
static void netlink_shutdown(void)
|
|
{
|
|
nl_cache_free(link_cache);
|
|
nl_cache_free(qdisc_cache);
|
|
nl_socket_free(sock);
|
|
}
|
|
|
|
static int netlink_do_init(void)
|
|
{
|
|
int err;
|
|
|
|
if (!(sock = nl_socket_alloc())) {
|
|
fprintf(stderr, "Unable to allocate netlink socket\n");
|
|
goto disable;
|
|
}
|
|
|
|
if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0) {
|
|
fprintf(stderr, "Unable to connect netlink socket: %s\n", nl_geterror(err));
|
|
goto disable;
|
|
}
|
|
|
|
if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) {
|
|
fprintf(stderr, "Unable to allocate link cache: %s\n", nl_geterror(err));
|
|
goto disable;
|
|
}
|
|
|
|
if ((err = rtnl_qdisc_alloc_cache(sock, &qdisc_cache)) < 0) {
|
|
fprintf(stderr, "Unable to allocate qdisc cache: %s\n", nl_geterror(err));
|
|
goto disable;
|
|
}
|
|
|
|
if (attr_map_load(link_attrs, ARRAY_SIZE(link_attrs)) ||
|
|
attr_map_load(tc_attrs, ARRAY_SIZE(tc_attrs)))
|
|
BUG();
|
|
|
|
if (!(grp = group_lookup(DEFAULT_GROUP, GROUP_CREATE)))
|
|
BUG();
|
|
|
|
return 0;
|
|
|
|
disable:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int netlink_probe(void)
|
|
{
|
|
struct nl_sock *sock;
|
|
struct nl_cache *lc;
|
|
int ret = 0;
|
|
|
|
if (!(sock = nl_socket_alloc()))
|
|
return 0;
|
|
|
|
if (nl_connect(sock, NETLINK_ROUTE) < 0)
|
|
return 0;
|
|
|
|
if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &lc) == 0) {
|
|
nl_cache_free(lc);
|
|
ret = 1;
|
|
}
|
|
|
|
nl_socket_free(sock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void print_help(void)
|
|
{
|
|
printf(
|
|
"netlink - Netlink statistic collector for Linux\n" \
|
|
"\n" \
|
|
" Powerful statistic collector for Linux using netlink sockets\n" \
|
|
" to collect link and traffic control statistics.\n" \
|
|
" Author: Thomas Graf <tgraf@suug.ch>\n" \
|
|
"\n" \
|
|
" Options:\n" \
|
|
" notc Do not collect traffic control statistics\n");
|
|
}
|
|
|
|
static void netlink_parse_opt(const char *type, const char *value)
|
|
{
|
|
if (!strcasecmp(type, "notc"))
|
|
c_notc = 1;
|
|
else if (!strcasecmp(type, "help")) {
|
|
print_help();
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
static struct bmon_module netlink_ops = {
|
|
.m_name = "netlink",
|
|
.m_flags = BMON_MODULE_DEFAULT,
|
|
.m_do = netlink_read,
|
|
.m_shutdown = netlink_shutdown,
|
|
.m_parse_opt = netlink_parse_opt,
|
|
.m_probe = netlink_probe,
|
|
.m_init = netlink_do_init,
|
|
};
|
|
|
|
static void __init netlink_init(void)
|
|
{
|
|
input_register(&netlink_ops);
|
|
}
|
|
#endif
|