diff options
Diffstat (limited to 'src/in_netlink.c')
-rw-r--r-- | src/in_netlink.c | 868 |
1 files changed, 868 insertions, 0 deletions
diff --git a/src/in_netlink.c b/src/in_netlink.c new file mode 100644 index 0000000..ddd687c --- /dev/null +++ b/src/in_netlink.c @@ -0,0 +1,868 @@ +/* + * 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> + +static int c_notc = 0; +static struct element_group *grp; +static struct bmon_module netlink_ops; + +#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 = "Packtes", + .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, i; + + 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); +} |