diff --git a/introspection/Makefile.am b/introspection/Makefile.am index d4762637a..27b54e5d0 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -43,6 +43,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 \ @@ -114,7 +116,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)) @@ -154,6 +157,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 000000000..5d23bf306 --- /dev/null +++ b/introspection/nm-device-statistics.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index c7ce11002..e5b1b3d6b 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.Settings" #define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings" diff --git a/src/Makefile.am b/src/Makefile.am index c460caff6..35bcf93ff 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -331,6 +331,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 7381fd930..58216e175 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -114,6 +114,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 000000000..b5d4197cf --- /dev/null +++ b/src/devices/nm-device-statistics.c @@ -0,0 +1,99 @@ +/* -*- 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 + +#include "nm-device-statistics.h" +#include "nm-device-private.h" +#include "nm-utils.h" +#include "nm-platform.h" + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG(level, ...) \ + nm_log_obj ((level), _NMLOG_DOMAIN, (self->device), "device-stats", \ + "(%s): " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + nm_device_get_iface (self->device) ?: "(none)" \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +struct _NMDeviceStatistics { + NMDevice *device; + guint stats_update_id; +}; + +static gboolean +update_stats (gpointer user_data) +{ + NMDeviceStatistics *self = user_data; + guint64 rx_packets; + guint64 rx_bytes; + guint64 tx_packets; + guint64 tx_bytes; + int ifindex; + + ifindex = nm_device_get_ip_ifindex (self->device); + + if (nm_platform_link_get_stats (NM_PLATFORM_GET, ifindex, + &rx_packets, &rx_bytes, + &tx_packets, &tx_bytes)) { + _LOGT ("{RX} %"PRIu64" packets %"PRIu64" bytes {TX} %"PRIu64" packets %"PRIu64" bytes", + rx_packets, rx_bytes, tx_packets, tx_bytes); + + nm_device_set_tx_bytes (self->device, tx_bytes); + nm_device_set_rx_bytes (self->device, rx_bytes); + } else { + _LOGE ("error no stats available"); + } + + /* Keep polling */ + nm_platform_link_refresh (NM_PLATFORM_GET, ifindex); + + return TRUE; +} + +/********************************************/ + +NMDeviceStatistics * +nm_device_statistics_new (NMDevice *device, unsigned rate_ms) +{ + NMDeviceStatistics *self; + + self = g_malloc0 (sizeof (*self)); + self->device = device; + + self->stats_update_id = g_timeout_add (rate_ms, update_stats, self); + + return self; +} + +void +nm_device_statistics_unref (NMDeviceStatistics *self) +{ + g_source_remove (self->stats_update_id); + g_free (self); +} + +void +nm_device_statistics_change_rate (NMDeviceStatistics *self, unsigned rate_ms) +{ + g_source_remove (self->stats_update_id); + + self->stats_update_id = g_timeout_add (rate_ms, update_stats, self); +} diff --git a/src/devices/nm-device-statistics.h b/src/devices/nm-device-statistics.h new file mode 100644 index 000000000..17ff19f48 --- /dev/null +++ b/src/devices/nm-device-statistics.h @@ -0,0 +1,31 @@ +/* -*- 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__ + +typedef struct _NMDeviceStatistics NMDeviceStatistics; + +NMDeviceStatistics * +nm_device_statistics_new (NMDevice *device, unsigned rate_ms); + +void nm_device_statistics_unref (NMDeviceStatistics *self); + +void nm_device_statistics_change_rate (NMDeviceStatistics *self, unsigned rate_ms); + +#endif /* __NETWORKMANAGER_DEVICE_STATISTICS_H__ */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 9c7319f03..72ed5e911 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) @@ -138,6 +140,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 @@ -407,6 +412,13 @@ typedef struct _NMDevicePrivate { NMLldpListener *lldp_listener; guint check_delete_unrealized_id; + + guint refresh_rate_ms; + guint64 tx_bytes; + guint64 rx_bytes; + + NMDeviceStatistics *statistics; + } NMDevicePrivate; static gboolean nm_device_set_ip4_config (NMDevice *self, @@ -769,6 +781,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) { @@ -2199,6 +2241,11 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) priv->carrier = TRUE; } + if (priv->refresh_rate_ms && !priv->statistics) { + priv->statistics = nm_device_statistics_new (self, + priv->refresh_rate_ms); + } + klass->realize_start_notify (self, plink); /* Do not manage externally created software devices until they are IFF_UP @@ -2370,6 +2417,14 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) g_clear_pointer (&priv->physical_port_id, g_free); _notify (self, PROP_PHYSICAL_PORT_ID); } + if (priv->statistics) { + nm_device_statistics_unref (priv->statistics); + priv->statistics = NULL; + priv->tx_bytes = 0; + priv->tx_bytes = 0; + _notify (self, PROP_TX_BYTES); + _notify (self, PROP_RX_BYTES); + } priv->hw_addr_type = HW_ADDR_TYPE_UNSET; g_clear_pointer (&priv->hw_addr_perm, g_free); @@ -11963,6 +12018,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* @@ -12111,6 +12171,11 @@ dispose (GObject *object) g_clear_object (&priv->lldp_listener); } + if (priv->statistics) { + nm_device_statistics_unref (priv->statistics); + priv->statistics = NULL; + } + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); if (nm_clear_g_source (&priv->queued_state.id)) { @@ -12243,6 +12308,28 @@ set_property (GObject *object, guint prop_id, /* construct only */ priv->hw_addr_perm = g_value_dup_string (value); break; + case PROP_REFRESH_RATE_MS: { + guint refresh_rate_ms; + + refresh_rate_ms = g_value_get_uint (value); + if (priv->refresh_rate_ms == refresh_rate_ms) + break; + + priv->refresh_rate_ms = refresh_rate_ms; + _LOGI (LOGD_DEVICE, "statistics refresh rate set to %u ms", priv->refresh_rate_ms); + + if (priv->refresh_rate_ms) { + if (priv->statistics) + nm_device_statistics_change_rate (priv->statistics, priv->refresh_rate_ms); + else + priv->statistics = + nm_device_statistics_new (self, priv->refresh_rate_ms); + } else { + nm_device_statistics_unref (priv->statistics); + priv->statistics = NULL; + } + break; + } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -12405,6 +12492,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; @@ -12655,6 +12751,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, UINT32_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 */ @@ -12725,4 +12838,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_STATISTICS_SKELETON, + NULL); } diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 6a4f22a5f..433448565 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -88,6 +88,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 ())