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.
Overall, this looks very good to me.
as the property is readwrite, I think we need to check permissions.
> ---
>  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"/>
See how that is done here:
https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/src/nm-manager.c?id=24430e4b07b4c8c902adbbbf190ead43b1e92115#n4831
you are not using libnl3. Which is fine, could you just shortly say why
> +
> +Â Â Â Â <!--
> +Â Â Â Â Â Â Â Â 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>
you choose that?
> +#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": " \
any chance to print in the logging prefix the device for which this is?
Either the device's self pointer (%p), or the ifname...
Otherwise, the logging lines cannot be immidiately associated with a
device.
This seems to work only for ethernet? So, we have to properly take care
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _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;
of other device types (e.g. when enabling the statistics it should just
do nothing).
the ifname can change. Is it possible to use the ifindex instead?
> +Â Â Â Â Â Â Â 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;
Otherwise, on an ifname change, we must ensure to restart the
statistics counter.
See
https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/src/nm-manager.c?id=24430e4b07b4c8c902adbbbf190ead43b1e92115#n4831Â
https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/src/devices/nm-device.c?id=24430e4b07b4c8c902adbbbf190ead43b1e92115#n1796
this initalization is not necessary, but ok.
> +
> +Â Â Â 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;
during unrealize, the statistics must be cleared too.
nm_device_unrealize(). Maybe that should not reset the refresh_rate_ms,
but clear priv->statistics.
On realizing, you should thus check if refresh_rate_ms is enabled, and
setup priv->statistics again.
no issue, but NMDeviceStatistics seems really an implementaion detail
> Â }
> Â
> Â 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;
of NMDevice. No need to put into nm-types.h
>  typedef struct _NMDhcp4Config        NMDhcp4Config;
>  typedef struct _NMDhcp6Config        NMDhcp6Config;
>  typedef struct _NMIP4Config          NMIP4Config;
overall, IMO very good!
Thanks,
Thomas