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"
26 #include "format-table.h"
27 #include "format-util.h"
28 #include "hwdb-util.h"
29 #include "local-addresses.h"
30 #include "locale-util.h"
32 #include "main-func.h"
33 #include "netlink-util.h"
35 #include "parse-util.h"
36 #include "pretty-print.h"
38 #include "socket-util.h"
39 #include "sort-util.h"
40 #include "sparse-endian.h"
41 #include "stdio-util.h"
42 #include "string-table.h"
43 #include "string-util.h"
46 #include "terminal-util.h"
49 static PagerFlags arg_pager_flags
= 0;
50 static bool arg_legend
= true;
51 static bool arg_all
= false;
52 static bool arg_stats
= false;
54 static char *link_get_type_string(unsigned short iftype
, sd_device
*d
) {
55 const char *t
, *devtype
;
59 sd_device_get_devtype(d
, &devtype
) >= 0 &&
61 return strdup(devtype
);
63 t
= arphrd_to_name(iftype
);
75 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
79 if (STRPTR_IN_SET(state
, "routable", "enslaved")) {
80 *on
= ansi_highlight_green();
82 } else if (streq_ptr(state
, "degraded")) {
83 *on
= ansi_highlight_yellow();
89 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
93 if (streq_ptr(state
, "configured")) {
94 *on
= ansi_highlight_green();
96 } else if (streq_ptr(state
, "configuring")) {
97 *on
= ansi_highlight_yellow();
99 } else if (STRPTR_IN_SET(state
, "failed", "linger")) {
100 *on
= ansi_highlight_red();
101 *off
= ansi_normal();
106 typedef struct LinkInfo
{
107 char name
[IFNAMSIZ
+1];
109 unsigned short iftype
;
110 struct ether_addr mac_address
;
118 struct rtnl_link_stats64 stats64
;
119 struct rtnl_link_stats stats
;
125 bool has_mac_address
:1;
126 bool has_tx_queues
:1;
127 bool has_rx_queues
:1;
133 static int link_info_compare(const LinkInfo
*a
, const LinkInfo
*b
) {
134 return CMP(a
->ifindex
, b
->ifindex
);
137 static int decode_link(sd_netlink_message
*m
, LinkInfo
*info
, char **patterns
) {
145 r
= sd_netlink_message_get_type(m
, &type
);
149 if (type
!= RTM_NEWLINK
)
152 r
= sd_rtnl_message_link_get_ifindex(m
, &ifindex
);
156 r
= sd_netlink_message_read_string(m
, IFLA_IFNAME
, &name
);
161 char str
[DECIMAL_STR_MAX(int)];
163 xsprintf(str
, "%i", ifindex
);
165 if (!strv_fnmatch(patterns
, str
, 0) && !strv_fnmatch(patterns
, name
, 0))
169 r
= sd_rtnl_message_link_get_type(m
, &info
->iftype
);
173 strscpy(info
->name
, sizeof info
->name
, name
);
174 info
->ifindex
= ifindex
;
176 info
->has_mac_address
=
177 sd_netlink_message_read_ether_addr(m
, IFLA_ADDRESS
, &info
->mac_address
) >= 0 &&
178 memcmp(&info
->mac_address
, ÐER_ADDR_NULL
, sizeof(struct ether_addr
)) != 0;
180 (void) sd_netlink_message_read_u32(m
, IFLA_MTU
, &info
->mtu
);
181 (void) sd_netlink_message_read_u32(m
, IFLA_MIN_MTU
, &info
->min_mtu
);
182 (void) sd_netlink_message_read_u32(m
, IFLA_MAX_MTU
, &info
->max_mtu
);
184 info
->has_rx_queues
=
185 sd_netlink_message_read_u32(m
, IFLA_NUM_RX_QUEUES
, &info
->rx_queues
) >= 0 &&
188 info
->has_tx_queues
=
189 sd_netlink_message_read_u32(m
, IFLA_NUM_TX_QUEUES
, &info
->tx_queues
) >= 0 &&
192 if (sd_netlink_message_read(m
, IFLA_STATS64
, sizeof info
->stats64
, &info
->stats64
) >= 0)
193 info
->has_stats64
= true;
194 else if (sd_netlink_message_read(m
, IFLA_STATS
, sizeof info
->stats
, &info
->stats
) >= 0)
195 info
->has_stats
= true;
200 static int acquire_link_bitrates(sd_bus
*bus
, LinkInfo
*link
) {
201 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
202 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
203 _cleanup_free_
char *path
= NULL
, *ifindex_str
= NULL
;
206 if (asprintf(&ifindex_str
, "%i", link
->ifindex
) < 0)
209 r
= sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str
, &path
);
213 r
= sd_bus_call_method(
215 "org.freedesktop.network1",
217 "org.freedesktop.DBus.Properties",
222 "org.freedesktop.network1.Link",
225 bool quiet
= sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_PROPERTY
) ||
226 sd_bus_error_has_name(&error
, BUS_ERROR_SPEED_METER_INACTIVE
);
228 return log_full_errno(quiet
? LOG_DEBUG
: LOG_WARNING
,
229 r
, "Failed to query link bit rates: %s", bus_error_message(&error
, r
));
232 r
= sd_bus_message_enter_container(reply
, 'v', "(dd)");
234 return bus_log_parse_error(r
);
236 r
= sd_bus_message_read(reply
, "(dd)", &link
->tx_bitrate
, &link
->rx_bitrate
);
238 return bus_log_parse_error(r
);
240 r
= sd_bus_message_exit_container(reply
);
242 return bus_log_parse_error(r
);
244 link
->has_bitrates
= link
->tx_bitrate
>= 0 && link
->rx_bitrate
>= 0;
249 static int acquire_link_info(sd_bus
*bus
, sd_netlink
*rtnl
, char **patterns
, LinkInfo
**ret
) {
250 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
251 _cleanup_free_ LinkInfo
*links
= NULL
;
252 size_t allocated
= 0, c
= 0, j
;
253 sd_netlink_message
*i
;
259 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
261 return rtnl_log_create_error(r
);
263 r
= sd_netlink_message_request_dump(req
, true);
265 return rtnl_log_create_error(r
);
267 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
269 return log_error_errno(r
, "Failed to enumerate links: %m");
271 for (i
= reply
; i
; i
= sd_netlink_message_next(i
)) {
272 if (!GREEDY_REALLOC0(links
, allocated
, c
+1))
275 r
= decode_link(i
, links
+ c
, patterns
);
282 typesafe_qsort(links
, c
, link_info_compare
);
285 for (j
= 0; j
< c
; j
++)
286 (void) acquire_link_bitrates(bus
, links
+ j
);
288 *ret
= TAKE_PTR(links
);
293 static int list_links(int argc
, char *argv
[], void *userdata
) {
294 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
295 _cleanup_free_ LinkInfo
*links
= NULL
;
296 _cleanup_(table_unrefp
) Table
*table
= NULL
;
300 r
= sd_netlink_open(&rtnl
);
302 return log_error_errno(r
, "Failed to connect to netlink: %m");
304 c
= acquire_link_info(NULL
, rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
308 (void) pager_open(arg_pager_flags
);
310 table
= table_new("IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
314 table_set_header(table
, arg_legend
);
316 assert_se(cell
= table_get_cell(table
, 0, 0));
317 (void) table_set_minimum_width(table
, cell
, 3);
318 (void) table_set_weight(table
, cell
, 0);
319 (void) table_set_ellipsize_percent(table
, cell
, 100);
320 (void) table_set_align_percent(table
, cell
, 100);
322 assert_se(cell
= table_get_cell(table
, 0, 1));
323 (void) table_set_ellipsize_percent(table
, cell
, 100);
325 for (i
= 0; i
< c
; i
++) {
326 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
327 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
328 const char *on_color_operational
, *off_color_operational
,
329 *on_color_setup
, *off_color_setup
;
330 char devid
[2 + DECIMAL_STR_MAX(int)];
331 _cleanup_free_
char *t
= NULL
;
333 (void) sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
334 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
336 r
= sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
337 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
338 setup_state
= strdup("unmanaged");
339 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
341 xsprintf(devid
, "n%i", links
[i
].ifindex
);
342 (void) sd_device_new_from_device_id(&d
, devid
);
344 t
= link_get_type_string(links
[i
].iftype
, d
);
346 r
= table_add_cell(table
, NULL
, TABLE_INT
, &links
[i
].ifindex
);
350 r
= table_add_many(table
,
351 TABLE_STRING
, links
[i
].name
,
352 TABLE_STRING
, strna(t
));
356 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(operational_state
));
360 (void) table_set_color(table
, cell
, on_color_operational
);
362 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(setup_state
));
366 (void) table_set_color(table
, cell
, on_color_setup
);
369 r
= table_print(table
, NULL
);
371 return log_error_errno(r
, "Failed to print table: %m");
374 printf("\n%i links listed.\n", c
);
379 /* IEEE Organizationally Unique Identifier vendor string */
380 static int ieee_oui(sd_hwdb
*hwdb
, const struct ether_addr
*mac
, char **ret
) {
381 const char *description
;
382 char modalias
[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc
;
393 /* skip commonly misused 00:00:00 (Xerox) prefix */
394 if (memcmp(mac
, "\0\0\0", 3) == 0)
397 xsprintf(modalias
, "OUI:" ETHER_ADDR_FORMAT_STR
,
398 ETHER_ADDR_FORMAT_VAL(*mac
));
400 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
404 desc
= strdup(description
);
413 static int get_gateway_description(
418 union in_addr_union
*gateway
,
419 char **gateway_description
) {
420 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
421 sd_netlink_message
*m
;
425 assert(ifindex
>= 0);
426 assert(IN_SET(family
, AF_INET
, AF_INET6
));
428 assert(gateway_description
);
430 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
434 r
= sd_netlink_message_request_dump(req
, true);
438 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
442 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
443 union in_addr_union gw
= IN_ADDR_NULL
;
444 struct ether_addr mac
= ETHER_ADDR_NULL
;
448 r
= sd_netlink_message_get_errno(m
);
450 log_error_errno(r
, "got error: %m");
454 r
= sd_netlink_message_get_type(m
, &type
);
456 log_error_errno(r
, "could not get type: %m");
460 if (type
!= RTM_NEWNEIGH
) {
461 log_error("type is not RTM_NEWNEIGH");
465 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
467 log_error_errno(r
, "could not get family: %m");
472 log_error("family is not correct");
476 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
478 log_error_errno(r
, "could not get ifindex: %m");
482 if (ifindex
> 0 && ifi
!= ifindex
)
487 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
493 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
502 if (!in_addr_equal(fam
, &gw
, gateway
))
505 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
509 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
519 static int dump_gateways(
524 _cleanup_free_
struct local_address
*local
= NULL
;
530 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
534 for (i
= 0; i
< n
; i
++) {
535 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
, *with_description
= NULL
;
537 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
541 r
= table_add_cell(table
, NULL
, TABLE_STRING
, i
== 0 ? "Gateway:" : "");
545 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
549 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
551 log_debug_errno(r
, "Could not get description of gateway: %m");
554 with_description
= strjoin(gateway
, " (", description
, ")");
555 if (!with_description
)
559 /* Show interface name for the entry if we show
560 * entries for all interfaces */
562 char name
[IF_NAMESIZE
+1];
564 if (format_ifname(local
[i
].ifindex
, name
))
565 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", with_description
?: gateway
, name
);
567 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", with_description
?: gateway
, local
[i
].ifindex
);
569 r
= table_add_cell(table
, NULL
, TABLE_STRING
, with_description
?: gateway
);
577 static int dump_addresses(
582 _cleanup_free_
struct local_address
*local
= NULL
;
588 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
592 for (i
= 0; i
< n
; i
++) {
593 _cleanup_free_
char *pretty
= NULL
;
595 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
599 r
= table_add_cell(table
, NULL
, TABLE_STRING
, i
== 0 ? "Address:" : "");
603 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
608 char name
[IF_NAMESIZE
+1];
610 if (format_ifname(local
[i
].ifindex
, name
))
611 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", pretty
, name
);
613 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", pretty
, local
[i
].ifindex
);
615 r
= table_add_cell(table
, NULL
, TABLE_STRING
, pretty
);
623 static int dump_address_labels(sd_netlink
*rtnl
) {
624 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
625 _cleanup_(table_unrefp
) Table
*table
= NULL
;
626 sd_netlink_message
*m
;
632 r
= sd_rtnl_message_new_addrlabel(rtnl
, &req
, RTM_GETADDRLABEL
, 0, AF_INET6
);
634 return log_error_errno(r
, "Could not allocate RTM_GETADDRLABEL message: %m");
636 r
= sd_netlink_message_request_dump(req
, true);
640 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
644 table
= table_new("Label", "Prefix/Prefixlen");
648 r
= table_set_sort(table
, 0, SIZE_MAX
);
652 assert_se(cell
= table_get_cell(table
, 0, 0));
653 (void) table_set_align_percent(table
, cell
, 100);
654 (void) table_set_ellipsize_percent(table
, cell
, 100);
656 assert_se(cell
= table_get_cell(table
, 0, 1));
657 (void) table_set_align_percent(table
, cell
, 100);
659 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
660 _cleanup_free_
char *pretty
= NULL
;
661 union in_addr_union prefix
= IN_ADDR_NULL
;
665 r
= sd_netlink_message_get_errno(m
);
667 log_error_errno(r
, "got error: %m");
671 r
= sd_netlink_message_read_u32(m
, IFAL_LABEL
, &label
);
672 if (r
< 0 && r
!= -ENODATA
) {
673 log_error_errno(r
, "Could not read IFAL_LABEL, ignoring: %m");
677 r
= sd_netlink_message_read_in6_addr(m
, IFAL_ADDRESS
, &prefix
.in6
);
681 r
= in_addr_to_string(AF_INET6
, &prefix
, &pretty
);
685 r
= sd_rtnl_message_addrlabel_get_prefixlen(m
, &prefixlen
);
689 r
= table_add_cell(table
, NULL
, TABLE_UINT32
, &label
);
693 r
= table_add_cell_stringf(table
, &cell
, "%s/%u", pretty
, prefixlen
);
698 return table_print(table
, NULL
);
701 static int list_address_labels(int argc
, char *argv
[], void *userdata
) {
702 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
705 r
= sd_netlink_open(&rtnl
);
707 return log_error_errno(r
, "Failed to connect to netlink: %m");
709 dump_address_labels(rtnl
);
714 static int open_lldp_neighbors(int ifindex
, FILE **ret
) {
715 _cleanup_free_
char *p
= NULL
;
718 if (asprintf(&p
, "/run/systemd/netif/lldp/%i", ifindex
) < 0)
729 static int next_lldp_neighbor(FILE *f
, sd_lldp_neighbor
**ret
) {
730 _cleanup_free_
void *raw
= NULL
;
738 l
= fread(&u
, 1, sizeof(u
), f
);
739 if (l
== 0 && feof(f
))
744 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
745 if (le64toh(u
) >= 4096)
748 raw
= new(uint8_t, le64toh(u
));
752 if (fread(raw
, 1, le64toh(u
), f
) != le64toh(u
))
755 r
= sd_lldp_neighbor_from_raw(ret
, raw
, le64toh(u
));
762 static int dump_lldp_neighbors(Table
*table
, const char *prefix
, int ifindex
) {
763 _cleanup_fclose_
FILE *f
= NULL
;
770 r
= open_lldp_neighbors(ifindex
, &f
);
777 const char *system_name
= NULL
, *port_id
= NULL
, *port_description
= NULL
;
778 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
779 _cleanup_free_
char *str
= NULL
;
781 r
= next_lldp_neighbor(f
, &n
);
787 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
791 r
= table_add_cell(table
, NULL
, TABLE_STRING
, c
== 0 ? prefix
: "");
795 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
796 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
797 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
799 if (asprintf(&str
, "%s on port %s%s%s%s",
800 strna(system_name
), strna(port_id
),
801 isempty(port_description
) ? "" : " (",
803 isempty(port_description
) ? "" : ")") < 0)
806 r
= table_add_cell(table
, NULL
, TABLE_STRING
, str
);
816 static int dump_ifindexes(Table
*table
, const char *prefix
, const int *ifindexes
) {
822 if (!ifindexes
|| ifindexes
[0] <= 0)
825 for (c
= 0; ifindexes
[c
] > 0; c
++) {
826 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
830 r
= table_add_cell(table
, NULL
, TABLE_STRING
, c
== 0 ? prefix
: "");
834 r
= table_add_cell(table
, NULL
, TABLE_IFINDEX
, ifindexes
+ c
);
842 static int dump_list(Table
*table
, const char *prefix
, char **l
) {
850 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
854 r
= table_add_cell(table
, NULL
, TABLE_STRING
, i
== l
? prefix
: "");
858 r
= table_add_cell(table
, NULL
, TABLE_STRING
, *i
);
866 #define DUMP_STATS_ONE(name, val_name) \
867 r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); \
870 r = table_add_cell(table, NULL, TABLE_STRING, name ":"); \
873 r = table_add_cell(table, NULL, info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
874 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
878 static int dump_statistics(Table
*table
, const LinkInfo
*info
) {
884 if (!info
->has_stats64
&& !info
->has_stats
)
887 DUMP_STATS_ONE("Rx Packets", rx_packets
);
888 DUMP_STATS_ONE("Tx Packets", tx_packets
);
889 DUMP_STATS_ONE("Rx Bytes", rx_bytes
);
890 DUMP_STATS_ONE("Tx Bytes", tx_bytes
);
891 DUMP_STATS_ONE("Rx Errors", rx_errors
);
892 DUMP_STATS_ONE("Tx Errors", tx_errors
);
893 DUMP_STATS_ONE("Rx Dropped", rx_dropped
);
894 DUMP_STATS_ONE("Tx Dropped", tx_dropped
);
895 DUMP_STATS_ONE("Multicast Packets", multicast
);
896 DUMP_STATS_ONE("Collisions", collisions
);
901 static const struct {
905 { .val
= 1e15
, .str
= "P" },
906 { .val
= 1e12
, .str
= "T" },
907 { .val
= 1e9
, .str
= "G" },
908 { .val
= 1e6
, .str
= "M" },
909 { .val
= 1e3
, .str
= "k" },
912 static void get_prefix(double val
, double *ret_div
, const char **ret_prefix
) {
916 for (size_t i
= 0; i
< ELEMENTSOF(prefix_table
); i
++)
917 if (val
> prefix_table
[i
].val
) {
918 *ret_div
= prefix_table
[i
].val
;
919 *ret_prefix
= prefix_table
[i
].str
;
927 static int link_status_one(
930 const LinkInfo
*info
) {
932 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
933 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
934 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
935 char devid
[2 + DECIMAL_STR_MAX(int)];
936 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
937 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
938 const char *on_color_operational
, *off_color_operational
,
939 *on_color_setup
, *off_color_setup
;
940 _cleanup_free_
int *carrier_bound_to
= NULL
, *carrier_bound_by
= NULL
;
941 _cleanup_(table_unrefp
) Table
*table
= NULL
;
948 (void) sd_network_link_get_operational_state(info
->ifindex
, &operational_state
);
949 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
951 r
= sd_network_link_get_setup_state(info
->ifindex
, &setup_state
);
952 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
953 setup_state
= strdup("unmanaged");
954 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
956 (void) sd_network_link_get_dns(info
->ifindex
, &dns
);
957 (void) sd_network_link_get_search_domains(info
->ifindex
, &search_domains
);
958 (void) sd_network_link_get_route_domains(info
->ifindex
, &route_domains
);
959 (void) sd_network_link_get_ntp(info
->ifindex
, &ntp
);
961 xsprintf(devid
, "n%i", info
->ifindex
);
963 (void) sd_device_new_from_device_id(&d
, devid
);
966 (void) sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
967 (void) sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
968 (void) sd_device_get_property_value(d
, "ID_PATH", &path
);
970 if (sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
) < 0)
971 (void) sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
973 if (sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
) < 0)
974 (void) sd_device_get_property_value(d
, "ID_MODEL", &model
);
977 t
= link_get_type_string(info
->iftype
, d
);
979 (void) sd_network_link_get_network_file(info
->ifindex
, &network
);
981 (void) sd_network_link_get_carrier_bound_to(info
->ifindex
, &carrier_bound_to
);
982 (void) sd_network_link_get_carrier_bound_by(info
->ifindex
, &carrier_bound_by
);
984 table
= table_new("DOT", "KEY", "VALUE");
988 assert_se(cell
= table_get_cell(table
, 0, 0));
989 (void) table_set_ellipsize_percent(table
, cell
, 100);
991 assert_se(cell
= table_get_cell(table
, 0, 1));
992 (void) table_set_ellipsize_percent(table
, cell
, 100);
994 table_set_header(table
, false);
996 r
= table_add_cell(table
, &cell
, TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
));
999 (void) table_set_color(table
, cell
, on_color_operational
);
1000 r
= table_add_cell_stringf(table
, &cell
, "%i: %s", info
->ifindex
, info
->name
);
1003 (void) table_set_align_percent(table
, cell
, 0);
1004 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1008 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1011 r
= table_add_cell(table
, &cell
, TABLE_STRING
, "Link File:");
1014 (void) table_set_align_percent(table
, cell
, 100);
1015 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(link
));
1019 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1022 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Network File:");
1025 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(network
));
1029 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1032 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Type:");
1035 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(t
));
1039 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1042 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "State:");
1045 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s (%s%s%s)",
1046 on_color_operational
, strna(operational_state
), off_color_operational
,
1047 on_color_setup
, strna(setup_state
), off_color_setup
);
1052 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1055 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Path:");
1058 r
= table_add_cell(table
, NULL
, TABLE_STRING
, path
);
1063 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1066 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Driver:");
1069 r
= table_add_cell(table
, NULL
, TABLE_STRING
, driver
);
1074 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1077 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Vendor:");
1080 r
= table_add_cell(table
, NULL
, TABLE_STRING
, vendor
);
1085 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1088 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Model:");
1091 r
= table_add_cell(table
, NULL
, TABLE_STRING
, model
);
1096 if (info
->has_mac_address
) {
1097 _cleanup_free_
char *description
= NULL
;
1098 char ea
[ETHER_ADDR_TO_STRING_MAX
];
1100 (void) ieee_oui(hwdb
, &info
->mac_address
, &description
);
1102 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1105 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "HW Address:");
1108 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s%s",
1109 ether_addr_to_string(&info
->mac_address
, ea
),
1110 description
? " (" : "",
1112 description
? ")" : "");
1117 if (info
->mtu
> 0) {
1118 char min_str
[DECIMAL_STR_MAX(uint32_t)], max_str
[DECIMAL_STR_MAX(uint32_t)];
1120 xsprintf(min_str
, "%" PRIu32
, info
->min_mtu
);
1121 xsprintf(max_str
, "%" PRIu32
, info
->max_mtu
);
1123 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1126 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "MTU:");
1129 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"%s%s%s%s%s%s%s",
1131 info
->min_mtu
> 0 || info
->max_mtu
> 0 ? " (" : "",
1132 info
->min_mtu
> 0 ? "min: " : "",
1133 info
->min_mtu
> 0 ? min_str
: "",
1134 info
->min_mtu
> 0 && info
->max_mtu
> 0 ? ", " : "",
1135 info
->max_mtu
> 0 ? "max: " : "",
1136 info
->max_mtu
> 0 ? max_str
: "",
1137 info
->min_mtu
> 0 || info
->max_mtu
> 0 ? ")" : "");
1142 if (info
->has_bitrates
) {
1143 const char *tx_prefix
, *rx_prefix
;
1144 double tx_div
, rx_div
;
1146 get_prefix(info
->tx_bitrate
, &tx_div
, &tx_prefix
);
1147 get_prefix(info
->rx_bitrate
, &rx_div
, &rx_prefix
);
1149 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1152 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Bit Rate (Tx/Rx):");
1156 r
= table_add_cell_stringf(table
, NULL
, "%.4g %sbps/%.4g %sbps",
1157 info
->tx_bitrate
/ tx_div
, strempty(tx_prefix
),
1158 info
->rx_bitrate
/ rx_div
, strempty(rx_prefix
));
1163 if (info
->has_tx_queues
|| info
->has_rx_queues
) {
1164 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1167 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Queue Length (Tx/Rx):");
1170 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"/%" PRIu32
, info
->tx_queues
, info
->rx_queues
);
1175 r
= dump_addresses(rtnl
, table
, info
->ifindex
);
1178 r
= dump_gateways(rtnl
, hwdb
, table
, info
->ifindex
);
1181 r
= dump_list(table
, "DNS:", dns
);
1184 r
= dump_list(table
, "Search Domains:", search_domains
);
1187 r
= dump_list(table
, "Route Domains:", route_domains
);
1190 r
= dump_list(table
, "NTP:", ntp
);
1193 r
= dump_ifindexes(table
, "Carrier Bound To:", carrier_bound_to
);
1196 r
= dump_ifindexes(table
, "Carrier Bound By:", carrier_bound_by
);
1200 (void) sd_network_link_get_timezone(info
->ifindex
, &tz
);
1202 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1205 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "Time Zone:");
1208 r
= table_add_cell(table
, NULL
, TABLE_STRING
, tz
);
1213 r
= dump_lldp_neighbors(table
, "Connected To:", info
->ifindex
);
1217 r
= dump_statistics(table
, info
);
1221 return table_print(table
, NULL
);
1224 static int system_status(sd_netlink
*rtnl
, sd_hwdb
*hwdb
) {
1225 _cleanup_free_
char *operational_state
= NULL
;
1226 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
1227 const char *on_color_operational
, *off_color_operational
;
1228 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1234 (void) sd_network_get_operational_state(&operational_state
);
1235 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
1237 table
= table_new("DOT", "KEY", "VALUE");
1241 assert_se(cell
= table_get_cell(table
, 0, 0));
1242 (void) table_set_ellipsize_percent(table
, cell
, 100);
1244 assert_se(cell
= table_get_cell(table
, 0, 1));
1245 (void) table_set_align_percent(table
, cell
, 100);
1246 (void) table_set_ellipsize_percent(table
, cell
, 100);
1248 table_set_header(table
, false);
1250 r
= table_add_cell(table
, &cell
, TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
));
1253 (void) table_set_color(table
, cell
, on_color_operational
);
1255 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "State:");
1259 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(operational_state
));
1262 (void) table_set_color(table
, cell
, on_color_operational
);
1264 r
= dump_addresses(rtnl
, table
, 0);
1267 r
= dump_gateways(rtnl
, hwdb
, table
, 0);
1271 (void) sd_network_get_dns(&dns
);
1272 r
= dump_list(table
, "DNS:", dns
);
1276 (void) sd_network_get_search_domains(&search_domains
);
1277 r
= dump_list(table
, "Search Domains:", search_domains
);
1281 (void) sd_network_get_route_domains(&route_domains
);
1282 r
= dump_list(table
, "Route Domains:", route_domains
);
1286 (void) sd_network_get_ntp(&ntp
);
1287 r
= dump_list(table
, "NTP:", ntp
);
1291 return table_print(table
, NULL
);
1294 static int link_status(int argc
, char *argv
[], void *userdata
) {
1295 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1296 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1297 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
1298 _cleanup_free_ LinkInfo
*links
= NULL
;
1301 (void) pager_open(arg_pager_flags
);
1303 r
= sd_bus_open_system(&bus
);
1305 return log_error_errno(r
, "Failed to connect system bus: %m");
1307 r
= sd_netlink_open(&rtnl
);
1309 return log_error_errno(r
, "Failed to connect to netlink: %m");
1311 r
= sd_hwdb_new(&hwdb
);
1313 log_debug_errno(r
, "Failed to open hardware database: %m");
1316 c
= acquire_link_info(bus
, rtnl
, NULL
, &links
);
1318 return system_status(rtnl
, hwdb
);
1320 c
= acquire_link_info(bus
, rtnl
, argv
+ 1, &links
);
1324 for (i
= 0; i
< c
; i
++) {
1326 fputc('\n', stdout
);
1328 link_status_one(rtnl
, hwdb
, links
+ i
);
1334 static char *lldp_capabilities_to_string(uint16_t x
) {
1335 static const char characters
[] = {
1336 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1341 ret
= new(char, ELEMENTSOF(characters
) + 1);
1345 for (i
= 0; i
< ELEMENTSOF(characters
); i
++)
1346 ret
[i
] = (x
& (1U << i
)) ? characters
[i
] : '.';
1352 static void lldp_capabilities_legend(uint16_t x
) {
1353 unsigned w
, i
, cols
= columns();
1354 static const char* const table
[] = {
1358 "w - WLAN Access Point",
1361 "d - DOCSIS cable device",
1363 "c - Customer VLAN",
1365 "m - Two-port MAC Relay (TPMR)",
1371 printf("\nCapability Flags:\n");
1372 for (w
= 0, i
= 0; i
< ELEMENTSOF(table
); i
++)
1373 if (x
& (1U << i
) || arg_all
) {
1376 newline
= w
+ strlen(table
[i
]) + (w
== 0 ? 0 : 2) > cols
;
1379 w
+= printf("%s%s%s", newline
? "\n" : "", w
== 0 ? "" : "; ", table
[i
]);
1384 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
1385 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1386 _cleanup_free_ LinkInfo
*links
= NULL
;
1387 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1392 r
= sd_netlink_open(&rtnl
);
1394 return log_error_errno(r
, "Failed to connect to netlink: %m");
1396 c
= acquire_link_info(NULL
, rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
1400 (void) pager_open(arg_pager_flags
);
1402 table
= table_new("LINK",
1407 "PORT DESCRIPTION");
1411 table_set_header(table
, arg_legend
);
1413 assert_se(cell
= table_get_cell(table
, 0, 0));
1414 table_set_minimum_width(table
, cell
, 16);
1416 assert_se(cell
= table_get_cell(table
, 0, 1));
1417 table_set_minimum_width(table
, cell
, 17);
1419 assert_se(cell
= table_get_cell(table
, 0, 2));
1420 table_set_minimum_width(table
, cell
, 16);
1422 assert_se(cell
= table_get_cell(table
, 0, 3));
1423 table_set_minimum_width(table
, cell
, 11);
1425 assert_se(cell
= table_get_cell(table
, 0, 4));
1426 table_set_minimum_width(table
, cell
, 17);
1428 assert_se(cell
= table_get_cell(table
, 0, 5));
1429 table_set_minimum_width(table
, cell
, 16);
1431 for (i
= 0; i
< c
; i
++) {
1432 _cleanup_fclose_
FILE *f
= NULL
;
1434 r
= open_lldp_neighbors(links
[i
].ifindex
, &f
);
1438 log_warning_errno(r
, "Failed to open LLDP data for %i, ignoring: %m", links
[i
].ifindex
);
1443 _cleanup_free_
char *cid
= NULL
, *pid
= NULL
, *sname
= NULL
, *pdesc
= NULL
, *capabilities
= NULL
;
1444 const char *chassis_id
= NULL
, *port_id
= NULL
, *system_name
= NULL
, *port_description
= NULL
;
1445 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
1448 r
= next_lldp_neighbor(f
, &n
);
1450 log_warning_errno(r
, "Failed to read neighbor data: %m");
1456 (void) sd_lldp_neighbor_get_chassis_id_as_string(n
, &chassis_id
);
1457 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
1458 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
1459 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
1462 cid
= ellipsize(chassis_id
, 17, 100);
1468 pid
= ellipsize(port_id
, 17, 100);
1474 sname
= ellipsize(system_name
, 16, 100);
1476 system_name
= sname
;
1479 if (port_description
) {
1480 pdesc
= ellipsize(port_description
, 16, 100);
1482 port_description
= pdesc
;
1485 if (sd_lldp_neighbor_get_enabled_capabilities(n
, &cc
) >= 0) {
1486 capabilities
= lldp_capabilities_to_string(cc
);
1490 r
= table_add_many(table
,
1491 TABLE_STRING
, links
[i
].name
,
1492 TABLE_STRING
, strna(chassis_id
),
1493 TABLE_STRING
, strna(system_name
),
1494 TABLE_STRING
, strna(capabilities
),
1495 TABLE_STRING
, strna(port_id
),
1496 TABLE_STRING
, strna(port_description
));
1504 r
= table_print(table
, NULL
);
1509 lldp_capabilities_legend(all
);
1510 printf("\n%i neighbors listed.\n", m
);
1516 static int link_delete_send_message(sd_netlink
*rtnl
, int index
) {
1517 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
1522 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_DELLINK
, index
);
1524 return rtnl_log_create_error(r
);
1526 r
= sd_netlink_call(rtnl
, req
, 0, NULL
);
1533 static int link_delete(int argc
, char *argv
[], void *userdata
) {
1534 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1535 _cleanup_set_free_ Set
*indexes
= NULL
;
1540 r
= sd_netlink_open(&rtnl
);
1542 return log_error_errno(r
, "Failed to connect to netlink: %m");
1544 indexes
= set_new(NULL
);
1548 for (i
= 1; i
< argc
; i
++) {
1549 r
= parse_ifindex_or_ifname(argv
[i
], &index
);
1551 return log_error_errno(r
, "Failed to resolve interface %s", argv
[i
]);
1553 r
= set_put(indexes
, INT_TO_PTR(index
));
1558 SET_FOREACH(p
, indexes
, j
) {
1559 r
= link_delete_send_message(rtnl
, PTR_TO_INT(p
));
1561 char ifname
[IF_NAMESIZE
+ 1];
1563 if (format_ifname(index
, ifname
))
1564 return log_error_errno(r
, "Failed to delete interface %s: %m", ifname
);
1566 return log_error_errno(r
, "Failed to delete interface %d: %m", index
);
1573 static int help(void) {
1574 _cleanup_free_
char *link
= NULL
;
1577 r
= terminal_urlify_man("networkctl", "1", &link
);
1581 printf("%s [OPTIONS...]\n\n"
1582 "Query and control the networking subsystem.\n\n"
1583 " -h --help Show this help\n"
1584 " --version Show package version\n"
1585 " --no-pager Do not pipe output into a pager\n"
1586 " --no-legend Do not show the headers and footers\n"
1587 " -a --all Show status for all links\n"
1588 " -s --stats Show detailed link statics\n"
1590 " list [PATTERN...] List links\n"
1591 " status [PATTERN...] Show link status\n"
1592 " lldp [PATTERN...] Show LLDP neighbors\n"
1593 " label Show current address label entries in the kernel\n"
1594 " delete DEVICES Delete virtual netdevs\n"
1595 "\nSee the %s for details.\n"
1596 , program_invocation_short_name
1603 static int parse_argv(int argc
, char *argv
[]) {
1606 ARG_VERSION
= 0x100,
1611 static const struct option options
[] = {
1612 { "help", no_argument
, NULL
, 'h' },
1613 { "version", no_argument
, NULL
, ARG_VERSION
},
1614 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1615 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1616 { "all", no_argument
, NULL
, 'a' },
1617 { "stats", no_argument
, NULL
, 's' },
1626 while ((c
= getopt_long(argc
, argv
, "has", options
, NULL
)) >= 0) {
1637 arg_pager_flags
|= PAGER_DISABLE
;
1656 assert_not_reached("Unhandled option");
1663 static int networkctl_main(int argc
, char *argv
[]) {
1664 static const Verb verbs
[] = {
1665 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, list_links
},
1666 { "status", VERB_ANY
, VERB_ANY
, 0, link_status
},
1667 { "lldp", VERB_ANY
, VERB_ANY
, 0, link_lldp_status
},
1668 { "label", VERB_ANY
, VERB_ANY
, 0, list_address_labels
},
1669 { "delete", 2, VERB_ANY
, 0, link_delete
},
1673 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1676 static void warn_networkd_missing(void) {
1678 if (access("/run/systemd/netif/state", F_OK
) >= 0)
1681 fprintf(stderr
, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1684 static int run(int argc
, char* argv
[]) {
1687 log_show_color(true);
1688 log_parse_environment();
1691 r
= parse_argv(argc
, argv
);
1695 warn_networkd_missing();
1697 return networkctl_main(argc
, argv
);
1700 DEFINE_MAIN_FUNCTION(run
);