version 1.1, 2012/02/21 22:19:56
|
version 1.1.1.2, 2014/07/30 07:55:27
|
Line 1
|
Line 1
|
/* |
/* |
* in_netlink.c rtnetlink input (Linux) | * in_netlink.c Netlink input |
* |
* |
* Copyright (c) 2001-2005 Thomas Graf <tgraf@suug.ch> | * 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 |
* Permission is hereby granted, free of charge, to any person obtaining a |
* copy of this software and associated documentation files (the "Software"), |
* copy of this software and associated documentation files (the "Software"), |
Line 24
|
Line 25
|
|
|
#include <bmon/bmon.h> |
#include <bmon/bmon.h> |
#include <bmon/input.h> |
#include <bmon/input.h> |
#include <bmon/node.h> | #include <bmon/element.h> |
#include <bmon/item.h> | #include <bmon/attr.h> |
#include <bmon/conf.h> |
#include <bmon/conf.h> |
|
#include <bmon/input.h> |
#include <bmon/utils.h> |
#include <bmon/utils.h> |
|
|
#if defined HAVE_NL && defined SYS_LINUX | #ifndef SYS_BSD |
|
|
static int c_notc = 0; |
static int c_notc = 0; |
static const char *c_mapfile; | static struct element_group *grp; |
| static struct bmon_module netlink_ops; |
|
|
struct hmap { |
|
char *id; |
|
char *name; |
|
struct hmap *next; |
|
}; |
|
|
|
static struct hmap *map; |
|
|
|
#include <netlink/netlink.h> |
#include <netlink/netlink.h> |
#include <netlink/cache.h> |
#include <netlink/cache.h> |
#include <netlink/helpers.h> | #include <netlink/utils.h> |
#include <netlink/route/link.h> |
#include <netlink/route/link.h> |
|
#include <netlink/route/tc.h> |
#include <netlink/route/qdisc.h> |
#include <netlink/route/qdisc.h> |
#include <netlink/route/class.h> |
#include <netlink/route/class.h> |
#include <netlink/route/filter.h> | #include <netlink/route/classifier.h> |
| #include <netlink/route/qdisc/htb.h> |
|
|
#include <net/if.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 nl_handle nl_h = NL_INIT_HANDLE(); | static struct attr_map tc_attrs[] = { |
static struct nl_cache link_cache = RTNL_INIT_LINK_CACHE(); | { |
static struct nl_cache *qdisc_cache; | .name = "tc_bytes", |
static struct nl_cache *class_cache; | .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 xdata { | struct rdata { |
item_t *intf; | struct element * parent; |
struct rtnl_link *link; | int level; |
int level; | |
item_t *parent; | |
}; |
}; |
|
|
static void read_map(const char *file) | 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) |
{ |
{ |
FILE *f; | int i; |
char buf[1024]; | |
int line = 0; | |
|
|
f = fopen(file, "r"); | for (i = 0; i < ARRAY_SIZE(tc_attrs); i++) { |
if (f == NULL) | uint64_t c_tx = rtnl_tc_get_stat(tc, tc_attrs[i].txid); |
quit("Could not open mapfile %s: %s\n", file, strerror(errno)); | attr_update(e, tc_attrs[i].attrid, 0, c_tx, UPDATE_FLAG_TX); |
| } |
| } |
|
|
while (fgets(buf, sizeof(buf), f)) { | static void update_tc_infos(struct element *e, struct rtnl_tc *tc) |
struct hmap *m; | { |
char *p, *id, *name; | char buf[64]; |
line++; | |
if (*buf == '#' || *buf == '\r' || *buf == '\n') | |
continue; | |
|
|
for (p = buf; *p == ' ' || *p == '\t'; p++); | snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_mtu(tc)); |
if (*p == '\0') | element_update_info(e, "MTU", buf); |
quit("%s: Parse error at line %d: Missing handle\n", | |
file, line); | |
id = p; | |
|
|
for (; *p != ' ' && *p != '\t' && *p != '\0'; p++); | snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_mpu(tc)); |
if (*p == '\0') | element_update_info(e, "MPU", buf); |
quit("%s: Parse error at line %d: Missing name\n", | |
file, line); | |
*p = '\0'; | |
|
|
for (++p; *p == ' ' || *p == '\t'; p++); | snprintf(buf, sizeof(buf), "%u", rtnl_tc_get_overhead(tc)); |
name = p; | element_update_info(e, "Overhead", buf); |
for (; *p != ' ' && *p != '\t' && *p != '\0' && | |
*p != '\r' && *p != '\n'; p++); | |
*p = '\0'; | |
|
|
if (strlen(id) <= 0 || strlen(name) <= 0) | snprintf(buf, sizeof(buf), "%#x", rtnl_tc_get_handle(tc)); |
quit("%s: Parse error at line %d: Invalid id or handle\n", | element_update_info(e, "Id", buf); |
file, line); | |
|
|
m = xcalloc(1, sizeof(*m)); | snprintf(buf, sizeof(buf), "%#x", rtnl_tc_get_parent(tc)); |
m->id = strdup(id); | element_update_info(e, "Parent", buf); |
m->name = strdup(name); | |
m->next = map; | |
map = m; | |
} | |
|
|
fclose(f); |
|
} |
} |
|
|
static const char *lookup_map(const char *handle) | 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) |
{ |
{ |
struct hmap *m; | char buf[IFNAME_MAX], name[IFNAME_MAX]; |
| uint32_t id = rtnl_tc_get_handle(tc); |
| struct element *e; |
|
|
for (m = map; m; m = m->next) | rtnl_tc_handle2str(id, buf, sizeof(buf)); |
if (!strcmp(handle, m->id)) | snprintf(name, sizeof(name), "%s %s (%s)", |
return m->name; | prefix, buf, rtnl_tc_get_kind(tc)); |
|
|
return handle; | 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_qdisc(struct nl_common *, void *); | static void handle_cls(struct nl_object *obj, void *arg) |
| { |
| struct rtnl_cls *cls = (struct rtnl_cls *) obj; |
| struct rdata *rdata = arg; |
|
|
#if 0 | handle_tc_obj((struct rtnl_tc *) cls, "cls", rdata); |
static void handle_filter(struct nl_common *c, void *arg) | } |
| |
| static void handle_class(struct nl_object *obj, void *arg) |
{ |
{ |
struct rtnl_filter *filter = (struct rtnl_filter *) c; | struct rtnl_tc *tc = (struct rtnl_tc *) obj; |
struct xdata *x = arg; | struct element *e; |
item_t *intf; | struct rdata *rdata = arg; |
char name[IFNAME_MAX]; | struct rdata ndata = { |
| .level = rdata->level + 1, |
| }; |
|
|
printf("HAHAH!\n"); | if (!(e = handle_tc_obj(tc, "class", rdata))) |
| return; |
|
|
if (filter->f_handle) { | ndata.parent = e; |
const char *h = lookup_map(nl_handle2str(filter->f_handle)); | |
snprintf(name, sizeof(name), "f:%s %s", filter->f_kind, h); | |
} else | |
snprintf(name, sizeof(name), "f:%s", filter->f_kind); | |
|
|
intf = lookup_item(get_local_node(), name, filter->f_handle, x->parent); | if (!strcmp(rtnl_tc_get_kind(tc), "htb")) |
| element_set_txmax(e, rtnl_htb_get_rate((struct rtnl_class *) tc)); |
|
|
if (intf == NULL) | 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; |
return; |
|
|
intf->i_link = x->intf->i_index; | rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); |
intf->i_flags |= ITEM_FLAG_IS_CHILD; | |
intf->i_level = x->level; | |
|
|
update_attr(intf, PACKETS, 0, 0, 0); | nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter), |
update_attr(intf, BYTES, 0, 0, 0); | handle_qdisc, rdata); |
|
|
notify_update(intf, NULL); | rtnl_qdisc_put(filter); |
increase_lifetime(intf, 1); | |
} |
} |
#endif |
|
|
|
static void handle_class(struct nl_common *c, void *arg) | static void find_cls(int ifindex, uint32_t parent, struct rdata *rdata) |
{ |
{ |
struct rtnl_class *class = (struct rtnl_class *) c; | struct nl_cache *cls_cache; |
struct rtnl_qdisc *leaf; | |
struct xdata *x = arg; | |
item_t *intf; | |
char name[IFNAME_MAX]; | |
struct xdata xn = { | |
.intf = x->intf, | |
.link = x->link, | |
.level = x->level + 1, | |
}; | |
|
|
if (class->c_handle) { | if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0) |
const char *h = lookup_map(nl_handle2str(class->c_handle)); | return; |
snprintf(name, sizeof(name), "c:%s(%s)", class->c_kind, h); | |
} else | |
snprintf(name, sizeof(name), "c:%s", class->c_kind); | |
|
|
intf = lookup_item(get_local_node(), name, class->c_handle, x->parent); | nl_cache_foreach(cls_cache, handle_cls, rdata); |
|
|
if (intf == NULL) | nl_cache_free(cls_cache); |
return; | } |
|
|
xn.parent = intf; | static void find_classes(uint32_t parent, struct rdata *rdata) |
| { |
| struct rtnl_class *filter; |
|
|
intf->i_link = x->intf->i_index; | if (!(filter = rtnl_class_alloc())) |
intf->i_flags |= ITEM_FLAG_IS_CHILD; | return; |
intf->i_level = x->level; | |
intf->i_major_attr = BYTES; | |
intf->i_minor_attr = PACKETS; | |
|
|
update_attr(intf, PACKETS, 0, class->c_stats.tcs_basic.packets, TX_PROVIDED); | rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); |
update_attr(intf, BYTES, 0, class->c_stats.tcs_basic.bytes, TX_PROVIDED); | |
update_attr(intf, DROP, 0, class->c_stats.tcs_queue.drops, TX_PROVIDED); | |
update_attr(intf, OVERLIMITS, 0, class->c_stats.tcs_queue.overlimits, TX_PROVIDED); | |
update_attr(intf, BPS, 0, class->c_stats.tcs_rate_est.bps, TX_PROVIDED); | |
update_attr(intf, PPS, 0, class->c_stats.tcs_rate_est.pps, TX_PROVIDED); | |
update_attr(intf, QLEN, 0, class->c_stats.tcs_queue.qlen, TX_PROVIDED); | |
update_attr(intf, BACKLOG, 0, class->c_stats.tcs_queue.backlog, TX_PROVIDED); | |
update_attr(intf, REQUEUES, 0, class->c_stats.tcs_queue.requeues, TX_PROVIDED); | |
|
|
notify_update(intf, NULL); | nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), |
increase_lifetime(intf, 1); | handle_class, rdata); |
|
|
if ((leaf = rtnl_class_leaf_qdisc(class, qdisc_cache))) | rtnl_class_put(filter); |
handle_qdisc((struct nl_common *) leaf, &xn); | |
rtnl_class_foreach_child(class, class_cache, &handle_class, &xn); | |
#if 0 | |
rtnl_class_foreach_filter_nocache(&nl_h, class, &handle_filter, &xn); | |
#endif | |
} |
} |
|
|
static void handle_qdisc(struct nl_common *c, void *arg) | static void handle_qdisc(struct nl_object *obj, void *arg) |
{ |
{ |
struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) c; | struct rtnl_tc *tc = (struct rtnl_tc *) obj; |
struct xdata *x = arg; | struct element *e; |
item_t *intf; | struct rdata *rdata = arg; |
char name[IFNAME_MAX]; | struct rdata ndata = { |
struct xdata xn = { | .level = rdata->level + 1, |
.intf = x->intf, | |
.link = x->link, | |
.level = x->level + 1, | |
}; |
}; |
|
|
if (qdisc->q_handle) { | if (!(e = handle_tc_obj(tc, "qdisc", rdata))) |
const char *h = lookup_map(nl_handle2str(qdisc->q_handle)); | |
snprintf(name, sizeof(name), "q:%s(%s)", qdisc->q_kind, h); | |
} else | |
snprintf(name, sizeof(name), "q:%s", qdisc->q_kind); | |
| |
intf = lookup_item(get_local_node(), name, qdisc->q_handle, x->parent); | |
if (intf == NULL) | |
return; |
return; |
|
|
xn.parent = intf; | ndata.parent = e; |
|
|
intf->i_link = x->intf->i_index; | find_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc), &ndata); |
intf->i_flags |= ITEM_FLAG_IS_CHILD; | |
intf->i_level = x->level; | |
intf->i_major_attr = BYTES; | |
intf->i_minor_attr = PACKETS; | |
|
|
if (qdisc->q_handle == 0xffff00) { | if (rtnl_tc_get_parent(tc) == TC_H_ROOT) { |
update_attr(intf, BYTES, qdisc->q_stats.tcs_basic.bytes, 0, RX_PROVIDED); | find_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT, &ndata); |
update_attr(intf, PACKETS, qdisc->q_stats.tcs_basic.packets, 0, RX_PROVIDED); | find_classes(TC_H_ROOT, &ndata); |
update_attr(intf, DROP, qdisc->q_stats.tcs_queue.drops, 0, RX_PROVIDED); | |
update_attr(intf, OVERLIMITS, qdisc->q_stats.tcs_queue.overlimits, 0, RX_PROVIDED); | |
update_attr(intf, BPS, qdisc->q_stats.tcs_rate_est.bps, 0, RX_PROVIDED); | |
update_attr(intf, PPS, qdisc->q_stats.tcs_rate_est.pps, 0, RX_PROVIDED); | |
update_attr(intf, QLEN, qdisc->q_stats.tcs_queue.qlen, 0, RX_PROVIDED); | |
update_attr(intf, BACKLOG, qdisc->q_stats.tcs_queue.backlog, 0, RX_PROVIDED); | |
update_attr(intf, REQUEUES, qdisc->q_stats.tcs_queue.requeues, 0, RX_PROVIDED); | |
} else { | |
update_attr(intf, BYTES, 0, qdisc->q_stats.tcs_basic.bytes, TX_PROVIDED); | |
update_attr(intf, PACKETS, 0, qdisc->q_stats.tcs_basic.packets, TX_PROVIDED); | |
update_attr(intf, DROP, 0, qdisc->q_stats.tcs_queue.drops, TX_PROVIDED); | |
update_attr(intf, OVERLIMITS, 0, qdisc->q_stats.tcs_queue.overlimits, TX_PROVIDED); | |
update_attr(intf, BPS, 0, qdisc->q_stats.tcs_rate_est.bps, TX_PROVIDED); | |
update_attr(intf, PPS, 0, qdisc->q_stats.tcs_rate_est.pps, TX_PROVIDED); | |
update_attr(intf, QLEN, 0, qdisc->q_stats.tcs_queue.qlen, TX_PROVIDED); | |
update_attr(intf, BACKLOG, 0, qdisc->q_stats.tcs_queue.backlog, TX_PROVIDED); | |
update_attr(intf, REQUEUES, 0, qdisc->q_stats.tcs_queue.requeues, TX_PROVIDED); | |
} |
} |
|
|
notify_update(intf, NULL); | find_classes(rtnl_tc_get_handle(tc), &ndata); |
increase_lifetime(intf, 1); | |
| |
rtnl_qdisc_foreach_child(qdisc, class_cache, &handle_class, &xn); | |
#if 0 | |
rtnl_qdisc_foreach_filter_nocache(&nl_h, qdisc, &handle_filter, &xn); | |
#endif | |
} |
} |
|
|
static void handle_tc(item_t *intf, struct rtnl_link *link) | static void handle_tc(struct element *e, struct rtnl_link *link) |
{ |
{ |
struct rtnl_qdisc *qdisc; |
struct rtnl_qdisc *qdisc; |
struct xdata x = { | int ifindex = rtnl_link_get_ifindex(link); |
.intf = intf, | struct rdata rdata = { |
.link = link, | |
.level = 1, |
.level = 1, |
.parent = intf, | .parent = e, |
}; |
}; |
|
|
class_cache = rtnl_class_build_cache(&nl_h, link->l_index); | if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0) |
if (class_cache == NULL) | |
return; |
return; |
|
|
if ((qdisc = rtnl_qdisc_get_root(qdisc_cache, link->l_index))) | qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT); |
handle_qdisc((struct nl_common *) qdisc, &x); | if (qdisc) { |
else if ((qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, link->l_index, 0))) | handle_qdisc(OBJ_CAST(qdisc), &rdata); |
handle_qdisc((struct nl_common *) qdisc, &x); | rtnl_qdisc_put(qdisc); |
| } |
|
|
if ((qdisc = rtnl_qdisc_get_ingress(qdisc_cache, link->l_index))) | qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0); |
handle_qdisc((struct nl_common *) qdisc, &x); | if (qdisc) { |
| handle_qdisc(OBJ_CAST(qdisc), &rdata); |
| rtnl_qdisc_put(qdisc); |
| } |
|
|
nl_cache_destroy_and_free(class_cache); | 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 do_link(struct nl_common *item, void *arg) | static void update_link_infos(struct element *e, struct rtnl_link *link) |
{ |
{ |
struct rtnl_link *link = (struct rtnl_link *) item; | char buf[64]; |
struct rtnl_lstats *st; | |
item_t *intf; | |
|
|
if (!link->l_name[0]) | snprintf(buf, sizeof(buf), "%u", rtnl_link_get_mtu(link)); |
return; | element_update_info(e, "MTU", buf); |
|
|
if (get_show_only_running() && !(link->l_flags & IFF_UP)) | 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; |
return; |
|
} |
|
|
intf = lookup_item(get_local_node(), link->l_name, 0, 0); | /* Check if the interface is a slave of another interface */ |
| if ((master_ifindex = rtnl_link_get_link(link))) { |
| char parent[IFNAMSIZ+1]; |
|
|
if (NULL == intf) | 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; |
return; |
|
|
intf->i_major_attr = BYTES; | if (e->e_flags & ELEMENT_FLAG_CREATED) { |
intf->i_minor_attr = PACKETS; | if (e->e_parent) |
| e->e_level = e->e_parent->e_level + 1; |
|
|
st = &link->l_stats; | if (element_set_key_attr(e, "bytes", "packets") || |
| element_set_usage_attr(e, "bytes")) |
| BUG(); |
|
|
update_attr(intf, BYTES, st->ls_rx.bytes, st->ls_tx.bytes, | /* FIXME: Update link infos every 1s or so */ |
RX_PROVIDED | TX_PROVIDED); | update_link_infos(e, link); |
update_attr(intf, PACKETS, st->ls_rx.packets, st->ls_tx.packets, | |
RX_PROVIDED | TX_PROVIDED); | |
|
|
update_attr(intf, ERRORS, st->ls_rx.errors, st->ls_tx.errors, | e->e_flags &= ~ELEMENT_FLAG_CREATED; |
RX_PROVIDED | TX_PROVIDED); | } |
|
|
update_attr(intf, DROP, st->ls_rx.dropped, st->ls_tx.dropped, | for (i = 0; i < ARRAY_SIZE(link_attrs); i++) { |
RX_PROVIDED | TX_PROVIDED); | struct attr_map *m = &link_attrs[i]; |
| uint64_t c_rx = 0, c_tx = 0; |
| int flags = 0; |
|
|
update_attr(intf, FIFO, st->ls_rx_fifo_errors, | if (m->rxid >= 0) { |
st->ls_tx_fifo_errors, RX_PROVIDED | TX_PROVIDED); | c_rx = rtnl_link_get_stat(link, m->rxid); |
| flags |= UPDATE_FLAG_RX; |
| } |
|
|
update_attr(intf, COMPRESSED, st->ls_rx.compressed, | if (m->txid >= 0) { |
st->ls_tx.compressed, RX_PROVIDED | TX_PROVIDED); | c_tx = rtnl_link_get_stat(link, m->txid); |
| flags |= UPDATE_FLAG_TX; |
| } |
|
|
update_attr(intf, MULTICAST, st->ls_rx.multicast, 0, RX_PROVIDED); | attr_update(e, m->attrid, c_rx, c_tx, flags); |
update_attr(intf, COLLISIONS, 0, st->ls_tx_collisions, TX_PROVIDED); | } |
update_attr(intf, LENGTH_ERRORS, st->ls_rx_length_errors, 0, RX_PROVIDED); | |
update_attr(intf, OVER_ERRORS, st->ls_rx_over_errors, 0, RX_PROVIDED); | |
update_attr(intf, CRC_ERRORS, st->ls_rx_crc_errors, 0, RX_PROVIDED); | |
update_attr(intf, FRAME, st->ls_rx_frame_errors, 0, RX_PROVIDED); | |
update_attr(intf, MISSED_ERRORS, st->ls_rx_missed_errors, 0, RX_PROVIDED); | |
update_attr(intf, ABORTED_ERRORS, 0, st->ls_tx_aborted_errors, TX_PROVIDED); | |
update_attr(intf, HEARTBEAT_ERRORS, 0, st->ls_tx_heartbeat_errors, | |
TX_PROVIDED); | |
update_attr(intf, WINDOW_ERRORS, 0, st->ls_tx_window_errors, TX_PROVIDED); | |
update_attr(intf, CARRIER_ERRORS, 0, st->ls_tx_carrier_errors, TX_PROVIDED); | |
|
|
if (!c_notc) |
if (!c_notc) |
handle_tc(intf, link); | handle_tc(e, link); |
| |
notify_update(intf, NULL); | element_notify_update(e, NULL); |
increase_lifetime(intf, 1); | element_lifesign(e, 1); |
} |
} |
|
|
static void netlink_read(void) |
static void netlink_read(void) |
{ |
{ |
if (nl_cache_update(&nl_h, &link_cache) < 0) | int err; |
quit("%s\n", nl_geterror()); | |
|
|
if (!c_notc) { | if ((err = nl_cache_resync(sock, link_cache, NULL, NULL)) < 0) { |
if (qdisc_cache == NULL) { | fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err)); |
qdisc_cache = rtnl_qdisc_build_cache(&nl_h); | goto disable; |
if (qdisc_cache == NULL) | |
c_notc = 1; | |
} else { | |
if (nl_cache_update(&nl_h, qdisc_cache) < 0) | |
c_notc = 1; | |
} | |
} |
} |
|
|
nl_cache_foreach(&link_cache, do_link, NULL); | 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) |
static void netlink_shutdown(void) |
{ |
{ |
nl_close(&nl_h); | nl_cache_free(link_cache); |
| nl_cache_free(qdisc_cache); |
| nl_socket_free(sock); |
} |
} |
|
|
static void netlink_do_init(void) | static int netlink_do_init(void) |
{ |
{ |
if (nl_connect(&nl_h, NETLINK_ROUTE) < 0) | int err, i; |
quit("%s\n", nl_geterror()); | |
|
|
nl_use_default_handlers(&nl_h); | 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) |
static int netlink_probe(void) |
{ |
{ |
if (nl_connect(&nl_h, NETLINK_ROUTE) < 0) | struct nl_sock *sock; |
| struct nl_cache *lc; |
| int ret = 0; |
| |
| if (!(sock = nl_socket_alloc())) |
return 0; |
return 0; |
|
|
if (nl_cache_update(&nl_h, &link_cache) < 0) { | if (nl_connect(sock, NETLINK_ROUTE) < 0) |
nl_close(&nl_h); | |
return 0; |
return 0; |
|
|
|
if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &lc) == 0) { |
|
nl_cache_free(lc); |
|
ret = 1; |
} |
} |
|
|
nl_cache_destroy(&link_cache); | nl_socket_free(sock); |
nl_close(&nl_h); | |
| |
if (c_mapfile) | |
read_map(c_mapfile); | |
|
|
return 1; | return ret; |
} |
} |
|
|
static void print_help(void) |
static void print_help(void) |
Line 418 static void print_help(void)
|
Line 841 static void print_help(void)
|
" Author: Thomas Graf <tgraf@suug.ch>\n" \ |
" Author: Thomas Graf <tgraf@suug.ch>\n" \ |
"\n" \ |
"\n" \ |
" Options:\n" \ |
" Options:\n" \ |
" notc Do not collect traffic control statistics\n" \ | " notc Do not collect traffic control statistics\n"); |
" map=FILE Translate handles to map using a mapfile\n" \ | |
"\n" \ | |
" Map File format:\n" \ | |
" # comment\n" \ | |
" <handle> <name> # midline comment\n"); | |
} |
} |
|
|
static void netlink_set_opts(tv_t *attrs) | static void netlink_parse_opt(const char *type, const char *value) |
{ |
{ |
while (attrs) { | if (!strcasecmp(type, "notc")) |
if (!strcasecmp(attrs->type, "notc")) | c_notc = 1; |
c_notc = 1; | else if (!strcasecmp(type, "help")) { |
else if (!strcasecmp(attrs->type, "map")) | print_help(); |
c_mapfile = attrs->value; | exit(0); |
else if (!strcasecmp(attrs->type, "help")) { | |
print_help(); | |
exit(0); | |
} | |
attrs = attrs->next; | |
} |
} |
} |
} |
|
|
static struct input_module netlink_ops = { | static struct bmon_module netlink_ops = { |
.im_name = "netlink", | .m_name = "netlink", |
.im_read = netlink_read, | .m_flags = BMON_MODULE_DEFAULT, |
.im_shutdown = netlink_shutdown, | .m_do = netlink_read, |
.im_set_opts = netlink_set_opts, | .m_shutdown = netlink_shutdown, |
.im_probe = netlink_probe, | .m_parse_opt = netlink_parse_opt, |
.im_init = netlink_do_init, | .m_probe = netlink_probe, |
| .m_init = netlink_do_init, |
}; |
}; |
|
|
static void __init netlink_init(void) |
static void __init netlink_init(void) |
{ |
{ |
register_input_module(&netlink_ops); | input_register(&netlink_ops); |
} |
} |
|
|
#endif |
#endif |