]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: monitor link bit rates
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 24 May 2019 20:08:13 +0000 (05:08 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 1 Jun 2019 01:24:47 +0000 (10:24 +0900)
12 files changed:
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/network/meson.build
src/network/networkd-conf.c
src/network/networkd-gperf.gperf
src/network/networkd-link-bus.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-speed-meter.c [new file with mode: 0644]
src/network/networkd-speed-meter.h [new file with mode: 0644]
src/network/networkd.conf

index 6e5fe00e06b03bc06c27f02623e9701cf58ac93c..ef53d0a450ac380c913b6c9d6b57318ec2515c11 100644 (file)
@@ -101,5 +101,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
 
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID,              EOPNOTSUPP),
 
+        SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE,         EOPNOTSUPP),
+
         SD_BUS_ERROR_MAP_END
 };
index 8339feb768618484db6becd64ac6506d0282c311..2544bdebc1027256696ef84d669831ec572edc9f 100644 (file)
@@ -78,4 +78,6 @@
 
 #define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID"
 
+#define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive"
+
 BUS_ERROR_MAP_ELF_USE(bus_common_errors);
index feeb98cb07424ed87dcffd89d2000fa556d754d8..959421fc5c4455c5cbb042c70fab651c6c6c80bc 100644 (file)
@@ -85,6 +85,8 @@ sources = files('''
         networkd-route.h
         networkd-routing-policy-rule.c
         networkd-routing-policy-rule.h
+        networkd-speed-meter.c
+        networkd-speed-meter.h
         networkd-util.c
         networkd-util.h
 '''.split())
index 334ffc3c5710f23c13f3ad3247651093c73c9c6b..1ef5beb2032d48c4b29d0bd9028aa19a4c8f881f 100644 (file)
 #include "extract-word.h"
 #include "hexdecoct.h"
 #include "networkd-conf.h"
+#include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-speed-meter.h"
 #include "string-table.h"
 
 int manager_parse_config_file(Manager *m) {
+        int r;
+
         assert(m);
 
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
-                                        CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
-                                        "DHCP\0",
-                                        config_item_perf_lookup, networkd_gperf_lookup,
-                                        CONFIG_PARSE_WARN, m);
+        r = config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
+                                     CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
+                                     "Network\0DHCP\0",
+                                     config_item_perf_lookup, networkd_gperf_lookup,
+                                     CONFIG_PARSE_WARN, m);
+        if (r < 0)
+                return r;
+
+        if (m->use_speed_meter && m->speed_meter_interval_usec < SPEED_METER_MINIMUM_TIME_INTERVAL) {
+                char buf[FORMAT_TIMESPAN_MAX];
+
+                log_warning("SpeedMeterIntervalSec= is too small, using %s.",
+                            format_timespan(buf, sizeof buf, SPEED_METER_MINIMUM_TIME_INTERVAL, USEC_PER_SEC));
+                m->speed_meter_interval_usec = SPEED_METER_MINIMUM_TIME_INTERVAL;
+        }
+
+        return 0;
 }
 
 static const char* const duid_type_table[_DUID_TYPE_MAX] = {
index cc88fc0addc96a952ef7ab9baa45d8ae34c32f05..d74d37559937573c7ffdfa17923e46299e4e1ea8 100644 (file)
@@ -18,5 +18,7 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-DHCP.DUIDType,              config_parse_duid_type,                 0,          offsetof(Manager, duid)
-DHCP.DUIDRawData,           config_parse_duid_rawdata,              0,          offsetof(Manager, duid)
+Network.SpeedMeter,            config_parse_bool,                      0,          offsetof(Manager, use_speed_meter)
+Network.SpeedMeterIntervalSec, config_parse_sec,                       0,          offsetof(Manager, speed_meter_interval_usec)
+DHCP.DUIDType,                 config_parse_duid_type,                 0,          offsetof(Manager, duid)
+DHCP.DUIDRawData,              config_parse_duid_rawdata,              0,          offsetof(Manager, duid)
index 0dbcd86d9e963ffb99958e0e8331f0c1a73b1f2e..2f414cb11652d124e93ab7283d1b265c3a07f555 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
+#include "bus-common-errors.h"
 #include "bus-util.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_administrative_state, link_state, LinkState);
 
+static int property_get_bit_rates(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Link *link = userdata;
+        Manager *manager;
+        double tx, rx, interval_sec;
+
+        assert(bus);
+        assert(reply);
+        assert(userdata);
+
+        manager = link->manager;
+
+        if (!manager->use_speed_meter)
+                return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Speed meter is disabled.");
+
+        if (manager->speed_meter_usec_old == 0)
+                return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Speed meter is not active.");
+
+        if (!link->stats_updated)
+                return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Failed to measure bit-rates.");
+
+        assert(manager->speed_meter_usec_new > manager->speed_meter_usec_old);
+        interval_sec = (double) (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC;
+
+        if (link->stats_new.tx_bytes > link->stats_old.tx_bytes)
+                tx = (link->stats_new.tx_bytes - link->stats_old.tx_bytes) / interval_sec;
+        else
+                tx = (UINT64_MAX - (link->stats_old.tx_bytes - link->stats_new.tx_bytes)) / interval_sec;
+
+        if (link->stats_new.rx_bytes > link->stats_old.rx_bytes)
+                rx = (link->stats_new.rx_bytes - link->stats_old.rx_bytes) / interval_sec;
+        else
+                rx = (UINT64_MAX - (link->stats_old.rx_bytes - link->stats_new.rx_bytes)) / interval_sec;
+
+        return sd_bus_message_append(reply, "(dd)", tx, rx);
+}
+
 const sd_bus_vtable link_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
         SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("BitRates", "(dd)", property_get_bit_rates, 0, 0),
 
         SD_BUS_VTABLE_END
 };
index f8121180c0d3a3f9c122346ccbf07a3ed3892aa5..d0290f7c7d56aae2d9454b0f2a5002d01ad3dc61 100644 (file)
@@ -122,6 +122,10 @@ typedef struct Link {
         Hashmap *bound_by_links;
         Hashmap *bound_to_links;
         Set *slaves;
+
+        /* For speed meter */
+        struct rtnl_link_stats64 stats_old, stats_new;
+        bool stats_updated;
 } Link;
 
 typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
index de177e6d1abfa9e80d72a4eed5f519fbc97ec3c1..6984c5b9679421ccd773088d0de41330fd0183de 100644 (file)
@@ -22,6 +22,7 @@
 #include "netlink-util.h"
 #include "network-internal.h"
 #include "networkd-manager.h"
+#include "networkd-speed-meter.h"
 #include "ordered-set.h"
 #include "path-util.h"
 #include "set.h"
@@ -1369,10 +1370,14 @@ int manager_new(Manager **ret) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
 
-        m = new0(Manager, 1);
+        m = new(Manager, 1);
         if (!m)
                 return -ENOMEM;
 
+        *m = (Manager) {
+                .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
+        };
+
         m->state_file = strdup("/run/systemd/netif/state");
         if (!m->state_file)
                 return -ENOMEM;
@@ -1469,6 +1474,7 @@ void manager_free(Manager *m) {
         sd_netlink_unref(m->genl);
         sd_resolve_unref(m->resolve);
 
+        sd_event_source_unref(m->speed_meter_event_source);
         sd_event_unref(m->event);
 
         sd_device_monitor_unref(m->device_monitor);
@@ -1484,9 +1490,14 @@ void manager_free(Manager *m) {
 int manager_start(Manager *m) {
         Link *link;
         Iterator i;
+        int r;
 
         assert(m);
 
+        r = manager_start_speed_meter(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize speed meter: %m");
+
         /* The dirty handler will deal with future serialization, but the first one
            must be done explicitly. */
 
index 06fa9d8d32f7ffd66b7f0c301538798f3126403d..c816d86d1877c4fcbd19a1cf6dd8b19ab25d8172 100644 (file)
@@ -11,6 +11,7 @@
 #include "dhcp-identifier.h"
 #include "hashmap.h"
 #include "list.h"
+#include "time-util.h"
 
 #include "networkd-address-pool.h"
 #include "networkd-link.h"
@@ -55,6 +56,13 @@ struct Manager {
         Set *rules_saved;
 
         int sysctl_ipv6_enabled;
+
+        /* For link speed meter*/
+        bool use_speed_meter;
+        sd_event_source *speed_meter_event_source;
+        usec_t speed_meter_interval_usec;
+        usec_t speed_meter_usec_new;
+        usec_t speed_meter_usec_old;
 };
 
 extern const sd_bus_vtable manager_vtable[];
diff --git a/src/network/networkd-speed-meter.c b/src/network/networkd-speed-meter.c
new file mode 100644 (file)
index 0000000..5fd30f3
--- /dev/null
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "sd-event.h"
+#include "sd-netlink.h"
+
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-speed-meter.h"
+
+static int process_message(Manager *manager, sd_netlink_message *message) {
+        uint16_t type;
+        int ifindex, r;
+        Link *link;
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0)
+                return r;
+
+        if (type != RTM_NEWLINK)
+                return 0;
+
+        r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
+        if (r < 0)
+                return r;
+
+        link = hashmap_get(manager->links, INT_TO_PTR(ifindex));
+        if (!link)
+                return -ENODEV;
+
+        link->stats_old = link->stats_new;
+
+        r = sd_netlink_message_read(message, IFLA_STATS64, sizeof link->stats_new, &link->stats_new);
+        if (r < 0)
+                return r;
+
+        link->stats_updated = true;
+
+        return 0;
+}
+
+static int speed_meter_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        Manager *manager = userdata;
+        sd_netlink_message *i;
+        usec_t usec_now;
+        Iterator j;
+        Link *link;
+        int r;
+
+        assert(s);
+        assert(userdata);
+
+        r = sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &usec_now);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_time(s, usec_now + manager->speed_meter_interval_usec);
+        if (r < 0)
+                return r;
+
+        manager->speed_meter_usec_old = manager->speed_meter_usec_new;
+        manager->speed_meter_usec_new = usec_now;
+
+        HASHMAP_FOREACH(link, manager->links, j)
+                link->stats_updated = false;
+
+        r = sd_rtnl_message_new_link(manager->rtnl, &req, RTM_GETLINK, 0);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to allocate RTM_GETLINK netlink message, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_request_dump(req, true);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to set dump flag, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_call(manager->rtnl, req, 0, &reply);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to call RTM_GETLINK, ignoring: %m");
+                return 0;
+        }
+
+        for (i = reply; i; i = sd_netlink_message_next(i))
+                (void) process_message(manager, i);
+
+        return 0;
+}
+
+int manager_start_speed_meter(Manager *manager) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        int r;
+
+        assert(manager);
+        assert(manager->event);
+
+        if (!manager->use_speed_meter)
+                return 0;
+
+        r = sd_event_add_time(manager->event, &s, CLOCK_MONOTONIC, 0, 0, speed_meter_handler, manager);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_enabled(s, SD_EVENT_ON);
+        if (r < 0)
+                return r;
+
+        manager->speed_meter_event_source = TAKE_PTR(s);
+        return 0;
+}
diff --git a/src/network/networkd-speed-meter.h b/src/network/networkd-speed-meter.h
new file mode 100644 (file)
index 0000000..f572778
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/* Default interval is 10sec. The speed meter periodically make networkd
+ * to be woke up. So, too small interval value is not desired.
+ * We set the minimum value 100msec = 0.1sec. */
+#define SPEED_METER_DEFAULT_TIME_INTERVAL (10 * USEC_PER_SEC)
+#define SPEED_METER_MINIMUM_TIME_INTERVAL (100 * USEC_PER_MSEC)
+
+typedef struct Manager Manager;
+
+int manager_start_speed_meter(Manager *m);
index 8dc5676166377945292e9e7df89632b49c89e692..c5667da9fadb0dfdee7a59aac2ebb24744862fc5 100644 (file)
 #
 # See networkd.conf(5) for details
 
+[Network]
+#SpeedMeter=no
+#SpeedMeterIntervalSec=10sec
+
 [DHCP]
 #DUIDType=vendor
 #DUIDRawData=