1 /* SPDX-License-Identifier: LGPL-2.1+ */
4 #include <linux/if_addrlabel.h>
12 #include "sd-device.h"
15 #include "sd-netlink.h"
16 #include "sd-network.h"
18 #include "alloc-util.h"
19 #include "arphrd-list.h"
20 #include "bus-common-errors.h"
21 #include "bus-error.h"
23 #include "device-util.h"
24 #include "ether-addr-util.h"
25 #include "ethtool-util.h"
27 #include "format-table.h"
28 #include "format-util.h"
29 #include "hwdb-util.h"
30 #include "local-addresses.h"
31 #include "locale-util.h"
33 #include "main-func.h"
34 #include "netlink-util.h"
36 #include "parse-util.h"
37 #include "pretty-print.h"
39 #include "socket-util.h"
40 #include "sort-util.h"
41 #include "sparse-endian.h"
42 #include "stdio-util.h"
43 #include "string-table.h"
44 #include "string-util.h"
47 #include "terminal-util.h"
50 static PagerFlags arg_pager_flags
= 0;
51 static bool arg_legend
= true;
52 static bool arg_all
= false;
53 static bool arg_stats
= false;
55 static char *link_get_type_string(unsigned short iftype
, sd_device
*d
) {
56 const char *t
, *devtype
;
60 sd_device_get_devtype(d
, &devtype
) >= 0 &&
62 return strdup(devtype
);
64 t
= arphrd_to_name(iftype
);
76 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
80 if (STRPTR_IN_SET(state
, "routable", "enslaved")) {
81 *on
= ansi_highlight_green();
83 } else if (streq_ptr(state
, "degraded")) {
84 *on
= ansi_highlight_yellow();
90 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
94 if (streq_ptr(state
, "configured")) {
95 *on
= ansi_highlight_green();
97 } else if (streq_ptr(state
, "configuring")) {
98 *on
= ansi_highlight_yellow();
100 } else if (STRPTR_IN_SET(state
, "failed", "linger")) {
101 *on
= ansi_highlight_red();
102 *off
= ansi_normal();
107 typedef struct LinkInfo
{
108 char name
[IFNAMSIZ
+1];
110 unsigned short iftype
;
111 struct ether_addr mac_address
;
119 struct rtnl_link_stats64 stats64
;
120 struct rtnl_link_stats stats
;
132 bool has_mac_address
:1;
133 bool has_tx_queues
:1;
134 bool has_rx_queues
:1;
138 bool has_ethtool_link_info
:1;
141 static int link_info_compare(const LinkInfo
*a
, const LinkInfo
*b
) {
142 return CMP(a
->ifindex
, b
->ifindex
);
145 static int decode_link(sd_netlink_message
*m
, LinkInfo
*info
, char **patterns
) {
153 r
= sd_netlink_message_get_type(m
, &type
);
157 if (type
!= RTM_NEWLINK
)
160 r
= sd_rtnl_message_link_get_ifindex(m
, &ifindex
);
164 r
= sd_netlink_message_read_string(m
, IFLA_IFNAME
, &name
);
169 char str
[DECIMAL_STR_MAX(int)];
171 xsprintf(str
, "%i", ifindex
);
173 if (!strv_fnmatch(patterns
, str
, 0) && !strv_fnmatch(patterns
, name
, 0))
177 r
= sd_rtnl_message_link_get_type(m
, &info
->iftype
);
181 strscpy(info
->name
, sizeof info
->name
, name
);
182 info
->ifindex
= ifindex
;
184 info
->has_mac_address
=
185 sd_netlink_message_read_ether_addr(m
, IFLA_ADDRESS
, &info
->mac_address
) >= 0 &&
186 memcmp(&info
->mac_address
, ÐER_ADDR_NULL
, sizeof(struct ether_addr
)) != 0;
188 (void) sd_netlink_message_read_u32(m
, IFLA_MTU
, &info
->mtu
);
189 (void) sd_netlink_message_read_u32(m
, IFLA_MIN_MTU
, &info
->min_mtu
);
190 (void) sd_netlink_message_read_u32(m
, IFLA_MAX_MTU
, &info
->max_mtu
);
192 info
->has_rx_queues
=
193 sd_netlink_message_read_u32(m
, IFLA_NUM_RX_QUEUES
, &info
->rx_queues
) >= 0 &&
196 info
->has_tx_queues
=
197 sd_netlink_message_read_u32(m
, IFLA_NUM_TX_QUEUES
, &info
->tx_queues
) >= 0 &&
200 if (sd_netlink_message_read(m
, IFLA_STATS64
, sizeof info
->stats64
, &info
->stats64
) >= 0)
201 info
->has_stats64
= true;
202 else if (sd_netlink_message_read(m
, IFLA_STATS
, sizeof info
->stats
, &info
->stats
) >= 0)
203 info
->has_stats
= true;
208 static int acquire_link_bitrates(sd_bus
*bus
, LinkInfo
*link
) {
209 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
210 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
211 _cleanup_free_
char *path
= NULL
, *ifindex_str
= NULL
;
214 if (asprintf(&ifindex_str
, "%i", link
->ifindex
) < 0)
217 r
= sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str
, &path
);
221 r
= sd_bus_call_method(
223 "org.freedesktop.network1",
225 "org.freedesktop.DBus.Properties",
230 "org.freedesktop.network1.Link",
233 bool quiet
= sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_PROPERTY
) ||
234 sd_bus_error_has_name(&error
, BUS_ERROR_SPEED_METER_INACTIVE
);
236 return log_full_errno(quiet
? LOG_DEBUG
: LOG_WARNING
,
237 r
, "Failed to query link bit rates: %s", bus_error_message(&error
, r
));
240 r
= sd_bus_message_enter_container(reply
, 'v', "(tt)");
242 return bus_log_parse_error(r
);
244 r
= sd_bus_message_read(reply
, "(tt)", &link
->tx_bitrate
, &link
->rx_bitrate
);
246 return bus_log_parse_error(r
);
248 r
= sd_bus_message_exit_container(reply
);
250 return bus_log_parse_error(r
);
252 link
->has_bitrates
= true;
257 static int acquire_link_info(sd_bus
*bus
, sd_netlink
*rtnl
, char **patterns
, LinkInfo
**ret
) {
258 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
259 _cleanup_free_ LinkInfo
*links
= NULL
;
260 _cleanup_close_
int fd
= -1;
261 size_t allocated
= 0, c
= 0, j
;
262 sd_netlink_message
*i
;
268 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
270 return rtnl_log_create_error(r
);
272 r
= sd_netlink_message_request_dump(req
, true);
274 return rtnl_log_create_error(r
);
276 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
278 return log_error_errno(r
, "Failed to enumerate links: %m");
280 for (i
= reply
; i
; i
= sd_netlink_message_next(i
)) {
281 if (!GREEDY_REALLOC0(links
, allocated
, c
+1))
284 r
= decode_link(i
, links
+ c
, patterns
);
290 r
= ethtool_get_link_info(&fd
, links
[c
].name
,
291 &links
[c
].autonegotiation
, &links
[c
].speed
,
292 &links
[c
].duplex
, &links
[c
].port
);
294 links
[c
].has_ethtool_link_info
= true;
299 typesafe_qsort(links
, c
, link_info_compare
);
302 for (j
= 0; j
< c
; j
++)
303 (void) acquire_link_bitrates(bus
, links
+ j
);
305 *ret
= TAKE_PTR(links
);
310 static int list_links(int argc
, char *argv
[], void *userdata
) {
311 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
312 _cleanup_free_ LinkInfo
*links
= NULL
;
313 _cleanup_(table_unrefp
) Table
*table
= NULL
;
317 r
= sd_netlink_open(&rtnl
);
319 return log_error_errno(r
, "Failed to connect to netlink: %m");
321 c
= acquire_link_info(NULL
, rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
325 (void) pager_open(arg_pager_flags
);
327 table
= table_new("IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
331 table_set_header(table
, arg_legend
);
333 assert_se(cell
= table_get_cell(table
, 0, 0));
334 (void) table_set_minimum_width(table
, cell
, 3);
335 (void) table_set_weight(table
, cell
, 0);
336 (void) table_set_ellipsize_percent(table
, cell
, 100);
337 (void) table_set_align_percent(table
, cell
, 100);
339 assert_se(cell
= table_get_cell(table
, 0, 1));
340 (void) table_set_ellipsize_percent(table
, cell
, 100);
342 for (i
= 0; i
< c
; i
++) {
343 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
344 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
345 const char *on_color_operational
, *off_color_operational
,
346 *on_color_setup
, *off_color_setup
;
347 char devid
[2 + DECIMAL_STR_MAX(int)];
348 _cleanup_free_
char *t
= NULL
;
350 (void) sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
351 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
353 r
= sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
354 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
355 setup_state
= strdup("unmanaged");
356 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
358 xsprintf(devid
, "n%i", links
[i
].ifindex
);
359 (void) sd_device_new_from_device_id(&d
, devid
);
361 t
= link_get_type_string(links
[i
].iftype
, d
);
363 r
= table_add_cell(table
, NULL
, TABLE_INT
, &links
[i
].ifindex
);
367 r
= table_add_many(table
,
368 TABLE_STRING
, links
[i
].name
,
369 TABLE_STRING
, strna(t
));
373 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(operational_state
));
377 (void) table_set_color(table
, cell
, on_color_operational
);
379 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(setup_state
));
383 (void) table_set_color(table
, cell
, on_color_setup
);
386 r
= table_print(table
, NULL
);
388 return log_error_errno(r
, "Failed to print table: %m");
391 printf("\n%i links listed.\n", c
);
396 /* IEEE Organizationally Unique Identifier vendor string */
397 static int ieee_oui(sd_hwdb
*hwdb
, const struct ether_addr
*mac
, char **ret
) {
398 const char *description
;
399 char modalias
[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc
;
410 /* skip commonly misused 00:00:00 (Xerox) prefix */
411 if (memcmp(mac
, "\0\0\0", 3) == 0)
414 xsprintf(modalias
, "OUI:" ETHER_ADDR_FORMAT_STR
,
415 ETHER_ADDR_FORMAT_VAL(*mac
));
417 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
421 desc
= strdup(description
);
430 static int get_gateway_description(
435 union in_addr_union
*gateway
,
436 char **gateway_description
) {
437 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
438 sd_netlink_message
*m
;
442 assert(ifindex
>= 0);
443 assert(IN_SET(family
, AF_INET
, AF_INET6
));
445 assert(gateway_description
);
447 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
451 r
= sd_netlink_message_request_dump(req
, true);
455 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
459 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
460 union in_addr_union gw
= IN_ADDR_NULL
;
461 struct ether_addr mac
= ETHER_ADDR_NULL
;
465 r
= sd_netlink_message_get_errno(m
);
467 log_error_errno(r
, "got error: %m");
471 r
= sd_netlink_message_get_type(m
, &type
);
473 log_error_errno(r
, "could not get type: %m");
477 if (type
!= RTM_NEWNEIGH
) {
478 log_error("type is not RTM_NEWNEIGH");
482 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
484 log_error_errno(r
, "could not get family: %m");
489 log_error("family is not correct");
493 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
495 log_error_errno(r
, "could not get ifindex: %m");
499 if (ifindex
> 0 && ifi
!= ifindex
)
504 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
510 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
519 if (!in_addr_equal(fam
, &gw
, gateway
))
522 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
526 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
536 static int dump_gateways(
541 _cleanup_free_
struct local_address
*local
= NULL
;
547 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
551 for (i
= 0; i
< n
; i
++) {
552 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
, *with_description
= NULL
;
554 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
558 r
= table_add_cell(table
, NULL
, TABLE_STRING
, i
== 0 ? "Gateway:" : "");
562 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
566 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
568 log_debug_errno(r
, "Could not get description of gateway: %m");
571 with_description
= strjoin(gateway
, " (", description
, ")");
572 if (!with_description
)
576 /* Show interface name for the entry if we show
577 * entries for all interfaces */
579 char name
[IF_NAMESIZE
+1];
581 if (format_ifname(local
[i
].ifindex
, name
))
582 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", with_description
?: gateway
, name
);
584 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", with_description
?: gateway
, local
[i
].ifindex
);
586 r
= table_add_cell(table
, NULL
, TABLE_STRING
, with_description
?: gateway
);
594 static int dump_addresses(
599 _cleanup_free_
struct local_address
*local
= NULL
;
605 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
609 for (i
= 0; i
< n
; i
++) {
610 _cleanup_free_
char *pretty
= NULL
;
612 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
616 r
= table_add_cell(table
, NULL
, TABLE_STRING
, i
== 0 ? "Address:" : "");
620 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
625 char name
[IF_NAMESIZE
+1];
627 if (format_ifname(local
[i
].ifindex
, name
))
628 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", pretty
, name
);
630 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", pretty
, local
[i
].ifindex
);
632 r
= table_add_cell(table
, NULL
, TABLE_STRING
, pretty
);
640 static int dump_address_labels(sd_netlink
*rtnl
) {
641 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
642 _cleanup_(table_unrefp
) Table
*table
= NULL
;
643 sd_netlink_message
*m
;
649 r
= sd_rtnl_message_new_addrlabel(rtnl
, &req
, RTM_GETADDRLABEL
, 0, AF_INET6
);
651 return log_error_errno(r
, "Could not allocate RTM_GETADDRLABEL message: %m");
653 r
= sd_netlink_message_request_dump(req
, true);
657 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
661 table
= table_new("Label", "Prefix/Prefixlen");
665 r
= table_set_sort(table
, 0, SIZE_MAX
);
669 assert_se(cell
= table_get_cell(table
, 0, 0));
670 (void) table_set_align_percent(table
, cell
, 100);
671 (void) table_set_ellipsize_percent(table
, cell
, 100);
673 assert_se(cell
= table_get_cell(table
, 0, 1));
674 (void) table_set_align_percent(table
, cell
, 100);
676 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
677 _cleanup_free_
char *pretty
= NULL
;
678 union in_addr_union prefix
= IN_ADDR_NULL
;
682 r
= sd_netlink_message_get_errno(m
);
684 log_error_errno(r
, "got error: %m");
688 r
= sd_netlink_message_read_u32(m
, IFAL_LABEL
, &label
);
689 if (r
< 0 && r
!= -ENODATA
) {
690 log_error_errno(r
, "Could not read IFAL_LABEL, ignoring: %m");
694 r
= sd_netlink_message_read_in6_addr(m
, IFAL_ADDRESS
, &prefix
.in6
);
698 r
= in_addr_to_string(AF_INET6
, &prefix
, &pretty
);
702 r
= sd_rtnl_message_addrlabel_get_prefixlen(m
, &prefixlen
);
706 r
= table_add_cell(table
, NULL
, TABLE_UINT32
, &label
);
710 r
= table_add_cell_stringf(table
, &cell
, "%s/%u", pretty
, prefixlen
);
715 return table_print(table
, NULL
);
718 static int list_address_labels(int argc
, char *argv
[], void *userdata
) {
719 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
722 r
= sd_netlink_open(&rtnl
);
724 return log_error_errno(r
, "Failed to connect to netlink: %m");
726 dump_address_labels(rtnl
);
731 static int open_lldp_neighbors(int ifindex
, FILE **ret
) {
732 _cleanup_free_
char *p
= NULL
;
735 if (asprintf(&p
, "/run/systemd/netif/lldp/%i", ifindex
) < 0)
746 static int next_lldp_neighbor(FILE *f
, sd_lldp_neighbor
**ret
) {
747 _cleanup_free_
void *raw
= NULL
;
755 l
= fread(&u
, 1, sizeof(u
), f
);
756 if (l
== 0 && feof(f
))
761 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
762 if (le64toh(u
) >= 4096)
765 raw
= new(uint8_t, le64toh(u
));
769 if (fread(raw
, 1, le64toh(u
), f
) != le64toh(u
))
772 r
= sd_lldp_neighbor_from_raw(ret
, raw
, le64toh(u
));
779 static int dump_lldp_neighbors(Table
*table
, const char *prefix
, int ifindex
) {
780 _cleanup_fclose_
FILE *f
= NULL
;
787 r
= open_lldp_neighbors(ifindex
, &f
);
794 const char *system_name
= NULL
, *port_id
= NULL
, *port_description
= NULL
;
795 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
796 _cleanup_free_
char *str
= NULL
;
798 r
= next_lldp_neighbor(f
, &n
);
804 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
808 r
= table_add_cell(table
, NULL
, TABLE_STRING
, c
== 0 ? prefix
: "");
812 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
813 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
814 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
816 if (asprintf(&str
, "%s on port %s%s%s%s",
817 strna(system_name
), strna(port_id
),
818 isempty(port_description
) ? "" : " (",
820 isempty(port_description
) ? "" : ")") < 0)
823 r
= table_add_cell(table
, NULL
, TABLE_STRING
, str
);
833 static int dump_ifindexes(Table
*table
, const char *prefix
, const int *ifindexes
) {
839 if (!ifindexes
|| ifindexes
[0] <= 0)
842 for (c
= 0; ifindexes
[c
] > 0; c
++) {
843 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
847 r
= table_add_cell(table
, NULL
, TABLE_STRING
, c
== 0 ? prefix
: "");
851 r
= table_add_cell(table
, NULL
, TABLE_IFINDEX
, ifindexes
+ c
);
859 static int dump_list(Table
*table
, const char *prefix
, char **l
) {
867 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
871 r
= table_add_cell(table
, NULL
, TABLE_STRING
, i
== l
? prefix
: "");
875 r
= table_add_cell(table
, NULL
, TABLE_STRING
, *i
);
883 #define DUMP_STATS_ONE(name, val_name) \
884 r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); \
887 r = table_add_cell(table, NULL, TABLE_STRING, name ":"); \
890 r = table_add_cell(table, NULL, info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
891 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
895 static int dump_statistics(Table
*table
, const LinkInfo
*info
) {
901 if (!info
->has_stats64
&& !info
->has_stats
)
904 DUMP_STATS_ONE("Rx Packets", rx_packets
);
905 DUMP_STATS_ONE("Tx Packets", tx_packets
);
906 DUMP_STATS_ONE("Rx Bytes", rx_bytes
);
907 DUMP_STATS_ONE("Tx Bytes", tx_bytes
);
908 DUMP_STATS_ONE("Rx Errors", rx_errors
);
909 DUMP_STATS_ONE("Tx Errors", tx_errors
);
910 DUMP_STATS_ONE("Rx Dropped", rx_dropped
);
911 DUMP_STATS_ONE("Tx Dropped", tx_dropped
);
912 DUMP_STATS_ONE("Multicast Packets", multicast
);
913 DUMP_STATS_ONE("Collisions", collisions
);
918 static int link_status_one(
921 const LinkInfo
*info
) {
923 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
924 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
925 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
926 char devid
[2 + DECIMAL_STR_MAX(int)];
927 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
928 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
929 const char *on_color_operational
, *off_color_operational
,
930 *on_color_setup
, *off_color_setup
;
931 _cleanup_free_
int *carrier_bound_to
= NULL
, *carrier_bound_by
= NULL
;
932 _cleanup_(table_unrefp
) Table
*table
= NULL
;
939 (void) sd_network_link_get_operational_state(info
->ifindex
, &operational_state
);
940 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
942 r
= sd_network_link_get_setup_state(info
->ifindex
, &setup_state
);
943 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
944 setup_state
= strdup("unmanaged");
945 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
947 (void) sd_network_link_get_dns(info
->ifindex
, &dns
);
948 (void) sd_network_link_get_search_domains(info
->ifindex
, &search_domains
);
949 (void) sd_network_link_get_route_domains(info
->ifindex
, &route_domains
);
950 (void) sd_network_link_get_ntp(info
->ifindex
, &ntp
);
952 xsprintf(devid
, "n%i", info
->ifindex
);
954 (void) sd_device_new_from_device_id(&d
, devid
);
957 (void) sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
958 (void) sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
959 (void) sd_device_get_property_value(d
, "ID_PATH", &path
);
961 if (sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
) < 0)
962 (void) sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
964 if (sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
) < 0)
965 (void) sd_device_get_property_value(d
, "ID_MODEL", &model
);
968 t
= link_get_type_string(info
->iftype
, d
);
970 (void) sd_network_link_get_network_file(info
->ifindex
, &network
);
972 (void) sd_network_link_get_carrier_bound_to(info
->ifindex
, &carrier_bound_to
);
973 (void) sd_network_link_get_carrier_bound_by(info
->ifindex
, &carrier_bound_by
);
975 table
= table_new("DOT", "KEY", "VALUE");
979 assert_se(cell
= table_get_cell(table
, 0, 0));
980 (void) table_set_ellipsize_percent(table
, cell
, 100);
982 assert_se(cell
= table_get_cell(table
, 0, 1));
983 (void) table_set_ellipsize_percent(table
, cell
, 100);
985 table_set_header(table
, false);
987 r
= table_add_cell(table
, &cell
, TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
));
990 (void) table_set_color(table
, cell
, on_color_operational
);
991 r
= table_add_cell_stringf(table
, &cell
, "%i: %s", info
->ifindex
, info
->name
);
994 (void) table_set_align_percent(table
, cell
, 0);
995 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
999 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1002 r
= table_add_cell(table
, &cell
, TABLE_STRING
, "Link File:");
1005 (void) table_set_align_percent(table
, cell
, 100);
1006 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(link
));
1010 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1013 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Network File:");
1016 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(network
));
1020 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1023 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Type:");
1026 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(t
));
1030 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1033 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "State:");
1036 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s (%s%s%s)",
1037 on_color_operational
, strna(operational_state
), off_color_operational
,
1038 on_color_setup
, strna(setup_state
), off_color_setup
);
1043 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1046 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Path:");
1049 r
= table_add_cell(table
, NULL
, TABLE_STRING
, path
);
1054 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1057 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Driver:");
1060 r
= table_add_cell(table
, NULL
, TABLE_STRING
, driver
);
1065 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1068 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Vendor:");
1071 r
= table_add_cell(table
, NULL
, TABLE_STRING
, vendor
);
1076 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1079 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Model:");
1082 r
= table_add_cell(table
, NULL
, TABLE_STRING
, model
);
1087 if (info
->has_mac_address
) {
1088 _cleanup_free_
char *description
= NULL
;
1089 char ea
[ETHER_ADDR_TO_STRING_MAX
];
1091 (void) ieee_oui(hwdb
, &info
->mac_address
, &description
);
1093 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1096 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "HW Address:");
1099 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s%s",
1100 ether_addr_to_string(&info
->mac_address
, ea
),
1101 description
? " (" : "",
1103 description
? ")" : "");
1108 if (info
->mtu
> 0) {
1109 char min_str
[DECIMAL_STR_MAX(uint32_t)], max_str
[DECIMAL_STR_MAX(uint32_t)];
1111 xsprintf(min_str
, "%" PRIu32
, info
->min_mtu
);
1112 xsprintf(max_str
, "%" PRIu32
, info
->max_mtu
);
1114 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1117 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "MTU:");
1120 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"%s%s%s%s%s%s%s",
1122 info
->min_mtu
> 0 || info
->max_mtu
> 0 ? " (" : "",
1123 info
->min_mtu
> 0 ? "min: " : "",
1124 info
->min_mtu
> 0 ? min_str
: "",
1125 info
->min_mtu
> 0 && info
->max_mtu
> 0 ? ", " : "",
1126 info
->max_mtu
> 0 ? "max: " : "",
1127 info
->max_mtu
> 0 ? max_str
: "",
1128 info
->min_mtu
> 0 || info
->max_mtu
> 0 ? ")" : "");
1133 if (info
->has_bitrates
) {
1134 char tx
[FORMAT_BYTES_MAX
], rx
[FORMAT_BYTES_MAX
];
1136 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1139 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Bit Rate (Tx/Rx):");
1143 r
= table_add_cell_stringf(table
, NULL
, "%sbps/%sbps",
1144 format_bytes_full(tx
, sizeof tx
, info
->tx_bitrate
, 0),
1145 format_bytes_full(rx
, sizeof rx
, info
->rx_bitrate
, 0));
1150 if (info
->has_tx_queues
|| info
->has_rx_queues
) {
1151 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1154 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Queue Length (Tx/Rx):");
1157 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"/%" PRIu32
, info
->tx_queues
, info
->rx_queues
);
1162 if (info
->has_ethtool_link_info
) {
1163 const char *duplex
= duplex_to_string(info
->duplex
);
1164 const char *port
= port_to_string(info
->port
);
1166 if (IN_SET(info
->autonegotiation
, AUTONEG_DISABLE
, AUTONEG_ENABLE
)) {
1167 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1170 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Auto negotiation:");
1173 r
= table_add_cell(table
, NULL
, TABLE_BOOLEAN
, &info
->autonegotiation
);
1178 if (info
->speed
> 0) {
1179 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1182 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Speed:");
1185 r
= table_add_cell(table
, NULL
, TABLE_BPS
, &info
->speed
);
1191 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1194 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Duplex:");
1197 r
= table_add_cell(table
, NULL
, TABLE_STRING
, duplex
);
1203 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1206 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Port:");
1209 r
= table_add_cell(table
, NULL
, TABLE_STRING
, port
);
1215 r
= dump_addresses(rtnl
, table
, info
->ifindex
);
1218 r
= dump_gateways(rtnl
, hwdb
, table
, info
->ifindex
);
1221 r
= dump_list(table
, "DNS:", dns
);
1224 r
= dump_list(table
, "Search Domains:", search_domains
);
1227 r
= dump_list(table
, "Route Domains:", route_domains
);
1230 r
= dump_list(table
, "NTP:", ntp
);
1233 r
= dump_ifindexes(table
, "Carrier Bound To:", carrier_bound_to
);
1236 r
= dump_ifindexes(table
, "Carrier Bound By:", carrier_bound_by
);
1240 (void) sd_network_link_get_timezone(info
->ifindex
, &tz
);
1242 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1245 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Time Zone:");
1248 r
= table_add_cell(table
, NULL
, TABLE_STRING
, tz
);
1253 r
= dump_lldp_neighbors(table
, "Connected To:", info
->ifindex
);
1257 r
= dump_statistics(table
, info
);
1261 return table_print(table
, NULL
);
1264 static int system_status(sd_netlink
*rtnl
, sd_hwdb
*hwdb
) {
1265 _cleanup_free_
char *operational_state
= NULL
;
1266 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
1267 const char *on_color_operational
, *off_color_operational
;
1268 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1274 (void) sd_network_get_operational_state(&operational_state
);
1275 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
1277 table
= table_new("DOT", "KEY", "VALUE");
1281 assert_se(cell
= table_get_cell(table
, 0, 0));
1282 (void) table_set_ellipsize_percent(table
, cell
, 100);
1284 assert_se(cell
= table_get_cell(table
, 0, 1));
1285 (void) table_set_align_percent(table
, cell
, 100);
1286 (void) table_set_ellipsize_percent(table
, cell
, 100);
1288 table_set_header(table
, false);
1290 r
= table_add_cell(table
, &cell
, TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
));
1293 (void) table_set_color(table
, cell
, on_color_operational
);
1295 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "State:");
1299 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(operational_state
));
1302 (void) table_set_color(table
, cell
, on_color_operational
);
1304 r
= dump_addresses(rtnl
, table
, 0);
1307 r
= dump_gateways(rtnl
, hwdb
, table
, 0);
1311 (void) sd_network_get_dns(&dns
);
1312 r
= dump_list(table
, "DNS:", dns
);
1316 (void) sd_network_get_search_domains(&search_domains
);
1317 r
= dump_list(table
, "Search Domains:", search_domains
);
1321 (void) sd_network_get_route_domains(&route_domains
);
1322 r
= dump_list(table
, "Route Domains:", route_domains
);
1326 (void) sd_network_get_ntp(&ntp
);
1327 r
= dump_list(table
, "NTP:", ntp
);
1331 return table_print(table
, NULL
);
1334 static int link_status(int argc
, char *argv
[], void *userdata
) {
1335 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1336 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1337 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
1338 _cleanup_free_ LinkInfo
*links
= NULL
;
1341 (void) pager_open(arg_pager_flags
);
1343 r
= sd_bus_open_system(&bus
);
1345 return log_error_errno(r
, "Failed to connect system bus: %m");
1347 r
= sd_netlink_open(&rtnl
);
1349 return log_error_errno(r
, "Failed to connect to netlink: %m");
1351 r
= sd_hwdb_new(&hwdb
);
1353 log_debug_errno(r
, "Failed to open hardware database: %m");
1356 c
= acquire_link_info(bus
, rtnl
, NULL
, &links
);
1358 return system_status(rtnl
, hwdb
);
1360 c
= acquire_link_info(bus
, rtnl
, argv
+ 1, &links
);
1364 for (i
= 0; i
< c
; i
++) {
1366 fputc('\n', stdout
);
1368 link_status_one(rtnl
, hwdb
, links
+ i
);
1374 static char *lldp_capabilities_to_string(uint16_t x
) {
1375 static const char characters
[] = {
1376 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1381 ret
= new(char, ELEMENTSOF(characters
) + 1);
1385 for (i
= 0; i
< ELEMENTSOF(characters
); i
++)
1386 ret
[i
] = (x
& (1U << i
)) ? characters
[i
] : '.';
1392 static void lldp_capabilities_legend(uint16_t x
) {
1393 unsigned w
, i
, cols
= columns();
1394 static const char* const table
[] = {
1398 "w - WLAN Access Point",
1401 "d - DOCSIS cable device",
1403 "c - Customer VLAN",
1405 "m - Two-port MAC Relay (TPMR)",
1411 printf("\nCapability Flags:\n");
1412 for (w
= 0, i
= 0; i
< ELEMENTSOF(table
); i
++)
1413 if (x
& (1U << i
) || arg_all
) {
1416 newline
= w
+ strlen(table
[i
]) + (w
== 0 ? 0 : 2) > cols
;
1419 w
+= printf("%s%s%s", newline
? "\n" : "", w
== 0 ? "" : "; ", table
[i
]);
1424 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
1425 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1426 _cleanup_free_ LinkInfo
*links
= NULL
;
1427 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1432 r
= sd_netlink_open(&rtnl
);
1434 return log_error_errno(r
, "Failed to connect to netlink: %m");
1436 c
= acquire_link_info(NULL
, rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
1440 (void) pager_open(arg_pager_flags
);
1442 table
= table_new("LINK",
1447 "PORT DESCRIPTION");
1451 table_set_header(table
, arg_legend
);
1453 assert_se(cell
= table_get_cell(table
, 0, 0));
1454 table_set_minimum_width(table
, cell
, 16);
1456 assert_se(cell
= table_get_cell(table
, 0, 1));
1457 table_set_minimum_width(table
, cell
, 17);
1459 assert_se(cell
= table_get_cell(table
, 0, 2));
1460 table_set_minimum_width(table
, cell
, 16);
1462 assert_se(cell
= table_get_cell(table
, 0, 3));
1463 table_set_minimum_width(table
, cell
, 11);
1465 assert_se(cell
= table_get_cell(table
, 0, 4));
1466 table_set_minimum_width(table
, cell
, 17);
1468 assert_se(cell
= table_get_cell(table
, 0, 5));
1469 table_set_minimum_width(table
, cell
, 16);
1471 for (i
= 0; i
< c
; i
++) {
1472 _cleanup_fclose_
FILE *f
= NULL
;
1474 r
= open_lldp_neighbors(links
[i
].ifindex
, &f
);
1478 log_warning_errno(r
, "Failed to open LLDP data for %i, ignoring: %m", links
[i
].ifindex
);
1483 _cleanup_free_
char *cid
= NULL
, *pid
= NULL
, *sname
= NULL
, *pdesc
= NULL
, *capabilities
= NULL
;
1484 const char *chassis_id
= NULL
, *port_id
= NULL
, *system_name
= NULL
, *port_description
= NULL
;
1485 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
1488 r
= next_lldp_neighbor(f
, &n
);
1490 log_warning_errno(r
, "Failed to read neighbor data: %m");
1496 (void) sd_lldp_neighbor_get_chassis_id_as_string(n
, &chassis_id
);
1497 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
1498 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
1499 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
1502 cid
= ellipsize(chassis_id
, 17, 100);
1508 pid
= ellipsize(port_id
, 17, 100);
1514 sname
= ellipsize(system_name
, 16, 100);
1516 system_name
= sname
;
1519 if (port_description
) {
1520 pdesc
= ellipsize(port_description
, 16, 100);
1522 port_description
= pdesc
;
1525 if (sd_lldp_neighbor_get_enabled_capabilities(n
, &cc
) >= 0) {
1526 capabilities
= lldp_capabilities_to_string(cc
);
1530 r
= table_add_many(table
,
1531 TABLE_STRING
, links
[i
].name
,
1532 TABLE_STRING
, strna(chassis_id
),
1533 TABLE_STRING
, strna(system_name
),
1534 TABLE_STRING
, strna(capabilities
),
1535 TABLE_STRING
, strna(port_id
),
1536 TABLE_STRING
, strna(port_description
));
1544 r
= table_print(table
, NULL
);
1549 lldp_capabilities_legend(all
);
1550 printf("\n%i neighbors listed.\n", m
);
1556 static int link_delete_send_message(sd_netlink
*rtnl
, int index
) {
1557 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
1562 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_DELLINK
, index
);
1564 return rtnl_log_create_error(r
);
1566 r
= sd_netlink_call(rtnl
, req
, 0, NULL
);
1573 static int link_delete(int argc
, char *argv
[], void *userdata
) {
1574 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1575 _cleanup_set_free_ Set
*indexes
= NULL
;
1580 r
= sd_netlink_open(&rtnl
);
1582 return log_error_errno(r
, "Failed to connect to netlink: %m");
1584 indexes
= set_new(NULL
);
1588 for (i
= 1; i
< argc
; i
++) {
1589 r
= parse_ifindex_or_ifname(argv
[i
], &index
);
1591 return log_error_errno(r
, "Failed to resolve interface %s", argv
[i
]);
1593 r
= set_put(indexes
, INT_TO_PTR(index
));
1598 SET_FOREACH(p
, indexes
, j
) {
1599 r
= link_delete_send_message(rtnl
, PTR_TO_INT(p
));
1601 char ifname
[IF_NAMESIZE
+ 1];
1603 if (format_ifname(index
, ifname
))
1604 return log_error_errno(r
, "Failed to delete interface %s: %m", ifname
);
1606 return log_error_errno(r
, "Failed to delete interface %d: %m", index
);
1613 static int help(void) {
1614 _cleanup_free_
char *link
= NULL
;
1617 r
= terminal_urlify_man("networkctl", "1", &link
);
1621 printf("%s [OPTIONS...]\n\n"
1622 "Query and control the networking subsystem.\n\n"
1623 " -h --help Show this help\n"
1624 " --version Show package version\n"
1625 " --no-pager Do not pipe output into a pager\n"
1626 " --no-legend Do not show the headers and footers\n"
1627 " -a --all Show status for all links\n"
1628 " -s --stats Show detailed link statics\n"
1630 " list [PATTERN...] List links\n"
1631 " status [PATTERN...] Show link status\n"
1632 " lldp [PATTERN...] Show LLDP neighbors\n"
1633 " label Show current address label entries in the kernel\n"
1634 " delete DEVICES Delete virtual netdevs\n"
1635 "\nSee the %s for details.\n"
1636 , program_invocation_short_name
1643 static int parse_argv(int argc
, char *argv
[]) {
1646 ARG_VERSION
= 0x100,
1651 static const struct option options
[] = {
1652 { "help", no_argument
, NULL
, 'h' },
1653 { "version", no_argument
, NULL
, ARG_VERSION
},
1654 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1655 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1656 { "all", no_argument
, NULL
, 'a' },
1657 { "stats", no_argument
, NULL
, 's' },
1666 while ((c
= getopt_long(argc
, argv
, "has", options
, NULL
)) >= 0) {
1677 arg_pager_flags
|= PAGER_DISABLE
;
1696 assert_not_reached("Unhandled option");
1703 static int networkctl_main(int argc
, char *argv
[]) {
1704 static const Verb verbs
[] = {
1705 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, list_links
},
1706 { "status", VERB_ANY
, VERB_ANY
, 0, link_status
},
1707 { "lldp", VERB_ANY
, VERB_ANY
, 0, link_lldp_status
},
1708 { "label", VERB_ANY
, VERB_ANY
, 0, list_address_labels
},
1709 { "delete", 2, VERB_ANY
, 0, link_delete
},
1713 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1716 static void warn_networkd_missing(void) {
1718 if (access("/run/systemd/netif/state", F_OK
) >= 0)
1721 fprintf(stderr
, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1724 static int run(int argc
, char* argv
[]) {
1727 log_show_color(true);
1728 log_parse_environment();
1731 r
= parse_argv(argc
, argv
);
1735 warn_networkd_missing();
1737 return networkctl_main(argc
, argv
);
1740 DEFINE_MAIN_FUNCTION(run
);