/* NETLINK_ROUTE socket. */
static struct nl_sock *rtnl_sock;
+/* Can we use RTM_GETLINK to get network device statistics? (In pre-2.6.19
+ * kernels, this was only available if wireless extensions were enabled.) */
+static bool use_netlink_stats;
+
/* This is set pretty low because we probably won't learn anything from the
* additional log messages. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
return retval;
}
-int
-netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+static int
+get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
{
struct ofpbuf request;
struct ofpbuf *reply;
struct ifinfomsg *ifi;
const struct rtnl_link_stats *rtnl_stats;
struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
- int ifindex;
int error;
- error = get_ifindex(netdev, &ifindex);
- if (error) {
- goto error;
- }
-
ofpbuf_init(&request, 0);
nl_msg_put_nlmsghdr(&request, rtnl_sock, sizeof *ifi,
RTM_GETLINK, NLM_F_REQUEST);
error = nl_sock_transact(rtnl_sock, &request, &reply);
ofpbuf_uninit(&request);
if (error) {
- goto error;
+ return error;
}
if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
rtnlgrp_link_policy,
attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
ofpbuf_delete(reply);
- error = EPROTO;
- goto error;
+ return EPROTO;
}
if (!attrs[IFLA_STATS]) {
VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats");
- error = EPROTO;
- goto error;
+ return EPROTO;
}
rtnl_stats = nl_attr_get(attrs[IFLA_STATS]);
stats->tx_window_errors = rtnl_stats->tx_window_errors;
return 0;
+}
-error:
- memset(stats, 0xff, sizeof *stats);
+static int
+get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
+{
+ static const char fn[] = "/proc/net/dev";
+ char line[1024];
+ FILE *stream;
+ int ln;
+
+ stream = fopen(fn, "r");
+ if (!stream) {
+ VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
+ return errno;
+ }
+
+ ln = 0;
+ while (fgets(line, sizeof line, stream)) {
+ if (++ln >= 3) {
+ char devname[16];
+#define X64 "%"SCNu64
+ if (sscanf(line,
+ " %15[^:]:"
+ X64 X64 X64 X64 X64 X64 X64 "%*u"
+ X64 X64 X64 X64 X64 X64 X64 "%*u",
+ devname,
+ &stats->rx_bytes,
+ &stats->rx_packets,
+ &stats->rx_errors,
+ &stats->rx_dropped,
+ &stats->rx_fifo_errors,
+ &stats->rx_frame_errors,
+ &stats->multicast,
+ &stats->tx_bytes,
+ &stats->tx_packets,
+ &stats->tx_errors,
+ &stats->tx_dropped,
+ &stats->tx_fifo_errors,
+ &stats->collisions,
+ &stats->tx_carrier_errors) != 15) {
+ VLOG_WARN_RL(&rl, "%s:%d: parse error", fn, ln);
+ } else if (!strcmp(devname, netdev_name)) {
+ stats->rx_length_errors = UINT64_MAX;
+ stats->rx_over_errors = UINT64_MAX;
+ stats->rx_crc_errors = UINT64_MAX;
+ stats->rx_missed_errors = UINT64_MAX;
+ stats->tx_aborted_errors = UINT64_MAX;
+ stats->tx_heartbeat_errors = UINT64_MAX;
+ stats->tx_window_errors = UINT64_MAX;
+ fclose(stream);
+ return 0;
+ }
+ }
+ }
+ VLOG_WARN_RL(&rl, "%s: no stats for %s", fn, netdev_name);
+ fclose(stream);
+ return ENODEV;
+}
+
+int
+netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+{
+ int error;
+
+ if (use_netlink_stats) {
+ int ifindex;
+
+ error = get_ifindex(netdev, &ifindex);
+ if (!error) {
+ error = get_stats_via_netlink(ifindex, stats);
+ }
+ } else {
+ error = get_stats_via_proc(netdev->name, stats);
+ }
+
+ if (error) {
+ memset(stats, 0xff, sizeof *stats);
+ }
return error;
}
{
static bool inited;
if (!inited) {
+ int ifindex;
int error;
inited = true;
if (error) {
ofp_fatal(error, "socket(AF_NETLINK, NETLINK_ROUTE)");
}
+
+ /* Decide on the netdev_get_stats() implementation to use. Netlink is
+ * preferable, so if that works, we'll use it. */
+ ifindex = do_get_ifindex("lo");
+ if (ifindex < 0) {
+ VLOG_WARN("failed to get ifindex for lo, "
+ "obtaining netdev stats from proc");
+ use_netlink_stats = false;
+ } else {
+ struct netdev_stats stats;
+ error = get_stats_via_netlink(ifindex, &stats);
+ if (!error) {
+ VLOG_DBG("obtaining netdev stats via rtnetlink");
+ use_netlink_stats = true;
+ } else {
+ VLOG_INFO("RTM_GETLINK failed (%s), obtaining netdev stats "
+ "via proc (you are probably running a pre-2.6.19 "
+ "kernel)", strerror(error));
+ use_netlink_stats = false;
+ }
+ }
}
}