Re: [PATCH 2/2] device: add statistics interface



On Fri, 2016-06-03 at 18:44 +0200, Alfonso Sanchez-Beato wrote:
Add statistics interface to all device instances. When active, the
properties of this interface are refreshed whenever there is network
activity for the device.

Activation is performed by changing RefreshRateMs property. If set to
zero, the interface is deactivated. If set to other value, the rest
of
the interface properties are refreshed whenever the related network
metric changes, being RefreshRateMs the minimum time between property
changes, in milliseconds.


For the netlink parts, is there any way we could extend the NMPlatform
to subscribe to stats and emit signals when things change for a device?
 There's an awful lot of netlink code in nm-device-statistics.c that
probably duplicates what the platform currently does.

The overall approach and D-Bus interface parts look OK to me.

Dan

---
 introspection/Makefile.am              |   6 +-
 introspection/nm-device-statistics.xml |  43 ++++
 libnm-core/nm-dbus-interface.h         |   1 +
 src/Makefile.am                        |   2 +
 src/devices/nm-device-private.h        |   3 +
 src/devices/nm-device-statistics.c     | 407
+++++++++++++++++++++++++++++++++
 src/devices/nm-device-statistics.h     |  33 +++
 src/devices/nm-device.c                | 102 ++++++++-
 src/devices/nm-device.h                |   4 +
 src/nm-types.h                         |   1 +
 10 files changed, 600 insertions(+), 2 deletions(-)
 create mode 100644 introspection/nm-device-statistics.xml
 create mode 100644 src/devices/nm-device-statistics.c
 create mode 100644 src/devices/nm-device-statistics.h

diff --git a/introspection/Makefile.am b/introspection/Makefile.am
index 3a62793..f72703d 100644
--- a/introspection/Makefile.am
+++ b/introspection/Makefile.am
@@ -41,6 +41,8 @@ nodist_libnmdbus_la_SOURCES = \
      nmdbus-device-modem.h \
      nmdbus-device-olpc-mesh.c \
      nmdbus-device-olpc-mesh.h \
+     nmdbus-device-statistics.c \
+     nmdbus-device-statistics.h \
      nmdbus-device-team.c \
      nmdbus-device-team.h \
      nmdbus-device-tun.c \
@@ -111,7 +113,8 @@ DBUS_INTERFACE_DOCS = \
      nmdbus-device-veth-
org.freedesktop.NetworkManager.Device.Veth.xml \
      nmdbus-settings-org.freedesktop.NetworkManager.Settings.xml
\
      nmdbus-device-ethernet-
org.freedesktop.NetworkManager.Device.Wired.xml \
-     nmdbus-ip4-config-
org.freedesktop.NetworkManager.IP4Config.xml
+     nmdbus-ip4-config-
org.freedesktop.NetworkManager.IP4Config.xml \
+     nmdbus-device-statistics-
org.freedesktop.NetworkManager.Device.Statistics.xml
 
 define _make_nmdbus_rule
 $(1): $(patsubst nmdbus-%.c,nm-%.xml,$(1))
@@ -150,6 +153,7 @@ EXTRA_DIST = \
      nm-device-macvlan.xml \
      nm-device-modem.xml \
      nm-device-olpc-mesh.xml \
+     nm-device-statistics.xml \
      nm-device-team.xml \
      nm-device-tun.xml \
      nm-device-veth.xml \
diff --git a/introspection/nm-device-statistics.xml
b/introspection/nm-device-statistics.xml
new file mode 100644
index 0000000..08a700e
--- /dev/null
+++ b/introspection/nm-device-statistics.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+  <interface
name="org.freedesktop.NetworkManager.Device.Statistics">
+
+    <!--
+        RefreshRateMs:
+
+        Rate of change of the rest of properties of this interface.
If zero, the
+        properties do not change. Othewise, the properties are
refreshed each
+        RefreshRateMs milliseconds in case the underlaying counter
has changed
+        too.
+
+        Returns: Unsigned 32-bit integer
+    -->
+    <property name="RefreshRateMs" type="u" access="readwrite"/>
+
+    <!--
+        TxBytes:
+
+        Number of transmitted bytes
+
+        Returns: Unsigned 64-bit integer
+    -->
+    <property name="TxBytes" type="t" access="read"/>
+
+    <!--
+        RxBytes:
+
+        Number of received bytes
+
+        Returns: Unsigned 64-bit integer
+    -->
+    <property name="RxBytes" type="t" access="read"/>
+
+    <!--
+        PropertiesChanged:
+         properties: A dictionary mapping property names to variant
boxed values
+    -->
+    <signal name="PropertiesChanged">
+        <arg name="properties" type="a{sv}"/>
+    </signal>
+  </interface>
+</node>
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-
interface.h
index 1e0fbe6..20eccb8 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -68,6 +68,7 @@
 #define NM_DBUS_INTERFACE_DEVICE_VXLAN      NM_DBUS_INTERFACE_DEVICE
".Vxlan"
 #define NM_DBUS_INTERFACE_DEVICE_GRE        NM_DBUS_INTERFACE_DEVICE
".Gre"
 #define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL  NM_DBUS_INTERFACE_DEVICE
".IPTunnel"
+#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE
".Statistics"
 
 #define
NM_DBUS_INTERFACE_SETTINGS        "org.freedesktop.NetworkManager.Set
tings"
 #define
NM_DBUS_PATH_SETTINGS             "/org/freedesktop/NetworkManager/Se
ttings"
diff --git a/src/Makefile.am b/src/Makefile.am
index e61e5aa..d9c8eba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -289,6 +289,8 @@ libNetworkManager_la_SOURCES = \
      devices/nm-device-generic.h \
      devices/nm-device-logging.h \
      devices/nm-device-private.h \
+     devices/nm-device-statistics.c \
+     devices/nm-device-statistics.h \
      \
      dhcp-manager/nm-dhcp-client.c \
      dhcp-manager/nm-dhcp-client.h \
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-
private.h
index 418ae2d..addeda0 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -111,6 +111,9 @@ void nm_device_ip_method_failed (NMDevice *self,
int family, NMDeviceStateReason
 
 gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char
*property, const char *value);
 
+void nm_device_set_tx_bytes (NMDevice *self, guint64 tx_bytes);
+void nm_device_set_rx_bytes (NMDevice *self, guint64 rx_bytes);
+
 #define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \
      NM_DEVICE_CLASS (klass)->connection_type = conn_type; \
      { \
diff --git a/src/devices/nm-device-statistics.c b/src/devices/nm-
device-statistics.c
new file mode 100644
index 0000000..daf67f1
--- /dev/null
+++ b/src/devices/nm-device-statistics.c
@@ -0,0 +1,407 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4
-*- */
+/* This program is free software; you can redistribute it and/or
modify
+ * it under the terms of the GNU General Public License as published
by
+ * the Free Software Foundation; either version 2, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
along
+ * with this program; if not, write to the Free Software Foundation,
Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ */
+
+#include "nm-default.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <net/if_arp.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+
+#include "nm-device-private.h"
+#include "nm-device-statistics.h"
+
+#define _NMLOG_DOMAIN        LOGD_DEVICE
+#define _NMLOG_PREFIX_NAME   "device (stats)"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
+struct rtnl_request {
+     struct nlmsghdr hdr;
+     struct rtgenmsg msg;
+};
+
+#define RTNL_REQUEST_SIZE  (sizeof (struct nlmsghdr) + sizeof
(struct rtgenmsg))
+#define SOCK_RX_BUFF_SIZE 4096
+
+struct _NMDeviceStatistics {
+     NMDevice *device;
+     char *iface;
+     GIOChannel *channel;
+     guint channel_watch;
+     guint stats_update_id;
+     gboolean req_pending;
+     guint32 request_seq;
+     unsigned char buf[SOCK_RX_BUFF_SIZE];
+};
+
+static const char *
+type_to_string (uint16_t type)
+{
+     switch (type) {
+     case NLMSG_NOOP:
+             return "NOOP";
+     case NLMSG_ERROR:
+             return "ERROR";
+     case NLMSG_DONE:
+             return "DONE";
+     case NLMSG_OVERRUN:
+             return "OVERRUN";
+     case RTM_GETLINK:
+             return "GETLINK";
+     case RTM_NEWLINK:
+             return "NEWLINK";
+     case RTM_DELLINK:
+             return "DELLINK";
+     case RTM_GETADDR:
+             return "GETADDR";
+     case RTM_NEWADDR:
+             return "NEWADDR";
+     case RTM_DELADDR:
+             return "DELADDR";
+     case RTM_GETROUTE:
+             return "GETROUTE";
+     case RTM_NEWROUTE:
+             return "NEWROUTE";
+     case RTM_DELROUTE:
+             return "DELROUTE";
+     case RTM_NEWNDUSEROPT:
+             return "NEWNDUSEROPT";
+     default:
+             return "UNKNOWN";
+     }
+}
+
+static const char *
+operstate_to_str (unsigned char operstate)
+{
+     switch (operstate) {
+     case IF_OPER_UNKNOWN:
+             return "UNKNOWN";
+     case IF_OPER_NOTPRESENT:
+             return "NOT-PRESENT";
+     case IF_OPER_DOWN:
+             return "DOWN";
+     case IF_OPER_LOWERLAYERDOWN:
+             return "LOWER-LAYER-DOWN";
+     case IF_OPER_TESTING:
+             return "TESTING";
+     case IF_OPER_DORMANT:
+             return "DORMANT";
+     case IF_OPER_UP:
+             return "UP";
+     default:
+             return "";
+     }
+}
+
+static gboolean
+extract_link (struct ifinfomsg *msg, int bytes,
+              struct ether_addr *address, const char **ifname,
+              unsigned int *mtu, unsigned char *operstate,
+              struct rtnl_link_stats *stats)
+{
+     struct rtattr *attr;
+
+     for (attr = IFLA_RTA (msg); RTA_OK (attr, bytes);
+          attr = RTA_NEXT (attr, bytes)) {
+
+             switch (attr->rta_type) {
+             case IFLA_ADDRESS:
+                     if (address)
+                             memcpy (address, RTA_DATA (attr),
ETH_ALEN);
+                     break;
+             case IFLA_IFNAME:
+                     if (ifname)
+                             *ifname = RTA_DATA (attr);
+                     break;
+             case IFLA_MTU:
+                     if (mtu)
+                             *mtu = *(unsigned int *) RTA_DATA
(attr);
+                     break;
+             case IFLA_STATS:
+                     if (stats)
+                             memcpy (stats, RTA_DATA (attr),
+                                     sizeof (struct
rtnl_link_stats));
+                     break;
+             case IFLA_OPERSTATE:
+                     if (operstate)
+                             *operstate = *(unsigned char *)
RTA_DATA (attr);
+                     break;
+             case IFLA_LINKMODE:
+                     break;
+             case IFLA_WIRELESS:
+                     return FALSE;
+             }
+     }
+
+     return TRUE;
+}
+
+static void
+process_newlink (NMDeviceStatistics *self, unsigned short type, int
index,
+                 unsigned flags, unsigned change, struct ifinfomsg
*msg,
+                 int bytes)
+{
+     struct ether_addr address = { { 0, 0, 0, 0, 0, 0 } };
+     struct rtnl_link_stats stats = { 0 };
+     unsigned char operstate = 0xFF;
+     const char *ifname = NULL;
+     unsigned int mtu = 0;
+     char hw_addr[3 * sizeof (address)];
+
+     if (!extract_link (msg, bytes, &address, &ifname, &mtu,
&operstate, &stats))
+             return;
+
+     if (g_strcmp0 (ifname, self->iface) != 0)
+             return;
+
+     snprintf(hw_addr, sizeof (hw_addr),
"%02X:%02X:%02X:%02X:%02X:%02X",
+              address.ether_addr_octet[0],
+              address.ether_addr_octet[1],
+              address.ether_addr_octet[2],
+              address.ether_addr_octet[3],
+              address.ether_addr_octet[4],
+              address.ether_addr_octet[5]);
+
+     if (flags & IFF_SLAVE) {
+             _LOGD ("%s {newlink} ignoring slave, index %d
address %s",
+                    ifname, index, hw_addr);
+             return;
+     }
+
+     _LOGD ("%s {newlink} index %d address %s mtu %u operstate %u
<%s>",
+            ifname, index, hw_addr, mtu, operstate,
operstate_to_str (operstate));
+     _LOGD ("%s {RX} %u packets %u bytes", ifname,
+            stats.rx_packets, stats.rx_bytes);
+     _LOGD ("%s {TX} %u packets %u bytes", ifname,
+            stats.tx_packets, stats.tx_bytes);
+
+     nm_device_set_tx_bytes (self->device, stats.tx_bytes);
+     nm_device_set_rx_bytes (self->device, stats.rx_bytes);
+}
+
+static void
+rtnl_newlink (NMDeviceStatistics *self, struct nlmsghdr *hdr)
+{
+     struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA
(hdr);
+
+     if (hdr->nlmsg_type == IFLA_WIRELESS)
+             _LOGW ("Obsolete WEXT WiFi driver detected");
+
+     process_newlink (self, msg->ifi_type, msg->ifi_index, msg-
ifi_flags,
+                      msg->ifi_change, msg, IFA_PAYLOAD (hdr));
+}
+
+static void
+rtnl_message (NMDeviceStatistics *self, unsigned char *buf, size_t
len)
+{
+     while (len > 0) {
+             struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
+             struct nlmsgerr *err;
+
+             if (!NLMSG_OK (hdr, len))
+                     break;
+
+             switch (hdr->nlmsg_type) {
+             case NLMSG_NOOP:
+             case NLMSG_OVERRUN:
+                     return;
+             case NLMSG_DONE:
+                     if (hdr->nlmsg_seq == self->request_seq - 1)
+                             self->req_pending = FALSE;
+                     return;
+             case NLMSG_ERROR:
+                     err = NLMSG_DATA (hdr);
+                     return;
+             case RTM_NEWLINK:
+                     rtnl_newlink (self, hdr);
+                     break;
+             case RTM_DELLINK:
+             case RTM_NEWADDR:
+             case RTM_DELADDR:
+             case RTM_NEWROUTE:
+             case RTM_DELROUTE:
+             case RTM_NEWNDUSEROPT:
+                     break;
+             }
+
+             len -= hdr->nlmsg_len;
+             buf += hdr->nlmsg_len;
+     }
+}
+
+static gboolean
+netlink_event (GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+     struct sockaddr_nl nladdr = { 0 };
+     socklen_t addr_len = sizeof (nladdr);
+     ssize_t status;
+     int fd;
+     NMDeviceStatistics *self = data;
+
+     if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+             _LOGE ("netlink socket error %d", errno);
+             return FALSE;
+     }
+
+     fd = g_io_channel_unix_get_fd (chan);
+
+     status = recvfrom (fd, self->buf, sizeof (self->buf), 0,
+                        (struct sockaddr *) &nladdr, &addr_len);
+     if (status < 0) {
+             if (errno == EINTR || errno == EAGAIN)
+                     return TRUE;
+
+             _LOGE ("error %d on receiving from netlink socket",
errno);
+             return FALSE;
+     }
+
+     /* EOF, remove callback */
+     if (status == 0)
+             return FALSE;
+
+     /* not sent by kernel, ignore */
+     if (nladdr.nl_pid != 0)
+             return TRUE;
+
+     rtnl_message (self, self->buf, status);
+
+     return TRUE;
+}
+
+static int
+send_getlink (NMDeviceStatistics *self)
+{
+     struct rtnl_request req = { 0 };
+     struct sockaddr_nl addr = { 0 };
+     int sk;
+
+     req.hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+     req.hdr.nlmsg_type = RTM_GETLINK;
+     req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+     req.hdr.nlmsg_pid = 0;
+     req.hdr.nlmsg_seq = self->request_seq++;
+     req.msg.rtgen_family = AF_UNSPEC;
+
+     _LOGD ("Sending %s len %d type %d flags 0x%04x seq %d",
+            type_to_string (req.hdr.nlmsg_type),
+            req.hdr.nlmsg_len, req.hdr.nlmsg_type,
+            req.hdr.nlmsg_flags, req.hdr.nlmsg_seq);
+
+     sk = g_io_channel_unix_get_fd(self->channel);
+
+     addr.nl_family = AF_NETLINK;
+
+     self->req_pending = TRUE;
+
+     return sendto (sk, &req, req.hdr.nlmsg_len, 0,
+                    (struct sockaddr *) &addr, sizeof (addr));
+}
+
+static gboolean
+update_stats (gpointer user_data)
+{
+     NMDeviceStatistics *self = user_data;
+
+     if (self->req_pending) {
+             _LOGD ("no response yet for pending netlink
request");
+             return TRUE;
+     }
+
+     send_getlink (self);
+     return TRUE;
+}
+
+/********************************************/
+
+NMDeviceStatistics *
+nm_device_statistics_create (NMDevice *device, const char *iface,
unsigned rate_ms)
+{
+     NMDeviceStatistics *self;
+     struct sockaddr_nl addr = { 0 };
+     int sk;
+     GIOChannel *channel = NULL;
+     guint channel_watch = 0;
+
+     self = g_malloc0 (sizeof (*self));
+
+     sk = socket (PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_ROUTE);
+     if (sk < 0) {
+             _LOGE ("Cannot create netlink socket: %d", errno);
+             goto error;
+     }
+
+     addr.nl_family = AF_NETLINK;
+     addr.nl_groups = RTMGRP_LINK;
+
+     if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) < 0)
{
+             close (sk);
+             _LOGE ("Cannot bind to netlink socket: %d", errno);
+             goto error;
+     }
+
+     channel = g_io_channel_unix_new (sk);
+
+     g_io_channel_set_close_on_unref (channel, TRUE);
+     g_io_channel_set_encoding (channel, NULL, NULL);
+     g_io_channel_set_buffered (channel, FALSE);
+
+     channel_watch =
+             g_io_add_watch (channel,
+                             G_IO_IN | G_IO_NVAL | G_IO_HUP |
G_IO_ERR,
+                             netlink_event, self);
+
+     self->device = device;
+     self->iface = g_strdup (iface);
+     self->channel = channel;
+     self->channel_watch = channel_watch;
+     self->stats_update_id = g_timeout_add (rate_ms,
update_stats, self);
+
+     return self;
+
+error:
+     g_free (self);
+     return NULL;
+}
+
+void
+nm_device_statistics_remove (NMDeviceStatistics *self)
+{
+     g_source_remove (self->stats_update_id);
+     g_source_remove (self->channel_watch);
+     g_io_channel_unref (self->channel);
+     g_free (self->iface);
+     g_free (self);
+}
diff --git a/src/devices/nm-device-statistics.h b/src/devices/nm-
device-statistics.h
new file mode 100644
index 0000000..496cecc
--- /dev/null
+++ b/src/devices/nm-device-statistics.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4
-*- */
+/* This program is free software; you can redistribute it and/or
modify
+ * it under the terms of the GNU General Public License as published
by
+ * the Free Software Foundation; either version 2, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
along
+ * with this program; if not, write to the Free Software Foundation,
Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Canonical Ltd
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_STATISTICS_H__
+#define __NETWORKMANAGER_DEVICE_STATISTICS_H__
+
+#include <stdlib.h>
+
+#include "nm-default.h"
+
+typedef struct _NMDeviceStatistics NMDeviceStatistics;
+
+NMDeviceStatistics *
+nm_device_statistics_create (NMDevice *device, const char *iface,
unsigned rate_ms);
+
+void nm_device_statistics_remove (NMDeviceStatistics *self);
+
+#endif /* __NETWORKMANAGER_DEVICE_STATISTICS_H__ */
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index c066b31..f1a7f77 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -66,11 +66,13 @@
 #include "sd-ipv4ll.h"
 #include "nm-audit-manager.h"
 #include "nm-arping-manager.h"
+#include "nm-device-statistics.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
 
 #include "nmdbus-device.h"
+#include "nmdbus-device-statistics.h"
 
 G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device,
NM_TYPE_EXPORTED_OBJECT)
 
@@ -137,6 +139,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
      PROP_LLDP_NEIGHBORS,
      PROP_REAL,
      PROP_SLAVES,
+     PROP_REFRESH_RATE_MS,
+     PROP_TX_BYTES,
+     PROP_RX_BYTES,
 );
 
 #define DEFAULT_AUTOCONNECT TRUE
@@ -394,6 +399,13 @@ typedef struct _NMDevicePrivate {
      NMLldpListener *lldp_listener;
 
      guint check_delete_unrealized_id;
+
+     guint32 refresh_rate_ms;
+     guint64 tx_bytes;
+     guint64 rx_bytes;
+
+     NMDeviceStatistics *statistics;
+
 } NMDevicePrivate;
 
 static gboolean nm_device_set_ip4_config (NMDevice *self,
@@ -732,6 +744,36 @@ nm_device_set_ip_iface (NMDevice *self, const
char *iface)
      g_free (old_ip_iface);
 }
 
+void
+nm_device_set_tx_bytes (NMDevice *self, guint64 tx_bytes)
+{
+     NMDevicePrivate *priv;
+
+     g_return_if_fail (NM_IS_DEVICE (self));
+
+     priv = NM_DEVICE_GET_PRIVATE (self);
+     if (tx_bytes == priv->tx_bytes)
+             return;
+
+     priv->tx_bytes = tx_bytes;
+     _notify (self, PROP_TX_BYTES);
+}
+
+void
+nm_device_set_rx_bytes (NMDevice *self, guint64 rx_bytes)
+{
+     NMDevicePrivate *priv;
+
+     g_return_if_fail (NM_IS_DEVICE (self));
+
+     priv = NM_DEVICE_GET_PRIVATE (self);
+     if (rx_bytes == priv->rx_bytes)
+             return;
+
+     priv->rx_bytes = rx_bytes;
+     _notify (self, PROP_RX_BYTES);
+}
+
 static gboolean
 get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId
*out_iid)
 {
@@ -11549,6 +11591,11 @@ nm_device_init (NMDevice *self)
 
      priv->v4_commit_first_time = TRUE;
      priv->v6_commit_first_time = TRUE;
+
+     priv->refresh_rate_ms = 0;
+     priv->tx_bytes = 0;
+     priv->rx_bytes = 0;
+     priv->statistics = NULL;
 }
 
 static GObject*
@@ -11685,6 +11732,11 @@ dispose (GObject *object)
              g_clear_object (&priv->lldp_listener);
      }
 
+     if (priv->statistics) {
+             nm_device_statistics_remove (priv->statistics);
+             priv->statistics = NULL;
+     }
+
      G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
 
      if (nm_clear_g_source (&priv->queued_state.id)) {
@@ -11737,7 +11789,7 @@ set_property (GObject *object, guint prop_id,
      NMDevice *self = NM_DEVICE (object);
      NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
      const char *hw_addr, *p;
-     guint count;
+     guint count, refresh_rate_ms;
 
      switch (prop_id) {
      case PROP_UDI:
@@ -11842,6 +11894,24 @@ set_property (GObject *object, guint
prop_id,
                      priv->hw_addr = NULL;
              }
              break;
+     case PROP_REFRESH_RATE_MS:
+             refresh_rate_ms = g_value_get_uint (value);
+             if (priv->refresh_rate_ms == refresh_rate_ms)
+                     break;
+
+             priv->refresh_rate_ms = g_value_get_uint (value);
+             _LOGI (LOGD_DEVICE, "statistics refresh rate set to
%u ms", priv->refresh_rate_ms);
+
+             if (priv->statistics) {
+                     nm_device_statistics_remove (priv-
statistics);
+                     priv->statistics = NULL;
+             }
+             if (priv->refresh_rate_ms)
+                     priv->statistics =
+                             nm_device_statistics_create (self,
+                                                          nm_devi
ce_get_ip_iface (self),
+                                                          priv-
refresh_rate_ms);
+             break;
      default:
              G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
pspec);
              break;
@@ -12000,6 +12070,15 @@ get_property (GObject *object, guint
prop_id,
              g_value_take_boxed (value, slave_list);
              break;
      }
+     case PROP_REFRESH_RATE_MS:
+             g_value_set_uint (value, priv->refresh_rate_ms);
+             break;
+     case PROP_TX_BYTES:
+             g_value_set_uint64 (value, priv->tx_bytes);
+             break;
+     case PROP_RX_BYTES:
+             g_value_set_uint64 (value, priv->rx_bytes);
+             break;
      default:
              G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
pspec);
              break;
@@ -12243,6 +12322,23 @@ nm_device_class_init (NMDeviceClass *klass)
                              G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS);
 
+     /* Statistics */
+     obj_properties[PROP_REFRESH_RATE_MS] =
+             g_param_spec_uint
(NM_DEVICE_STATISTICS_REFRESH_RATE_MS, "", "",
+                                0, UINT_MAX, 0,
+                                G_PARAM_READWRITE |
+                                G_PARAM_STATIC_STRINGS);
+     obj_properties[PROP_TX_BYTES] =
+             g_param_spec_uint64 (NM_DEVICE_STATISTICS_TX_BYTES,
"", "",
+                                  0, UINT64_MAX, 0,
+                                  G_PARAM_READABLE |
+                                  G_PARAM_STATIC_STRINGS);
+     obj_properties[PROP_RX_BYTES] =
+             g_param_spec_uint64 (NM_DEVICE_STATISTICS_RX_BYTES,
"", "",
+                                  0, UINT64_MAX, 0,
+                                  G_PARAM_READABLE |
+                                  G_PARAM_STATIC_STRINGS);
+
      g_object_class_install_properties (object_class,
_PROPERTY_ENUMS_LAST, obj_properties);
 
      /* Signals */
@@ -12313,4 +12409,8 @@ nm_device_class_init (NMDeviceClass *klass)
                                              "Disconnect",
impl_device_disconnect,
                                              "Delete",
impl_device_delete,
                                              NULL);
+
+     nm_exported_object_class_add_interface
(NM_EXPORTED_OBJECT_CLASS (klass),
+                                             NMDBUS_TYPE_DEVICE_S
TATISTICS_SKELETON,
+                                             NULL);
 }
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 5f1a168..8bc6e16 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -83,6 +83,10 @@
 #define NM_DEVICE_STATE_CHANGED         "state-changed"
 #define NM_DEVICE_LINK_INITIALIZED      "link-initialized"
 
+#define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms"
+#define NM_DEVICE_STATISTICS_TX_BYTES        "tx-bytes"
+#define NM_DEVICE_STATISTICS_RX_BYTES        "rx-bytes"
+
 G_BEGIN_DECLS
 
 #define NM_TYPE_DEVICE            (nm_device_get_type ())
diff --git a/src/nm-types.h b/src/nm-types.h
index fa70372..637bc86 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -40,6 +40,7 @@ typedef struct _NMConnectionProvider
NMConnectionProvider;
 typedef struct _NMConnectivity       NMConnectivity;
 typedef struct _NMDefaultRouteManager NMDefaultRouteManager;
 typedef struct _NMDevice             NMDevice;
+typedef struct _NMDeviceStatistics  NMDeviceStatistics;
 typedef struct _NMDhcp4Config        NMDhcp4Config;
 typedef struct _NMDhcp6Config        NMDhcp6Config;
 typedef struct _NMIP4Config          NMIP4Config;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]