network: monitor link bit rates
This commit is contained in:
parent
94a58cc1f9
commit
a879e1a46e
|
@ -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_NO_PRODUCT_UUID, EOPNOTSUPP),
|
||||||
|
|
||||||
|
SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE, EOPNOTSUPP),
|
||||||
|
|
||||||
SD_BUS_ERROR_MAP_END
|
SD_BUS_ERROR_MAP_END
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,4 +78,6 @@
|
||||||
|
|
||||||
#define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID"
|
#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);
|
BUS_ERROR_MAP_ELF_USE(bus_common_errors);
|
||||||
|
|
|
@ -85,6 +85,8 @@ sources = files('''
|
||||||
networkd-route.h
|
networkd-route.h
|
||||||
networkd-routing-policy-rule.c
|
networkd-routing-policy-rule.c
|
||||||
networkd-routing-policy-rule.h
|
networkd-routing-policy-rule.h
|
||||||
|
networkd-speed-meter.c
|
||||||
|
networkd-speed-meter.h
|
||||||
networkd-util.c
|
networkd-util.c
|
||||||
networkd-util.h
|
networkd-util.h
|
||||||
'''.split())
|
'''.split())
|
||||||
|
|
|
@ -11,17 +11,33 @@
|
||||||
#include "extract-word.h"
|
#include "extract-word.h"
|
||||||
#include "hexdecoct.h"
|
#include "hexdecoct.h"
|
||||||
#include "networkd-conf.h"
|
#include "networkd-conf.h"
|
||||||
|
#include "networkd-manager.h"
|
||||||
#include "networkd-network.h"
|
#include "networkd-network.h"
|
||||||
|
#include "networkd-speed-meter.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
|
|
||||||
int manager_parse_config_file(Manager *m) {
|
int manager_parse_config_file(Manager *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
return config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
|
r = config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
|
||||||
CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
|
CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
|
||||||
"DHCP\0",
|
"Network\0DHCP\0",
|
||||||
config_item_perf_lookup, networkd_gperf_lookup,
|
config_item_perf_lookup, networkd_gperf_lookup,
|
||||||
CONFIG_PARSE_WARN, m);
|
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] = {
|
static const char* const duid_type_table[_DUID_TYPE_MAX] = {
|
||||||
|
|
|
@ -18,5 +18,7 @@ struct ConfigPerfItem;
|
||||||
%struct-type
|
%struct-type
|
||||||
%includes
|
%includes
|
||||||
%%
|
%%
|
||||||
DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid)
|
Network.SpeedMeter, config_parse_bool, 0, offsetof(Manager, use_speed_meter)
|
||||||
DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid)
|
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)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "bus-common-errors.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
#include "networkd-link.h"
|
#include "networkd-link.h"
|
||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
|
@ -10,11 +11,56 @@
|
||||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
|
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 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[] = {
|
const sd_bus_vtable link_vtable[] = {
|
||||||
SD_BUS_VTABLE_START(0),
|
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("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("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
|
SD_BUS_VTABLE_END
|
||||||
};
|
};
|
||||||
|
|
|
@ -122,6 +122,10 @@ typedef struct Link {
|
||||||
Hashmap *bound_by_links;
|
Hashmap *bound_by_links;
|
||||||
Hashmap *bound_to_links;
|
Hashmap *bound_to_links;
|
||||||
Set *slaves;
|
Set *slaves;
|
||||||
|
|
||||||
|
/* For speed meter */
|
||||||
|
struct rtnl_link_stats64 stats_old, stats_new;
|
||||||
|
bool stats_updated;
|
||||||
} Link;
|
} Link;
|
||||||
|
|
||||||
typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
|
typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "netlink-util.h"
|
#include "netlink-util.h"
|
||||||
#include "network-internal.h"
|
#include "network-internal.h"
|
||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
|
#include "networkd-speed-meter.h"
|
||||||
#include "ordered-set.h"
|
#include "ordered-set.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
|
@ -1369,10 +1370,14 @@ int manager_new(Manager **ret) {
|
||||||
_cleanup_(manager_freep) Manager *m = NULL;
|
_cleanup_(manager_freep) Manager *m = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
m = new0(Manager, 1);
|
m = new(Manager, 1);
|
||||||
if (!m)
|
if (!m)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*m = (Manager) {
|
||||||
|
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
|
||||||
|
};
|
||||||
|
|
||||||
m->state_file = strdup("/run/systemd/netif/state");
|
m->state_file = strdup("/run/systemd/netif/state");
|
||||||
if (!m->state_file)
|
if (!m->state_file)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1469,6 +1474,7 @@ void manager_free(Manager *m) {
|
||||||
sd_netlink_unref(m->genl);
|
sd_netlink_unref(m->genl);
|
||||||
sd_resolve_unref(m->resolve);
|
sd_resolve_unref(m->resolve);
|
||||||
|
|
||||||
|
sd_event_source_unref(m->speed_meter_event_source);
|
||||||
sd_event_unref(m->event);
|
sd_event_unref(m->event);
|
||||||
|
|
||||||
sd_device_monitor_unref(m->device_monitor);
|
sd_device_monitor_unref(m->device_monitor);
|
||||||
|
@ -1484,9 +1490,14 @@ void manager_free(Manager *m) {
|
||||||
int manager_start(Manager *m) {
|
int manager_start(Manager *m) {
|
||||||
Link *link;
|
Link *link;
|
||||||
Iterator i;
|
Iterator i;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(m);
|
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
|
/* The dirty handler will deal with future serialization, but the first one
|
||||||
must be done explicitly. */
|
must be done explicitly. */
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "dhcp-identifier.h"
|
#include "dhcp-identifier.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
#include "time-util.h"
|
||||||
|
|
||||||
#include "networkd-address-pool.h"
|
#include "networkd-address-pool.h"
|
||||||
#include "networkd-link.h"
|
#include "networkd-link.h"
|
||||||
|
@ -55,6 +56,13 @@ struct Manager {
|
||||||
Set *rules_saved;
|
Set *rules_saved;
|
||||||
|
|
||||||
int sysctl_ipv6_enabled;
|
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[];
|
extern const sd_bus_vtable manager_vtable[];
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
|
@ -11,6 +11,10 @@
|
||||||
#
|
#
|
||||||
# See networkd.conf(5) for details
|
# See networkd.conf(5) for details
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
#SpeedMeter=no
|
||||||
|
#SpeedMeterIntervalSec=10sec
|
||||||
|
|
||||||
[DHCP]
|
[DHCP]
|
||||||
#DUIDType=vendor
|
#DUIDType=vendor
|
||||||
#DUIDRawData=
|
#DUIDRawData=
|
||||||
|
|
Loading…
Reference in New Issue