#include <sys/types.h>
#include <unistd.h>
+#include "sd-bus.h"
#include "sd-device.h"
#include "sd-hwdb.h"
#include "sd-lldp.h"
#include "alloc-util.h"
#include "arphrd-list.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "device-util.h"
#include "ether-addr-util.h"
+#include "ethtool-util.h"
#include "fd-util.h"
#include "format-table.h"
+#include "format-util.h"
#include "hwdb-util.h"
#include "local-addresses.h"
#include "locale-util.h"
#include "terminal-util.h"
#include "verbs.h"
+/* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
+#define NETDEV_KIND_MAX 64
+
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static bool arg_all = false;
+static bool arg_stats = false;
static char *link_get_type_string(unsigned short iftype, sd_device *d) {
const char *t, *devtype;
*on = *off = "";
}
+typedef struct VxLanInfo {
+ uint32_t vni;
+ uint32_t link;
+
+ int local_family;
+ int group_family;
+
+ union in_addr_union local;
+ union in_addr_union group;
+
+ uint16_t dest_port;
+
+} VxLanInfo;
+
typedef struct LinkInfo {
char name[IFNAMSIZ+1];
+ char netdev_kind[NETDEV_KIND_MAX];
int ifindex;
unsigned short iftype;
struct ether_addr mac_address;
uint32_t tx_queues;
uint32_t rx_queues;
+ union {
+ struct rtnl_link_stats64 stats64;
+ struct rtnl_link_stats stats;
+ };
+
+ uint64_t tx_bitrate;
+ uint64_t rx_bitrate;
+
+ /* bridge info */
+ uint32_t forward_delay;
+ uint32_t hello_time;
+ uint32_t max_age;
+ uint32_t ageing_time;
+ uint32_t stp_state;
+ uint16_t priority;
+ uint8_t mcast_igmp_version;
+
+ /* vxlan info */
+ VxLanInfo vxlan_info;
+
+ /* ethtool info */
+ int autonegotiation;
+ size_t speed;
+ Duplex duplex;
+ NetDevPort port;
+
bool has_mac_address:1;
- bool has_mtu:1;
bool has_tx_queues:1;
bool has_rx_queues:1;
+ bool has_stats64:1;
+ bool has_stats:1;
+ bool has_bitrates:1;
+ bool has_ethtool_link_info:1;
} LinkInfo;
static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
return CMP(a->ifindex, b->ifindex);
}
+static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
+ const char *received_kind;
+ int r;
+
+ assert(m);
+ assert(info);
+
+ r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
+ if (r < 0)
+ return r;
+
+ if (streq(received_kind, "bridge")) {
+ (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
+ (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
+ (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
+ (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
+ (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
+ (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
+ (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
+
+ } else if (streq(received_kind, "vxlan")) {
+ (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
+
+ r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
+ if (r >= 0)
+ info->vxlan_info.group_family = AF_INET;
+ else {
+ r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
+ if (r >= 0)
+ info->vxlan_info.group_family = AF_INET6;
+ }
+
+ r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
+ if (r >= 0)
+ info->vxlan_info.local_family = AF_INET;
+ else {
+ r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
+ if (r >= 0)
+ info->vxlan_info.local_family = AF_INET6;
+ }
+
+ (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
+ (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
+ }
+
+ strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
+
+ (void) sd_netlink_message_exit_container(m);
+ (void) sd_netlink_message_exit_container(m);
+
+ return 0;
+}
+
static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
const char *name;
- uint16_t type;
int ifindex, r;
+ uint16_t type;
assert(m);
assert(info);
sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 &&
memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
- info->has_mtu =
- sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) >= 0 &&
- info->mtu > 0;
-
- if (info->has_mtu) {
- (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
- (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
- }
+ (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu);
+ (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
+ (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
info->has_rx_queues =
sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
info->tx_queues > 0;
+ if (sd_netlink_message_read(m, IFLA_STATS64, sizeof info->stats64, &info->stats64) >= 0)
+ info->has_stats64 = true;
+ else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
+ info->has_stats = true;
+
+ /* fill kind info */
+ (void) decode_netdev(m, info);
+
return 1;
}
-static int acquire_link_info(sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
+static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
+ int r;
+
+ if (asprintf(&ifindex_str, "%i", link->ifindex) < 0)
+ return -ENOMEM;
+
+ r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &error,
+ &reply,
+ "ss",
+ "org.freedesktop.network1.Link",
+ "BitRates");
+ if (r < 0) {
+ bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) ||
+ sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
+
+ return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
+ }
+
+ r = sd_bus_message_enter_container(reply, 'v', "(tt)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ link->has_bitrates = true;
+
+ return 0;
+}
+
+static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_free_ LinkInfo *links = NULL;
- size_t allocated = 0, c = 0;
+ _cleanup_close_ int fd = -1;
+ size_t allocated = 0, c = 0, j;
sd_netlink_message *i;
int r;
return log_error_errno(r, "Failed to enumerate links: %m");
for (i = reply; i; i = sd_netlink_message_next(i)) {
- if (!GREEDY_REALLOC(links, allocated, c+1))
+ if (!GREEDY_REALLOC0(links, allocated, c+1))
return -ENOMEM;
r = decode_link(i, links + c, patterns);
if (r < 0)
return r;
- if (r > 0)
- c++;
+ if (r == 0)
+ continue;
+
+ r = ethtool_get_link_info(&fd, links[c].name,
+ &links[c].autonegotiation, &links[c].speed,
+ &links[c].duplex, &links[c].port);
+ if (r >= 0)
+ links[c].has_ethtool_link_info = true;
+
+ c++;
}
typesafe_qsort(links, c, link_info_compare);
+ if (bus)
+ for (j = 0; j < c; j++)
+ (void) acquire_link_bitrates(bus, links + j);
+
*ret = TAKE_PTR(links);
return (int) c;
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- c = acquire_link_info(rtnl, argc > 1 ? argv + 1 : NULL, &links);
+ c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
if (c < 0)
return c;
(void) pager_open(arg_pager_flags);
- table = table_new("IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
+ table = table_new("idx", "link", "type", "operational", "setup");
if (!table)
return log_oom();
assert_se(cell = table_get_cell(table, 0, 0));
(void) table_set_minimum_width(table, cell, 3);
(void) table_set_weight(table, cell, 0);
- (void) table_set_ellipsize_percent(table, cell, 0);
+ (void) table_set_ellipsize_percent(table, cell, 100);
(void) table_set_align_percent(table, cell, 100);
assert_se(cell = table_get_cell(table, 0, 1));
- (void) table_set_minimum_width(table, cell, 16);
-
- assert_se(cell = table_get_cell(table, 0, 2));
- (void) table_set_minimum_width(table, cell, 18);
-
- assert_se(cell = table_get_cell(table, 0, 3));
- (void) table_set_minimum_width(table, cell, 16);
-
- assert_se(cell = table_get_cell(table, 0, 4));
- (void) table_set_minimum_width(table, cell, 10);
+ (void) table_set_ellipsize_percent(table, cell, 100);
for (i = 0; i < c; i++) {
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
t = link_get_type_string(links[i].iftype, d);
- r = table_add_cell_full(table, NULL, TABLE_INT, &links[i].ifindex, SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
-
r = table_add_many(table,
+ TABLE_INT, links[i].ifindex,
TABLE_STRING, links[i].name,
- TABLE_STRING, strna(t));
- if (r < 0)
- return r;
-
- r = table_add_cell(table, &cell, TABLE_STRING, strna(operational_state));
- if (r < 0)
- return r;
-
- (void) table_set_color(table, cell, on_color_operational);
-
- r = table_add_cell(table, &cell, TABLE_STRING, strna(setup_state));
+ TABLE_STRING, strna(t),
+ TABLE_STRING, strna(operational_state),
+ TABLE_SET_COLOR, on_color_operational,
+ TABLE_STRING, strna(setup_state),
+ TABLE_SET_COLOR, on_color_setup);
if (r < 0)
return r;
-
- (void) table_set_color(table, cell, on_color_setup);
}
r = table_print(table, NULL);
if (!in_addr_equal(fam, &gw, gateway))
continue;
- r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
+ r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
if (r < 0)
continue;
for (i = 0; i < n; i++) {
_cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
-
- r = table_add_cell_full(table, NULL, TABLE_STRING, i == 0 ? "Gateway:" : "", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, i == 0 ? "Gateway:" : "");
if (r < 0)
return r;
/* Show interface name for the entry if we show
* entries for all interfaces */
if (ifindex <= 0) {
- char name[IF_NAMESIZE+1] = {};
+ char name[IF_NAMESIZE+1];
- if (if_indextoname(local[i].ifindex, name))
+ if (format_ifname(local[i].ifindex, name))
r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway, name);
else
r = table_add_cell_stringf(table, NULL, "%s on %%%i", with_description ?: gateway, local[i].ifindex);
for (i = 0; i < n; i++) {
_cleanup_free_ char *pretty = NULL;
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
-
- r = table_add_cell_full(table, NULL, TABLE_STRING, i == 0 ? "Address:" : "", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, i == 0 ? "Address:" : "");
if (r < 0)
return r;
return r;
if (ifindex <= 0) {
- char name[IF_NAMESIZE+1] = {};
+ char name[IF_NAMESIZE+1];
- if (if_indextoname(local[i].ifindex, name))
+ if (format_ifname(local[i].ifindex, name))
r = table_add_cell_stringf(table, NULL, "%s on %s", pretty, name);
else
r = table_add_cell_stringf(table, NULL, "%s on %%%i", pretty, local[i].ifindex);
static int dump_address_labels(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
sd_netlink_message *m;
+ TableCell *cell;
int r;
assert(rtnl);
if (r < 0)
return r;
- printf("%10s/%s %30s\n", "Prefix", "Prefixlen", "Label");
+ table = table_new("label", "prefix/prefixlen");
+ if (!table)
+ return -ENOMEM;
+
+ r = table_set_sort(table, 0, SIZE_MAX);
+ if (r < 0)
+ return r;
+
+ assert_se(cell = table_get_cell(table, 0, 0));
+ (void) table_set_align_percent(table, cell, 100);
+ (void) table_set_ellipsize_percent(table, cell, 100);
+
+ assert_se(cell = table_get_cell(table, 0, 1));
+ (void) table_set_align_percent(table, cell, 100);
for (m = reply; m; m = sd_netlink_message_next(m)) {
_cleanup_free_ char *pretty = NULL;
if (r < 0)
continue;
- printf("%10s/%-5u %30u\n", pretty, prefixlen, label);
+ r = table_add_cell(table, NULL, TABLE_UINT32, &label);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
+ if (r < 0)
+ return r;
}
- return 0;
+ return table_print(table, NULL);
}
static int list_address_labels(int argc, char *argv[], void *userdata) {
for (;;) {
const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
- _cleanup_free_ char *str = NULL;
r = next_lldp_neighbor(f, &n);
if (r < 0)
if (r == 0)
break;
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
-
- r = table_add_cell_full(table, NULL, TABLE_STRING, c == 0 ? prefix : "", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, c == 0 ? prefix : "");
if (r < 0)
return r;
(void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
(void) sd_lldp_neighbor_get_port_description(n, &port_description);
- if (asprintf(&str, "%s on port %s%s%s%s",
- strna(system_name), strna(port_id),
- isempty(port_description) ? "" : " (",
- port_description,
- isempty(port_description) ? "" : ")") < 0)
- return -ENOMEM;
-
- r = table_add_cell(table, NULL, TABLE_STRING, str);
+ r = table_add_cell_stringf(table, NULL,
+ "%s on port %s%s%s%s",
+ strna(system_name), strna(port_id),
+ isempty(port_description) ? "" : " (",
+ port_description,
+ isempty(port_description) ? "" : ")");
if (r < 0)
return r;
return 0;
for (c = 0; ifindexes[c] > 0; c++) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
-
- r = table_add_cell_full(table, NULL, TABLE_STRING, c == 0 ? prefix : "", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
-
- r = table_add_cell(table, NULL, TABLE_IFINDEX, ifindexes + c);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, c == 0 ? prefix : "",
+ TABLE_IFINDEX, ifindexes[c]);
if (r < 0)
return r;
}
return 0;
STRV_FOREACH(i, l) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, i == l ? prefix : "",
+ TABLE_STRING, *i);
if (r < 0)
return r;
+ }
- r = table_add_cell_full(table, NULL, TABLE_STRING, i == l ? prefix : "", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
+ return 0;
+}
- r = table_add_cell(table, NULL, TABLE_STRING, *i);
- if (r < 0)
- return r;
- }
+#define DUMP_STATS_ONE(name, val_name) \
+ r = table_add_many(table, \
+ TABLE_EMPTY, \
+ TABLE_STRING, name ":"); \
+ if (r < 0) \
+ return r; \
+ r = table_add_cell(table, NULL, \
+ info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
+ info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
+ if (r < 0) \
+ return r;
+
+static int dump_statistics(Table *table, const LinkInfo *info) {
+ int r;
+
+ if (!arg_stats)
+ return 0;
+
+ if (!info->has_stats64 && !info->has_stats)
+ return 0;
+
+ DUMP_STATS_ONE("Rx Packets", rx_packets);
+ DUMP_STATS_ONE("Tx Packets", tx_packets);
+ DUMP_STATS_ONE("Rx Bytes", rx_bytes);
+ DUMP_STATS_ONE("Tx Bytes", tx_bytes);
+ DUMP_STATS_ONE("Rx Errors", rx_errors);
+ DUMP_STATS_ONE("Tx Errors", tx_errors);
+ DUMP_STATS_ONE("Rx Dropped", rx_dropped);
+ DUMP_STATS_ONE("Tx Dropped", tx_dropped);
+ DUMP_STATS_ONE("Multicast Packets", multicast);
+ DUMP_STATS_ONE("Collisions", collisions);
return 0;
}
(void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
(void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
- table = table_new("DOT", "KEY", "VALUE");
+ table = table_new("dot", "key", "value");
if (!table)
return -ENOMEM;
- table_set_header(table, false);
-
- r = table_add_cell(table, &cell, TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE));
- if (r < 0)
- return r;
- (void) table_set_ellipsize_percent(table, cell, 0);
- (void) table_set_color(table, cell, on_color_operational);
- r = table_add_cell_stringf(table, NULL, "%i: %s", info->ifindex, info->name);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
+ assert_se(cell = table_get_cell(table, 0, 0));
+ (void) table_set_ellipsize_percent(table, cell, 100);
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Link File:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, strna(link));
- if (r < 0)
- return r;
+ assert_se(cell = table_get_cell(table, 0, 1));
+ (void) table_set_ellipsize_percent(table, cell, 100);
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Network File:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, strna(network));
- if (r < 0)
- return r;
+ table_set_header(table, false);
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Type:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, strna(t));
+ r = table_add_many(table,
+ TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
+ TABLE_SET_COLOR, on_color_operational);
if (r < 0)
return r;
-
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
if (r < 0)
return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "State:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ (void) table_set_align_percent(table, cell, 0);
+
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_STRING, "Link File:",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_STRING, strna(link),
+ TABLE_EMPTY,
+ TABLE_STRING, "Network File:",
+ TABLE_STRING, strna(network),
+ TABLE_EMPTY,
+ TABLE_STRING, "Type:",
+ TABLE_STRING, strna(t),
+ TABLE_EMPTY,
+ TABLE_STRING, "State:");
if (r < 0)
return r;
r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
- on_color_operational, strna(operational_state), off_color_operational,
- on_color_setup, strna(setup_state), off_color_setup);
+ on_color_operational, strna(operational_state), off_color_operational,
+ on_color_setup, strna(setup_state), off_color_setup);
if (r < 0)
return r;
if (path) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Path:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, path);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Path:",
+ TABLE_STRING, path);
if (r < 0)
return r;
}
if (driver) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Driver:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, driver);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Driver:",
+ TABLE_STRING, driver);
if (r < 0)
return r;
}
if (vendor) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Vendor:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, vendor);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Vendor:",
+ TABLE_STRING, vendor);
if (r < 0)
return r;
}
if (model) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Model:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, model);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Model:",
+ TABLE_STRING, model);
if (r < 0)
return r;
}
(void) ieee_oui(hwdb, &info->mac_address, &description);
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "HW Address:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "HW Address:");
if (r < 0)
return r;
r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
- ether_addr_to_string(&info->mac_address, ea),
- description ? " (" : "",
- description,
- description ? ")" : "");
+ ether_addr_to_string(&info->mac_address, ea),
+ description ? " (" : "",
+ strempty(description),
+ description ? ")" : "");
if (r < 0)
return r;
}
- if (info->has_mtu) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ if (info->mtu > 0) {
+ char min_str[DECIMAL_STR_MAX(uint32_t)], max_str[DECIMAL_STR_MAX(uint32_t)];
+
+ xsprintf(min_str, "%" PRIu32, info->min_mtu);
+ xsprintf(max_str, "%" PRIu32, info->max_mtu);
+
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "MTU:");
if (r < 0)
return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "MTU:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ r = table_add_cell_stringf(table, NULL, "%" PRIu32 "%s%s%s%s%s%s%s",
+ info->mtu,
+ info->min_mtu > 0 || info->max_mtu > 0 ? " (" : "",
+ info->min_mtu > 0 ? "min: " : "",
+ info->min_mtu > 0 ? min_str : "",
+ info->min_mtu > 0 && info->max_mtu > 0 ? ", " : "",
+ info->max_mtu > 0 ? "max: " : "",
+ info->max_mtu > 0 ? max_str : "",
+ info->min_mtu > 0 || info->max_mtu > 0 ? ")" : "");
if (r < 0)
return r;
- r = table_add_cell_stringf(table, NULL, "%" PRIu32 " (Minimum: %" PRIu32 ", Maximum: %" PRIu32 ")",
- info->mtu, info->min_mtu, info->max_mtu);
+ }
+
+ if (streq_ptr(info->netdev_kind, "bridge")) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Forward Delay:",
+ TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->forward_delay),
+ TABLE_EMPTY,
+ TABLE_STRING, "Hello Time:",
+ TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->hello_time),
+ TABLE_EMPTY,
+ TABLE_STRING, "Max Age:",
+ TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->max_age),
+ TABLE_EMPTY,
+ TABLE_STRING, "Ageing Time:",
+ TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->ageing_time),
+ TABLE_EMPTY,
+ TABLE_STRING, "Priority:",
+ TABLE_UINT16, info->priority,
+ TABLE_EMPTY,
+ TABLE_STRING, "STP:",
+ TABLE_BOOLEAN, info->stp_state > 0,
+ TABLE_EMPTY,
+ TABLE_STRING, "Multicast IGMP Version:",
+ TABLE_UINT8, info->mcast_igmp_version);
if (r < 0)
return r;
+
+ } else if (streq_ptr(info->netdev_kind, "vxlan")) {
+ if (info->vxlan_info.vni > 0) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "VNI:",
+ TABLE_UINT32, info->vxlan_info.vni);
+ if (r < 0)
+ return r;
+ }
+
+ if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Group:",
+ info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
+ &info->vxlan_info.group);
+ if (r < 0)
+ return r;
+ }
+
+ if (IN_SET(info->vxlan_info.local_family, AF_INET, AF_INET6)) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Local:",
+ info->vxlan_info.local_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
+ &info->vxlan_info.local);
+ if (r < 0)
+ return r;
+ }
+
+ if (info->vxlan_info.dest_port > 0) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Destination Port:",
+ TABLE_UINT16, be16toh(info->vxlan_info.dest_port));
+ if (r < 0)
+ return r;
+ }
+
+ if (info->vxlan_info.link > 0) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Underlying Device:",
+ TABLE_IFINDEX, info->vxlan_info.link);
+ if (r < 0)
+ return r;
+ }
}
- if (info->has_tx_queues || info->has_rx_queues) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ if (info->has_bitrates) {
+ char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX];
+
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Bit Rate (Tx/Rx):");
if (r < 0)
return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Queue Length (Tx/Rx):", SIZE_MAX, SIZE_MAX, 0, 100, 0);
+ r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
+ format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0),
+ format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0));
+ if (r < 0)
+ return r;
+ }
+
+ if (info->has_tx_queues || info->has_rx_queues) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Queue Length (Tx/Rx):");
if (r < 0)
return r;
r = table_add_cell_stringf(table, NULL, "%" PRIu32 "/%" PRIu32, info->tx_queues, info->rx_queues);
return r;
}
+ if (info->has_ethtool_link_info) {
+ const char *duplex = duplex_to_string(info->duplex);
+ const char *port = port_to_string(info->port);
+
+ if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Auto negotiation:",
+ TABLE_BOOLEAN, info->autonegotiation == AUTONEG_ENABLE);
+ if (r < 0)
+ return r;
+ }
+
+ if (info->speed > 0) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Speed:",
+ TABLE_BPS, (uint64_t) info->speed);
+ if (r < 0)
+ return r;
+ }
+
+ if (duplex) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Duplex:",
+ TABLE_STRING, duplex);
+ if (r < 0)
+ return r;
+ }
+
+ if (port) {
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Port:",
+ TABLE_STRING, port);
+ if (r < 0)
+ return r;
+ }
+ }
+
r = dump_addresses(rtnl, table, info->ifindex);
if (r < 0)
return r;
(void) sd_network_link_get_timezone(info->ifindex, &tz);
if (tz) {
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- if (r < 0)
- return r;
- r = table_add_cell_full(table, NULL, TABLE_STRING, "Time Zone:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
- r = table_add_cell(table, NULL, TABLE_STRING, tz);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, "Time Zone:",
+ TABLE_STRING, tz);
if (r < 0)
return r;
}
if (r < 0)
return r;
+ r = dump_statistics(table, info);
+ if (r < 0)
+ return r;
+
return table_print(table, NULL);
}
(void) sd_network_get_operational_state(&operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
- table = table_new("DOT", "KEY", "VALUE");
+ table = table_new("dot", "key", "value");
if (!table)
return -ENOMEM;
- table_set_header(table, false);
+ assert_se(cell = table_get_cell(table, 0, 0));
+ (void) table_set_ellipsize_percent(table, cell, 100);
- r = table_add_cell(table, &cell, TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE));
- if (r < 0)
- return r;
- (void) table_set_color(table, cell, on_color_operational);
- (void) table_set_ellipsize_percent(table, cell, 0);
+ assert_se(cell = table_get_cell(table, 0, 1));
+ (void) table_set_align_percent(table, cell, 100);
+ (void) table_set_ellipsize_percent(table, cell, 100);
- r = table_add_cell_full(table, NULL, TABLE_STRING, "State:", SIZE_MAX, SIZE_MAX, 0, 100, 0);
- if (r < 0)
- return r;
+ table_set_header(table, false);
- r = table_add_cell(table, &cell, TABLE_STRING, strna(operational_state));
- if (r < 0)
- return r;
- (void) table_set_color(table, cell, on_color_operational);
+ r = table_add_many(table,
+ TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
+ TABLE_SET_COLOR, on_color_operational,
+ TABLE_STRING, "State:",
+ TABLE_STRING, strna(operational_state),
+ TABLE_SET_COLOR, on_color_operational);
r = dump_addresses(rtnl, table, 0);
if (r < 0)
}
static int link_status(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
_cleanup_free_ LinkInfo *links = NULL;
(void) pager_open(arg_pager_flags);
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect system bus: %m");
+
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
log_debug_errno(r, "Failed to open hardware database: %m");
if (arg_all)
- c = acquire_link_info(rtnl, NULL, &links);
+ c = acquire_link_info(bus, rtnl, NULL, &links);
else if (argc <= 1)
return system_status(rtnl, hwdb);
else
- c = acquire_link_info(rtnl, argv + 1, &links);
+ c = acquire_link_info(bus, rtnl, argv + 1, &links);
if (c < 0)
return c;
static int link_lldp_status(int argc, char *argv[], void *userdata) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ LinkInfo *links = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
int i, r, c, m = 0;
uint16_t all = 0;
+ TableCell *cell;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- c = acquire_link_info(rtnl, argc > 1 ? argv + 1 : NULL, &links);
+ c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
if (c < 0)
return c;
(void) pager_open(arg_pager_flags);
- if (arg_legend)
- printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
- "LINK",
- "CHASSIS ID",
- "SYSTEM NAME",
- "CAPS",
- "PORT ID",
- "PORT DESCRIPTION");
+ table = table_new("link",
+ "chassis id",
+ "system name",
+ "caps",
+ "port id",
+ "port description");
+ if (!table)
+ return -ENOMEM;
+
+ table_set_header(table, arg_legend);
+
+ assert_se(cell = table_get_cell(table, 0, 0));
+ table_set_minimum_width(table, cell, 16);
+
+ assert_se(cell = table_get_cell(table, 0, 1));
+ table_set_minimum_width(table, cell, 17);
+
+ assert_se(cell = table_get_cell(table, 0, 2));
+ table_set_minimum_width(table, cell, 16);
+
+ assert_se(cell = table_get_cell(table, 0, 3));
+ table_set_minimum_width(table, cell, 11);
+
+ assert_se(cell = table_get_cell(table, 0, 4));
+ table_set_minimum_width(table, cell, 17);
+
+ assert_se(cell = table_get_cell(table, 0, 5));
+ table_set_minimum_width(table, cell, 16);
for (i = 0; i < c; i++) {
_cleanup_fclose_ FILE *f = NULL;
}
for (;;) {
- _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL;
- const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL;
+ _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL, *capabilities = NULL;
+ const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
uint16_t cc;
all |= cc;
}
- printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
- links[i].name,
- strna(chassis_id),
- strna(system_name),
- strna(capabilities),
- strna(port_id),
- strna(port_description));
+ r = table_add_many(table,
+ TABLE_STRING, links[i].name,
+ TABLE_STRING, strna(chassis_id),
+ TABLE_STRING, strna(system_name),
+ TABLE_STRING, strna(capabilities),
+ TABLE_STRING, strna(port_id),
+ TABLE_STRING, strna(port_description));
+ if (r < 0)
+ return r;
m++;
}
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return r;
+
if (arg_legend) {
lldp_capabilities_legend(all);
printf("\n%i neighbors listed.\n", m);
static int link_delete(int argc, char *argv[], void *userdata) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_set_free_ Set *indexes = NULL;
- char ifname[IFNAMSIZ] = "";
int index, r, i;
Iterator j;
+ void *p;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_oom();
}
- SET_FOREACH(index, indexes, j) {
- r = link_delete_send_message(rtnl, index);
+ SET_FOREACH(p, indexes, j) {
+ r = link_delete_send_message(rtnl, PTR_TO_INT(p));
if (r < 0) {
- if (if_indextoname(index, ifname))
+ char ifname[IF_NAMESIZE + 1];
+
+ if (format_ifname(index, ifname))
return log_error_errno(r, "Failed to delete interface %s: %m", ifname);
else
return log_error_errno(r, "Failed to delete interface %d: %m", index);
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
- " -a --all Show status for all links\n\n"
- "Commands:\n"
+ " -a --all Show status for all links\n"
+ " -s --stats Show detailed link statics\n"
+ "\nCommands:\n"
" list [PATTERN...] List links\n"
" status [PATTERN...] Show link status\n"
" lldp [PATTERN...] Show LLDP neighbors\n"
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "all", no_argument, NULL, 'a' },
+ { "stats", no_argument, NULL, 's' },
{}
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "has", options, NULL)) >= 0) {
switch (c) {
arg_all = true;
break;
+ case 's':
+ arg_stats = true;
+ break;
+
case '?':
return -EINVAL;