Linux now uses stats using netlink - if that isn't available for some reason, we fallback to the /proc stats. If netlink stats is used, link state info is available.
This commit is contained in:
parent
31c7f08e7c
commit
9cac3cd918
@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#ifdef Q_OS_LINUX
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QTime>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@ -31,12 +33,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
QList<LinuxPort*> LinuxPort::allPorts_;
|
||||
LinuxPort::StatsMonitor *LinuxPort::monitor_;
|
||||
|
||||
LinuxPort::LinuxPort(int id, const char *device)
|
||||
: PcapPort(id, device)
|
||||
{
|
||||
isPromisc_ = true;
|
||||
clearPromisc_ = false;
|
||||
|
||||
// We don't need per port Rx/Tx monitors for Linux
|
||||
@ -93,15 +98,17 @@ LinuxPort::~LinuxPort()
|
||||
|
||||
void LinuxPort::init()
|
||||
{
|
||||
// TODO: Update Notes with Promisc/Non-Promisc
|
||||
|
||||
if (!monitor_->isRunning())
|
||||
monitor_->start();
|
||||
|
||||
monitor_->waitForSetupFinished();
|
||||
|
||||
if (!isPromisc_)
|
||||
addNote("Non Promiscuous Mode");
|
||||
}
|
||||
|
||||
OstProto::LinkState LinuxPort::linkState()
|
||||
{
|
||||
// TODO
|
||||
return linkState_;
|
||||
}
|
||||
|
||||
@ -121,12 +128,29 @@ LinuxPort::StatsMonitor::StatsMonitor()
|
||||
: QThread()
|
||||
{
|
||||
stop_ = false;
|
||||
setupDone_ = false;
|
||||
ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
Q_ASSERT(ioctlSocket_ >= 0);
|
||||
}
|
||||
|
||||
LinuxPort::StatsMonitor::~StatsMonitor()
|
||||
{
|
||||
close(ioctlSocket_);
|
||||
}
|
||||
|
||||
void LinuxPort::StatsMonitor::run()
|
||||
{
|
||||
if (netlinkStats() < 0)
|
||||
{
|
||||
qDebug("netlink stats not available - using /proc stats");
|
||||
procStats();
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPort::StatsMonitor::procStats()
|
||||
{
|
||||
PortStats **portStats;
|
||||
int fd, sd;
|
||||
int fd;
|
||||
QByteArray buf;
|
||||
int len;
|
||||
char *p, *end;
|
||||
@ -136,7 +160,6 @@ void LinuxPort::StatsMonitor::run()
|
||||
"%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n",
|
||||
};
|
||||
const char *fmt;
|
||||
struct ifreq ifr;
|
||||
|
||||
//
|
||||
// We first setup stuff before we start polling for stats
|
||||
@ -184,10 +207,6 @@ void LinuxPort::StatsMonitor::run()
|
||||
portStats = (PortStats**) calloc(count, sizeof(PortStats));
|
||||
Q_ASSERT(portStats != NULL);
|
||||
|
||||
sd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
Q_ASSERT(sd >= 0);
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
//
|
||||
// Populate the port stats array
|
||||
//
|
||||
@ -224,28 +243,11 @@ void LinuxPort::StatsMonitor::run()
|
||||
{
|
||||
portStats[index] = &(port->stats_);
|
||||
|
||||
// Set promisc mode, if not already set
|
||||
strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name));
|
||||
if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1)
|
||||
{
|
||||
if ((ifr.ifr_flags & IFF_PROMISC) == 0)
|
||||
{
|
||||
ifr.ifr_flags |= IFF_PROMISC;
|
||||
if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1)
|
||||
port->clearPromisc_ = true;
|
||||
else
|
||||
{
|
||||
qDebug("%s: failed to set promisc; "
|
||||
"SIOCSIFFLAGS failed (%s)",
|
||||
port->name(), strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (setPromisc(port->name()))
|
||||
port->clearPromisc_ = true;
|
||||
else
|
||||
{
|
||||
qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)",
|
||||
port->name(), strerror(errno));
|
||||
}
|
||||
port->isPromisc_ = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -260,9 +262,8 @@ void LinuxPort::StatsMonitor::run()
|
||||
}
|
||||
Q_ASSERT(index == count);
|
||||
|
||||
close(sd);
|
||||
|
||||
qDebug("stats for %d ports setup", count);
|
||||
setupDone_ = true;
|
||||
|
||||
//
|
||||
// We are all set - Let's start polling for stats!
|
||||
@ -353,8 +354,379 @@ void LinuxPort::StatsMonitor::run()
|
||||
free(portStats);
|
||||
}
|
||||
|
||||
int LinuxPort::StatsMonitor::netlinkStats()
|
||||
{
|
||||
QHash<uint, PortStats*> portStats;
|
||||
QHash<uint, OstProto::LinkState*> linkState;
|
||||
int fd;
|
||||
struct sockaddr_nl local;
|
||||
struct sockaddr_nl kernel;
|
||||
QByteArray buf;
|
||||
int len, count;
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct rtgenmsg rtg;
|
||||
} ifListReq;
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
struct nlmsghdr *nlm;
|
||||
bool done = false;
|
||||
|
||||
//
|
||||
// We first setup stuff before we start polling for stats
|
||||
//
|
||||
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (fd < 0)
|
||||
{
|
||||
qWarning("Unable to open netlink socket (errno %d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
|
||||
if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0)
|
||||
{
|
||||
qWarning("Unable to bind netlink socket (errno %d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&ifListReq, 0, sizeof(ifListReq));
|
||||
ifListReq.nlh.nlmsg_len = sizeof(ifListReq);
|
||||
ifListReq.nlh.nlmsg_type = RTM_GETLINK;
|
||||
ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
ifListReq.nlh.nlmsg_pid = 0;
|
||||
ifListReq.rtg.rtgen_family = AF_PACKET;
|
||||
|
||||
buf.fill('\0', 1024);
|
||||
|
||||
msg.msg_name = &kernel;
|
||||
msg.msg_namelen = sizeof(kernel);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags);
|
||||
|
||||
if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0)
|
||||
{
|
||||
qWarning("Unable to send GETLINK request (errno %d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find required size of buffer and resize accordingly
|
||||
while (1)
|
||||
{
|
||||
iov.iov_base = buf.data();
|
||||
iov.iov_len = buf.size();
|
||||
msg.msg_flags = 0;
|
||||
|
||||
// Peek at reply to check buffer size required
|
||||
len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
|
||||
qWarning("netlink recv error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
else if (len == 0)
|
||||
{
|
||||
qWarning("netlink closed the socket on my face!");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (msg.msg_flags & MSG_TRUNC)
|
||||
{
|
||||
if (len == buf.size()) // Older Kernel returns truncated size
|
||||
{
|
||||
qDebug("netlink buffer size %d not enough", buf.size());
|
||||
qDebug("retrying with double the size");
|
||||
// Double the size and retry
|
||||
buf.resize(buf.size()*2);
|
||||
continue;
|
||||
}
|
||||
else // Newer Kernel returns actual size required
|
||||
{
|
||||
qDebug("netlink required buffer size = %d", len);
|
||||
buf.resize(len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
qDebug("buffer size %d enough for netlink", buf.size());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
_retry:
|
||||
msg.msg_flags = 0;
|
||||
|
||||
// Actually receive the reply now
|
||||
len = recvmsg(fd, &msg, 0);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
goto _retry;
|
||||
qWarning("netlink recv error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
else if (len == 0)
|
||||
{
|
||||
qWarning("netlink socket closed unexpectedly");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// Populate the port stats hash table
|
||||
//
|
||||
nlm = (struct nlmsghdr*) buf.data();
|
||||
while (NLMSG_OK(nlm, (uint)len))
|
||||
{
|
||||
struct ifinfomsg *ifi;
|
||||
struct rtattr *rta;
|
||||
int rtaLen;
|
||||
char ifname[64] = "";
|
||||
|
||||
if (nlm->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlm->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm);
|
||||
qDebug("RTNETLINK error %d", err->error);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK);
|
||||
|
||||
ifi = (struct ifinfomsg*) NLMSG_DATA(nlm);
|
||||
rta = IFLA_RTA(ifi);
|
||||
rtaLen = len - NLMSG_LENGTH(sizeof(*ifi));
|
||||
while (RTA_OK(rta, rtaLen))
|
||||
{
|
||||
if (rta->rta_type == IFLA_IFNAME)
|
||||
{
|
||||
strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta));
|
||||
ifname[RTA_PAYLOAD(rta)] = 0;
|
||||
break;
|
||||
}
|
||||
rta = RTA_NEXT(rta, rtaLen);
|
||||
}
|
||||
|
||||
qDebug("if: %s(%d)", ifname, ifi->ifi_index);
|
||||
foreach(LinuxPort* port, allPorts_)
|
||||
{
|
||||
if (strcmp(port->name(), ifname) == 0)
|
||||
{
|
||||
portStats[uint(ifi->ifi_index)] = &(port->stats_);
|
||||
linkState[uint(ifi->ifi_index)] = &(port->linkState_);
|
||||
|
||||
if (setPromisc(port->name()))
|
||||
port->clearPromisc_ = true;
|
||||
else
|
||||
port->isPromisc_ = false;
|
||||
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
nlm = NLMSG_NEXT(nlm, len);
|
||||
}
|
||||
|
||||
if (!done)
|
||||
goto _retry;
|
||||
|
||||
qDebug("port count = %d\n", count);
|
||||
if (count <= 0)
|
||||
{
|
||||
qWarning("no ports in RTNETLINK GET_LINK - no stats will be available");
|
||||
return - 1;
|
||||
}
|
||||
|
||||
qDebug("stats for %d ports setup", count);
|
||||
setupDone_ = true;
|
||||
|
||||
//
|
||||
// We are all set - Let's start polling for stats!
|
||||
//
|
||||
while (!stop_)
|
||||
{
|
||||
if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0)
|
||||
{
|
||||
qWarning("Unable to send GETLINK request (errno %d)", errno);
|
||||
goto _try_later;
|
||||
}
|
||||
|
||||
done = false;
|
||||
|
||||
_retry_recv:
|
||||
msg.msg_flags = 0;
|
||||
len = recvmsg(fd, &msg, 0);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
goto _retry_recv;
|
||||
qWarning("netlink recv error %d", errno);
|
||||
break;
|
||||
}
|
||||
else if (len == 0)
|
||||
{
|
||||
qWarning("netlink socket closed unexpectedly");
|
||||
break;
|
||||
}
|
||||
|
||||
nlm = (struct nlmsghdr*) buf.data();
|
||||
while (NLMSG_OK(nlm, (uint)len))
|
||||
{
|
||||
struct ifinfomsg *ifi;
|
||||
struct rtattr *rta;
|
||||
int rtaLen;
|
||||
|
||||
if (nlm->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlm->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm);
|
||||
qDebug("RTNETLINK error: %s", strerror(-err->error));
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK);
|
||||
|
||||
ifi = (struct ifinfomsg*) NLMSG_DATA(nlm);
|
||||
rta = IFLA_RTA(ifi);
|
||||
rtaLen = len - NLMSG_LENGTH(sizeof(*ifi));
|
||||
while (RTA_OK(rta, rtaLen))
|
||||
{
|
||||
// TODO: IFLA_STATS64
|
||||
if (rta->rta_type == IFLA_STATS)
|
||||
{
|
||||
struct rtnl_link_stats *rtnlStats =
|
||||
(struct rtnl_link_stats*) RTA_DATA(rta);
|
||||
AbstractPort::PortStats *stats = portStats[ifi->ifi_index];
|
||||
OstProto::LinkState *state = linkState[ifi->ifi_index];
|
||||
|
||||
if (!stats)
|
||||
break;
|
||||
|
||||
stats->rxPps = (rtnlStats->rx_packets - stats->rxPkts)
|
||||
/ kRefreshFreq_;
|
||||
stats->rxBps = (rtnlStats->rx_bytes - stats->rxBytes)
|
||||
/ kRefreshFreq_;
|
||||
stats->rxPkts = rtnlStats->rx_packets;
|
||||
stats->rxBytes = rtnlStats->rx_bytes;
|
||||
stats->txPps = (rtnlStats->tx_packets - stats->txPkts)
|
||||
/ kRefreshFreq_;
|
||||
stats->txBps = (rtnlStats->tx_bytes - stats->txBytes)
|
||||
/ kRefreshFreq_;
|
||||
stats->txPkts = rtnlStats->tx_packets;
|
||||
stats->txBytes = rtnlStats->tx_bytes;
|
||||
|
||||
// TODO: export detailed error stats
|
||||
stats->rxDrops = rtnlStats->rx_dropped
|
||||
+ rtnlStats->rx_missed_errors;
|
||||
stats->rxErrors = rtnlStats->rx_errors;
|
||||
stats->rxFifoErrors = rtnlStats->rx_fifo_errors;
|
||||
stats->rxFrameErrors = rtnlStats->rx_crc_errors
|
||||
+ rtnlStats->rx_length_errors
|
||||
+ rtnlStats->rx_over_errors
|
||||
+ rtnlStats->rx_frame_errors;
|
||||
|
||||
Q_ASSERT(state);
|
||||
*state = ifi->ifi_flags & IFF_RUNNING ?
|
||||
OstProto::LinkStateUp : OstProto::LinkStateDown;
|
||||
|
||||
break;
|
||||
}
|
||||
rta = RTA_NEXT(rta, rtaLen);
|
||||
}
|
||||
nlm = NLMSG_NEXT(nlm, len);
|
||||
}
|
||||
|
||||
if (!done)
|
||||
goto _retry_recv;
|
||||
|
||||
_try_later:
|
||||
QThread::sleep(kRefreshFreq_);
|
||||
}
|
||||
|
||||
portStats.clear();
|
||||
linkState.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LinuxPort::StatsMonitor::setPromisc(const char * portName)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name));
|
||||
|
||||
if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1)
|
||||
{
|
||||
if ((ifr.ifr_flags & IFF_PROMISC) == 0)
|
||||
{
|
||||
ifr.ifr_flags |= IFF_PROMISC;
|
||||
if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("%s: failed to set promisc; "
|
||||
"SIOCSIFFLAGS failed (%s)",
|
||||
portName, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)",
|
||||
portName, strerror(errno));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LinuxPort::StatsMonitor::stop()
|
||||
{
|
||||
stop_ = true;
|
||||
}
|
||||
|
||||
bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs)
|
||||
{
|
||||
QTime t;
|
||||
|
||||
t.start();
|
||||
while (!setupDone_)
|
||||
{
|
||||
if (t.elapsed() > msecs)
|
||||
return false;
|
||||
|
||||
QThread::msleep(10);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@ -43,13 +43,22 @@ protected:
|
||||
{
|
||||
public:
|
||||
StatsMonitor();
|
||||
~StatsMonitor();
|
||||
void run();
|
||||
void stop();
|
||||
bool waitForSetupFinished(int msecs = 10000);
|
||||
private:
|
||||
int netlinkStats();
|
||||
void procStats();
|
||||
int setPromisc(const char* portName);
|
||||
|
||||
static const int kRefreshFreq_ = 1; // in seconds
|
||||
bool stop_;
|
||||
bool setupDone_;
|
||||
int ioctlSocket_;
|
||||
};
|
||||
|
||||
bool isPromisc_;
|
||||
bool clearPromisc_;
|
||||
static QList<LinuxPort*> allPorts_;
|
||||
static StatsMonitor *monitor_; // rx/tx stats for ALL ports
|
||||
|
Loading…
Reference in New Issue
Block a user