From 382134166d901ebf8a50659da29f2f4bb4d29cb6 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 5 Mar 2009 15:09:00 -0800 Subject: [PATCH] netdev: Fall back to /proc/net/dev on kernels that don't support RTM_GETLINK. RTM_GETLINK is the best way to get network device statistics, but it was only enabled in all kernels in 2.6.19. So now test for support at startup and fall back to reading /proc/net/dev if RTM_GETLINK does not work. --- lib/netdev.c | 123 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 15 deletions(-) diff --git a/lib/netdev.c b/lib/netdev.c index 2e577962..95d3d2c3 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -130,6 +130,10 @@ static int af_inet_sock = -1; /* 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); @@ -993,22 +997,16 @@ netdev_arp_lookup(const struct netdev *netdev, 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); @@ -1018,21 +1016,19 @@ netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats) 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]); @@ -1059,9 +1055,84 @@ netdev_get_stats(const struct netdev *netdev, struct netdev_stats *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; } @@ -1134,6 +1205,7 @@ init_netdev(void) { static bool inited; if (!inited) { + int ifindex; int error; inited = true; @@ -1149,6 +1221,27 @@ init_netdev(void) 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; + } + } } } -- 2.30.2