1 /* SPDX-License-Identifier: LGPL-2.1+ */
4 #include <linux/if_addrlabel.h>
11 #include "sd-device.h"
14 #include "sd-netlink.h"
15 #include "sd-network.h"
17 #include "alloc-util.h"
18 #include "arphrd-list.h"
19 #include "device-util.h"
20 #include "ether-addr-util.h"
22 #include "format-table.h"
23 #include "format-util.h"
24 #include "hwdb-util.h"
25 #include "local-addresses.h"
26 #include "locale-util.h"
28 #include "main-func.h"
29 #include "netlink-util.h"
31 #include "parse-util.h"
32 #include "pretty-print.h"
34 #include "socket-util.h"
35 #include "sort-util.h"
36 #include "sparse-endian.h"
37 #include "stdio-util.h"
38 #include "string-table.h"
39 #include "string-util.h"
42 #include "terminal-util.h"
45 static PagerFlags arg_pager_flags
= 0;
46 static bool arg_legend
= true;
47 static bool arg_all
= false;
49 static char *link_get_type_string(unsigned short iftype
, sd_device
*d
) {
50 const char *t
, *devtype
;
54 sd_device_get_devtype(d
, &devtype
) >= 0 &&
56 return strdup(devtype
);
58 t
= arphrd_to_name(iftype
);
70 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
74 if (STRPTR_IN_SET(state
, "routable", "enslaved")) {
75 *on
= ansi_highlight_green();
77 } else if (streq_ptr(state
, "degraded")) {
78 *on
= ansi_highlight_yellow();
84 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
88 if (streq_ptr(state
, "configured")) {
89 *on
= ansi_highlight_green();
91 } else if (streq_ptr(state
, "configuring")) {
92 *on
= ansi_highlight_yellow();
94 } else if (STRPTR_IN_SET(state
, "failed", "linger")) {
95 *on
= ansi_highlight_red();
101 typedef struct LinkInfo
{
102 char name
[IFNAMSIZ
+1];
104 unsigned short iftype
;
105 struct ether_addr mac_address
;
112 bool has_mac_address
:1;
114 bool has_tx_queues
:1;
115 bool has_rx_queues
:1;
118 static int link_info_compare(const LinkInfo
*a
, const LinkInfo
*b
) {
119 return CMP(a
->ifindex
, b
->ifindex
);
122 static int decode_link(sd_netlink_message
*m
, LinkInfo
*info
, char **patterns
) {
130 r
= sd_netlink_message_get_type(m
, &type
);
134 if (type
!= RTM_NEWLINK
)
137 r
= sd_rtnl_message_link_get_ifindex(m
, &ifindex
);
141 r
= sd_netlink_message_read_string(m
, IFLA_IFNAME
, &name
);
146 char str
[DECIMAL_STR_MAX(int)];
148 xsprintf(str
, "%i", ifindex
);
150 if (!strv_fnmatch(patterns
, str
, 0) && !strv_fnmatch(patterns
, name
, 0))
154 r
= sd_rtnl_message_link_get_type(m
, &info
->iftype
);
158 strscpy(info
->name
, sizeof info
->name
, name
);
159 info
->ifindex
= ifindex
;
161 info
->has_mac_address
=
162 sd_netlink_message_read_ether_addr(m
, IFLA_ADDRESS
, &info
->mac_address
) >= 0 &&
163 memcmp(&info
->mac_address
, ÐER_ADDR_NULL
, sizeof(struct ether_addr
)) != 0;
166 sd_netlink_message_read_u32(m
, IFLA_MTU
, &info
->mtu
) >= 0 &&
170 (void) sd_netlink_message_read_u32(m
, IFLA_MIN_MTU
, &info
->min_mtu
);
171 (void) sd_netlink_message_read_u32(m
, IFLA_MAX_MTU
, &info
->max_mtu
);
174 info
->has_rx_queues
=
175 sd_netlink_message_read_u32(m
, IFLA_NUM_RX_QUEUES
, &info
->rx_queues
) >= 0 &&
178 info
->has_tx_queues
=
179 sd_netlink_message_read_u32(m
, IFLA_NUM_TX_QUEUES
, &info
->tx_queues
) >= 0 &&
185 static int acquire_link_info(sd_netlink
*rtnl
, char **patterns
, LinkInfo
**ret
) {
186 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
187 _cleanup_free_ LinkInfo
*links
= NULL
;
188 size_t allocated
= 0, c
= 0;
189 sd_netlink_message
*i
;
195 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
197 return rtnl_log_create_error(r
);
199 r
= sd_netlink_message_request_dump(req
, true);
201 return rtnl_log_create_error(r
);
203 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
205 return log_error_errno(r
, "Failed to enumerate links: %m");
207 for (i
= reply
; i
; i
= sd_netlink_message_next(i
)) {
208 if (!GREEDY_REALLOC(links
, allocated
, c
+1))
211 r
= decode_link(i
, links
+ c
, patterns
);
218 typesafe_qsort(links
, c
, link_info_compare
);
220 *ret
= TAKE_PTR(links
);
225 static int list_links(int argc
, char *argv
[], void *userdata
) {
226 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
227 _cleanup_free_ LinkInfo
*links
= NULL
;
228 _cleanup_(table_unrefp
) Table
*table
= NULL
;
232 r
= sd_netlink_open(&rtnl
);
234 return log_error_errno(r
, "Failed to connect to netlink: %m");
236 c
= acquire_link_info(rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
240 (void) pager_open(arg_pager_flags
);
242 table
= table_new("IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
246 table_set_header(table
, arg_legend
);
248 assert_se(cell
= table_get_cell(table
, 0, 0));
249 (void) table_set_minimum_width(table
, cell
, 3);
250 (void) table_set_weight(table
, cell
, 0);
251 (void) table_set_ellipsize_percent(table
, cell
, 0);
252 (void) table_set_align_percent(table
, cell
, 100);
254 assert_se(cell
= table_get_cell(table
, 0, 1));
255 (void) table_set_minimum_width(table
, cell
, 16);
257 assert_se(cell
= table_get_cell(table
, 0, 2));
258 (void) table_set_minimum_width(table
, cell
, 18);
260 assert_se(cell
= table_get_cell(table
, 0, 3));
261 (void) table_set_minimum_width(table
, cell
, 16);
263 assert_se(cell
= table_get_cell(table
, 0, 4));
264 (void) table_set_minimum_width(table
, cell
, 10);
266 for (i
= 0; i
< c
; i
++) {
267 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
268 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
269 const char *on_color_operational
, *off_color_operational
,
270 *on_color_setup
, *off_color_setup
;
271 char devid
[2 + DECIMAL_STR_MAX(int)];
272 _cleanup_free_
char *t
= NULL
;
274 (void) sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
275 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
277 r
= sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
278 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
279 setup_state
= strdup("unmanaged");
280 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
282 xsprintf(devid
, "n%i", links
[i
].ifindex
);
283 (void) sd_device_new_from_device_id(&d
, devid
);
285 t
= link_get_type_string(links
[i
].iftype
, d
);
287 r
= table_add_cell_full(table
, NULL
, TABLE_INT
, &links
[i
].ifindex
, SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
291 r
= table_add_many(table
,
292 TABLE_STRING
, links
[i
].name
,
293 TABLE_STRING
, strna(t
));
297 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(operational_state
));
301 (void) table_set_color(table
, cell
, on_color_operational
);
303 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(setup_state
));
307 (void) table_set_color(table
, cell
, on_color_setup
);
310 r
= table_print(table
, NULL
);
312 return log_error_errno(r
, "Failed to print table: %m");
315 printf("\n%i links listed.\n", c
);
320 /* IEEE Organizationally Unique Identifier vendor string */
321 static int ieee_oui(sd_hwdb
*hwdb
, const struct ether_addr
*mac
, char **ret
) {
322 const char *description
;
323 char modalias
[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc
;
334 /* skip commonly misused 00:00:00 (Xerox) prefix */
335 if (memcmp(mac
, "\0\0\0", 3) == 0)
338 xsprintf(modalias
, "OUI:" ETHER_ADDR_FORMAT_STR
,
339 ETHER_ADDR_FORMAT_VAL(*mac
));
341 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
345 desc
= strdup(description
);
354 static int get_gateway_description(
359 union in_addr_union
*gateway
,
360 char **gateway_description
) {
361 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
362 sd_netlink_message
*m
;
366 assert(ifindex
>= 0);
367 assert(IN_SET(family
, AF_INET
, AF_INET6
));
369 assert(gateway_description
);
371 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
375 r
= sd_netlink_message_request_dump(req
, true);
379 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
383 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
384 union in_addr_union gw
= IN_ADDR_NULL
;
385 struct ether_addr mac
= ETHER_ADDR_NULL
;
389 r
= sd_netlink_message_get_errno(m
);
391 log_error_errno(r
, "got error: %m");
395 r
= sd_netlink_message_get_type(m
, &type
);
397 log_error_errno(r
, "could not get type: %m");
401 if (type
!= RTM_NEWNEIGH
) {
402 log_error("type is not RTM_NEWNEIGH");
406 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
408 log_error_errno(r
, "could not get family: %m");
413 log_error("family is not correct");
417 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
419 log_error_errno(r
, "could not get ifindex: %m");
423 if (ifindex
> 0 && ifi
!= ifindex
)
428 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
434 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
443 if (!in_addr_equal(fam
, &gw
, gateway
))
446 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
450 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
460 static int dump_gateways(
465 _cleanup_free_
struct local_address
*local
= NULL
;
471 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
475 for (i
= 0; i
< n
; i
++) {
476 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
, *with_description
= NULL
;
478 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
482 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, i
== 0 ? "Gateway:" : "", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
486 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
490 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
492 log_debug_errno(r
, "Could not get description of gateway: %m");
495 with_description
= strjoin(gateway
, " (", description
, ")");
496 if (!with_description
)
500 /* Show interface name for the entry if we show
501 * entries for all interfaces */
503 char name
[IF_NAMESIZE
+1];
505 if (format_ifname(local
[i
].ifindex
, name
))
506 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", with_description
?: gateway
, name
);
508 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", with_description
?: gateway
, local
[i
].ifindex
);
510 r
= table_add_cell(table
, NULL
, TABLE_STRING
, with_description
?: gateway
);
518 static int dump_addresses(
523 _cleanup_free_
struct local_address
*local
= NULL
;
529 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
533 for (i
= 0; i
< n
; i
++) {
534 _cleanup_free_
char *pretty
= NULL
;
536 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
540 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, i
== 0 ? "Address:" : "", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
544 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
549 char name
[IF_NAMESIZE
+1];
551 if (format_ifname(local
[i
].ifindex
, name
))
552 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", pretty
, name
);
554 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", pretty
, local
[i
].ifindex
);
556 r
= table_add_cell(table
, NULL
, TABLE_STRING
, pretty
);
564 static int dump_address_labels(sd_netlink
*rtnl
) {
565 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
566 _cleanup_(table_unrefp
) Table
*table
= NULL
;
567 sd_netlink_message
*m
;
573 r
= sd_rtnl_message_new_addrlabel(rtnl
, &req
, RTM_GETADDRLABEL
, 0, AF_INET6
);
575 return log_error_errno(r
, "Could not allocate RTM_GETADDRLABEL message: %m");
577 r
= sd_netlink_message_request_dump(req
, true);
581 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
585 table
= table_new("Label", "Prefix/Prefixlen");
589 r
= table_set_sort(table
, 0, SIZE_MAX
);
593 assert_se(cell
= table_get_cell(table
, 0, 0));
594 (void) table_set_align_percent(table
, cell
, 100);
596 assert_se(cell
= table_get_cell(table
, 0, 1));
597 (void) table_set_align_percent(table
, cell
, 100);
599 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
600 _cleanup_free_
char *pretty
= NULL
;
601 union in_addr_union prefix
= IN_ADDR_NULL
;
605 r
= sd_netlink_message_get_errno(m
);
607 log_error_errno(r
, "got error: %m");
611 r
= sd_netlink_message_read_u32(m
, IFAL_LABEL
, &label
);
612 if (r
< 0 && r
!= -ENODATA
) {
613 log_error_errno(r
, "Could not read IFAL_LABEL, ignoring: %m");
617 r
= sd_netlink_message_read_in6_addr(m
, IFAL_ADDRESS
, &prefix
.in6
);
621 r
= in_addr_to_string(AF_INET6
, &prefix
, &pretty
);
625 r
= sd_rtnl_message_addrlabel_get_prefixlen(m
, &prefixlen
);
629 r
= table_add_cell_full(table
, NULL
, TABLE_UINT32
, &label
, SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
633 r
= table_add_cell_stringf(table
, &cell
, "%s/%u", pretty
, prefixlen
);
637 (void) table_set_align_percent(table
, cell
, 100);
640 return table_print(table
, NULL
);
643 static int list_address_labels(int argc
, char *argv
[], void *userdata
) {
644 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
647 r
= sd_netlink_open(&rtnl
);
649 return log_error_errno(r
, "Failed to connect to netlink: %m");
651 dump_address_labels(rtnl
);
656 static int open_lldp_neighbors(int ifindex
, FILE **ret
) {
657 _cleanup_free_
char *p
= NULL
;
660 if (asprintf(&p
, "/run/systemd/netif/lldp/%i", ifindex
) < 0)
671 static int next_lldp_neighbor(FILE *f
, sd_lldp_neighbor
**ret
) {
672 _cleanup_free_
void *raw
= NULL
;
680 l
= fread(&u
, 1, sizeof(u
), f
);
681 if (l
== 0 && feof(f
))
686 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
687 if (le64toh(u
) >= 4096)
690 raw
= new(uint8_t, le64toh(u
));
694 if (fread(raw
, 1, le64toh(u
), f
) != le64toh(u
))
697 r
= sd_lldp_neighbor_from_raw(ret
, raw
, le64toh(u
));
704 static int dump_lldp_neighbors(Table
*table
, const char *prefix
, int ifindex
) {
705 _cleanup_fclose_
FILE *f
= NULL
;
712 r
= open_lldp_neighbors(ifindex
, &f
);
719 const char *system_name
= NULL
, *port_id
= NULL
, *port_description
= NULL
;
720 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
721 _cleanup_free_
char *str
= NULL
;
723 r
= next_lldp_neighbor(f
, &n
);
729 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
733 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, c
== 0 ? prefix
: "", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
737 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
738 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
739 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
741 if (asprintf(&str
, "%s on port %s%s%s%s",
742 strna(system_name
), strna(port_id
),
743 isempty(port_description
) ? "" : " (",
745 isempty(port_description
) ? "" : ")") < 0)
748 r
= table_add_cell(table
, NULL
, TABLE_STRING
, str
);
758 static int dump_ifindexes(Table
*table
, const char *prefix
, const int *ifindexes
) {
764 if (!ifindexes
|| ifindexes
[0] <= 0)
767 for (c
= 0; ifindexes
[c
] > 0; c
++) {
768 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
772 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, c
== 0 ? prefix
: "", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
776 r
= table_add_cell(table
, NULL
, TABLE_IFINDEX
, ifindexes
+ c
);
784 static int dump_list(Table
*table
, const char *prefix
, char **l
) {
792 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
796 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, i
== l
? prefix
: "", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
800 r
= table_add_cell(table
, NULL
, TABLE_STRING
, *i
);
808 static int link_status_one(
811 const LinkInfo
*info
) {
813 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
814 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
815 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
816 char devid
[2 + DECIMAL_STR_MAX(int)];
817 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
818 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
819 const char *on_color_operational
, *off_color_operational
,
820 *on_color_setup
, *off_color_setup
;
821 _cleanup_free_
int *carrier_bound_to
= NULL
, *carrier_bound_by
= NULL
;
822 _cleanup_(table_unrefp
) Table
*table
= NULL
;
829 (void) sd_network_link_get_operational_state(info
->ifindex
, &operational_state
);
830 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
832 r
= sd_network_link_get_setup_state(info
->ifindex
, &setup_state
);
833 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
834 setup_state
= strdup("unmanaged");
835 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
837 (void) sd_network_link_get_dns(info
->ifindex
, &dns
);
838 (void) sd_network_link_get_search_domains(info
->ifindex
, &search_domains
);
839 (void) sd_network_link_get_route_domains(info
->ifindex
, &route_domains
);
840 (void) sd_network_link_get_ntp(info
->ifindex
, &ntp
);
842 xsprintf(devid
, "n%i", info
->ifindex
);
844 (void) sd_device_new_from_device_id(&d
, devid
);
847 (void) sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
848 (void) sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
849 (void) sd_device_get_property_value(d
, "ID_PATH", &path
);
851 if (sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
) < 0)
852 (void) sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
854 if (sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
) < 0)
855 (void) sd_device_get_property_value(d
, "ID_MODEL", &model
);
858 t
= link_get_type_string(info
->iftype
, d
);
860 (void) sd_network_link_get_network_file(info
->ifindex
, &network
);
862 (void) sd_network_link_get_carrier_bound_to(info
->ifindex
, &carrier_bound_to
);
863 (void) sd_network_link_get_carrier_bound_by(info
->ifindex
, &carrier_bound_by
);
865 table
= table_new("DOT", "KEY", "VALUE");
869 table_set_header(table
, false);
871 r
= table_add_cell(table
, &cell
, TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
));
874 (void) table_set_ellipsize_percent(table
, cell
, 0);
875 (void) table_set_color(table
, cell
, on_color_operational
);
876 r
= table_add_cell_stringf(table
, NULL
, "%i: %s", info
->ifindex
, info
->name
);
879 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
883 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
886 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Link File:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
889 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(link
));
893 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
896 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Network File:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
899 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(network
));
903 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
906 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Type:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
909 r
= table_add_cell(table
, NULL
, TABLE_STRING
, strna(t
));
913 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
916 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "State:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
919 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s (%s%s%s)",
920 on_color_operational
, strna(operational_state
), off_color_operational
,
921 on_color_setup
, strna(setup_state
), off_color_setup
);
926 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
929 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Path:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
932 r
= table_add_cell(table
, NULL
, TABLE_STRING
, path
);
937 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
940 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Driver:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
943 r
= table_add_cell(table
, NULL
, TABLE_STRING
, driver
);
948 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
951 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Vendor:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
954 r
= table_add_cell(table
, NULL
, TABLE_STRING
, vendor
);
959 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
962 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Model:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
965 r
= table_add_cell(table
, NULL
, TABLE_STRING
, model
);
970 if (info
->has_mac_address
) {
971 _cleanup_free_
char *description
= NULL
;
972 char ea
[ETHER_ADDR_TO_STRING_MAX
];
974 (void) ieee_oui(hwdb
, &info
->mac_address
, &description
);
976 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
979 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "HW Address:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
982 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s%s",
983 ether_addr_to_string(&info
->mac_address
, ea
),
984 description
? " (" : "",
986 description
? ")" : "");
992 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
995 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "MTU:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
998 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
" (Minimum: %" PRIu32
", Maximum: %" PRIu32
")",
999 info
->mtu
, info
->min_mtu
, info
->max_mtu
);
1004 if (info
->has_tx_queues
|| info
->has_rx_queues
) {
1005 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1008 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Queue Length (Tx/Rx):", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
1011 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"/%" PRIu32
, info
->tx_queues
, info
->rx_queues
);
1016 r
= dump_addresses(rtnl
, table
, info
->ifindex
);
1019 r
= dump_gateways(rtnl
, hwdb
, table
, info
->ifindex
);
1022 r
= dump_list(table
, "DNS:", dns
);
1025 r
= dump_list(table
, "Search Domains:", search_domains
);
1028 r
= dump_list(table
, "Route Domains:", route_domains
);
1031 r
= dump_list(table
, "NTP:", ntp
);
1034 r
= dump_ifindexes(table
, "Carrier Bound To:", carrier_bound_to
);
1037 r
= dump_ifindexes(table
, "Carrier Bound By:", carrier_bound_by
);
1041 (void) sd_network_link_get_timezone(info
->ifindex
, &tz
);
1043 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
1046 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "Time Zone:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
1049 r
= table_add_cell(table
, NULL
, TABLE_STRING
, tz
);
1054 r
= dump_lldp_neighbors(table
, "Connected To:", info
->ifindex
);
1058 return table_print(table
, NULL
);
1061 static int system_status(sd_netlink
*rtnl
, sd_hwdb
*hwdb
) {
1062 _cleanup_free_
char *operational_state
= NULL
;
1063 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
1064 const char *on_color_operational
, *off_color_operational
;
1065 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1071 (void) sd_network_get_operational_state(&operational_state
);
1072 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
1074 table
= table_new("DOT", "KEY", "VALUE");
1078 table_set_header(table
, false);
1080 r
= table_add_cell(table
, &cell
, TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
));
1083 (void) table_set_color(table
, cell
, on_color_operational
);
1084 (void) table_set_ellipsize_percent(table
, cell
, 0);
1086 r
= table_add_cell_full(table
, NULL
, TABLE_STRING
, "State:", SIZE_MAX
, SIZE_MAX
, 0, 100, 0);
1090 r
= table_add_cell(table
, &cell
, TABLE_STRING
, strna(operational_state
));
1093 (void) table_set_color(table
, cell
, on_color_operational
);
1095 r
= dump_addresses(rtnl
, table
, 0);
1098 r
= dump_gateways(rtnl
, hwdb
, table
, 0);
1102 (void) sd_network_get_dns(&dns
);
1103 r
= dump_list(table
, "DNS:", dns
);
1107 (void) sd_network_get_search_domains(&search_domains
);
1108 r
= dump_list(table
, "Search Domains:", search_domains
);
1112 (void) sd_network_get_route_domains(&route_domains
);
1113 r
= dump_list(table
, "Route Domains:", route_domains
);
1117 (void) sd_network_get_ntp(&ntp
);
1118 r
= dump_list(table
, "NTP:", ntp
);
1122 return table_print(table
, NULL
);
1125 static int link_status(int argc
, char *argv
[], void *userdata
) {
1126 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1127 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
1128 _cleanup_free_ LinkInfo
*links
= NULL
;
1131 (void) pager_open(arg_pager_flags
);
1133 r
= sd_netlink_open(&rtnl
);
1135 return log_error_errno(r
, "Failed to connect to netlink: %m");
1137 r
= sd_hwdb_new(&hwdb
);
1139 log_debug_errno(r
, "Failed to open hardware database: %m");
1142 c
= acquire_link_info(rtnl
, NULL
, &links
);
1144 return system_status(rtnl
, hwdb
);
1146 c
= acquire_link_info(rtnl
, argv
+ 1, &links
);
1150 for (i
= 0; i
< c
; i
++) {
1152 fputc('\n', stdout
);
1154 link_status_one(rtnl
, hwdb
, links
+ i
);
1160 static char *lldp_capabilities_to_string(uint16_t x
) {
1161 static const char characters
[] = {
1162 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1167 ret
= new(char, ELEMENTSOF(characters
) + 1);
1171 for (i
= 0; i
< ELEMENTSOF(characters
); i
++)
1172 ret
[i
] = (x
& (1U << i
)) ? characters
[i
] : '.';
1178 static void lldp_capabilities_legend(uint16_t x
) {
1179 unsigned w
, i
, cols
= columns();
1180 static const char* const table
[] = {
1184 "w - WLAN Access Point",
1187 "d - DOCSIS cable device",
1189 "c - Customer VLAN",
1191 "m - Two-port MAC Relay (TPMR)",
1197 printf("\nCapability Flags:\n");
1198 for (w
= 0, i
= 0; i
< ELEMENTSOF(table
); i
++)
1199 if (x
& (1U << i
) || arg_all
) {
1202 newline
= w
+ strlen(table
[i
]) + (w
== 0 ? 0 : 2) > cols
;
1205 w
+= printf("%s%s%s", newline
? "\n" : "", w
== 0 ? "" : "; ", table
[i
]);
1210 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
1211 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1212 _cleanup_free_ LinkInfo
*links
= NULL
;
1213 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1218 r
= sd_netlink_open(&rtnl
);
1220 return log_error_errno(r
, "Failed to connect to netlink: %m");
1222 c
= acquire_link_info(rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
1226 (void) pager_open(arg_pager_flags
);
1228 table
= table_new("LINK",
1233 "PORT DESCRIPTION");
1237 table_set_header(table
, arg_legend
);
1239 assert_se(cell
= table_get_cell(table
, 0, 0));
1240 table_set_minimum_width(table
, cell
, 16);
1242 assert_se(cell
= table_get_cell(table
, 0, 1));
1243 table_set_minimum_width(table
, cell
, 17);
1245 assert_se(cell
= table_get_cell(table
, 0, 2));
1246 table_set_minimum_width(table
, cell
, 16);
1248 assert_se(cell
= table_get_cell(table
, 0, 3));
1249 table_set_minimum_width(table
, cell
, 11);
1251 assert_se(cell
= table_get_cell(table
, 0, 4));
1252 table_set_minimum_width(table
, cell
, 17);
1254 assert_se(cell
= table_get_cell(table
, 0, 5));
1255 table_set_minimum_width(table
, cell
, 16);
1257 for (i
= 0; i
< c
; i
++) {
1258 _cleanup_fclose_
FILE *f
= NULL
;
1260 r
= open_lldp_neighbors(links
[i
].ifindex
, &f
);
1264 log_warning_errno(r
, "Failed to open LLDP data for %i, ignoring: %m", links
[i
].ifindex
);
1269 _cleanup_free_
char *cid
= NULL
, *pid
= NULL
, *sname
= NULL
, *pdesc
= NULL
;
1270 const char *chassis_id
= NULL
, *port_id
= NULL
, *system_name
= NULL
, *port_description
= NULL
, *capabilities
= NULL
;
1271 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
1274 r
= next_lldp_neighbor(f
, &n
);
1276 log_warning_errno(r
, "Failed to read neighbor data: %m");
1282 (void) sd_lldp_neighbor_get_chassis_id_as_string(n
, &chassis_id
);
1283 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
1284 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
1285 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
1288 cid
= ellipsize(chassis_id
, 17, 100);
1294 pid
= ellipsize(port_id
, 17, 100);
1300 sname
= ellipsize(system_name
, 16, 100);
1302 system_name
= sname
;
1305 if (port_description
) {
1306 pdesc
= ellipsize(port_description
, 16, 100);
1308 port_description
= pdesc
;
1311 if (sd_lldp_neighbor_get_enabled_capabilities(n
, &cc
) >= 0) {
1312 capabilities
= lldp_capabilities_to_string(cc
);
1316 r
= table_add_many(table
,
1317 TABLE_STRING
, links
[i
].name
,
1318 TABLE_STRING
, strna(chassis_id
),
1319 TABLE_STRING
, strna(system_name
),
1320 TABLE_STRING
, strna(capabilities
),
1321 TABLE_STRING
, strna(port_id
),
1322 TABLE_STRING
, strna(port_description
));
1330 r
= table_print(table
, NULL
);
1335 lldp_capabilities_legend(all
);
1336 printf("\n%i neighbors listed.\n", m
);
1342 static int link_delete_send_message(sd_netlink
*rtnl
, int index
) {
1343 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
1348 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_DELLINK
, index
);
1350 return rtnl_log_create_error(r
);
1352 r
= sd_netlink_call(rtnl
, req
, 0, NULL
);
1359 static int link_delete(int argc
, char *argv
[], void *userdata
) {
1360 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1361 _cleanup_set_free_ Set
*indexes
= NULL
;
1365 r
= sd_netlink_open(&rtnl
);
1367 return log_error_errno(r
, "Failed to connect to netlink: %m");
1369 indexes
= set_new(NULL
);
1373 for (i
= 1; i
< argc
; i
++) {
1374 r
= parse_ifindex_or_ifname(argv
[i
], &index
);
1376 return log_error_errno(r
, "Failed to resolve interface %s", argv
[i
]);
1378 r
= set_put(indexes
, INT_TO_PTR(index
));
1383 SET_FOREACH(index
, indexes
, j
) {
1384 r
= link_delete_send_message(rtnl
, index
);
1386 char ifname
[IF_NAMESIZE
+ 1];
1388 if (format_ifname(index
, ifname
))
1389 return log_error_errno(r
, "Failed to delete interface %s: %m", ifname
);
1391 return log_error_errno(r
, "Failed to delete interface %d: %m", index
);
1398 static int help(void) {
1399 _cleanup_free_
char *link
= NULL
;
1402 r
= terminal_urlify_man("networkctl", "1", &link
);
1406 printf("%s [OPTIONS...]\n\n"
1407 "Query and control the networking subsystem.\n\n"
1408 " -h --help Show this help\n"
1409 " --version Show package version\n"
1410 " --no-pager Do not pipe output into a pager\n"
1411 " --no-legend Do not show the headers and footers\n"
1412 " -a --all Show status for all links\n\n"
1414 " list [PATTERN...] List links\n"
1415 " status [PATTERN...] Show link status\n"
1416 " lldp [PATTERN...] Show LLDP neighbors\n"
1417 " label Show current address label entries in the kernel\n"
1418 " delete DEVICES Delete virtual netdevs\n"
1419 "\nSee the %s for details.\n"
1420 , program_invocation_short_name
1427 static int parse_argv(int argc
, char *argv
[]) {
1430 ARG_VERSION
= 0x100,
1435 static const struct option options
[] = {
1436 { "help", no_argument
, NULL
, 'h' },
1437 { "version", no_argument
, NULL
, ARG_VERSION
},
1438 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1439 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1440 { "all", no_argument
, NULL
, 'a' },
1449 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0) {
1460 arg_pager_flags
|= PAGER_DISABLE
;
1475 assert_not_reached("Unhandled option");
1482 static int networkctl_main(int argc
, char *argv
[]) {
1483 static const Verb verbs
[] = {
1484 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, list_links
},
1485 { "status", VERB_ANY
, VERB_ANY
, 0, link_status
},
1486 { "lldp", VERB_ANY
, VERB_ANY
, 0, link_lldp_status
},
1487 { "label", VERB_ANY
, VERB_ANY
, 0, list_address_labels
},
1488 { "delete", 2, VERB_ANY
, 0, link_delete
},
1492 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1495 static void warn_networkd_missing(void) {
1497 if (access("/run/systemd/netif/state", F_OK
) >= 0)
1500 fprintf(stderr
, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1503 static int run(int argc
, char* argv
[]) {
1506 log_show_color(true);
1507 log_parse_environment();
1510 r
= parse_argv(argc
, argv
);
1514 warn_networkd_missing();
1516 return networkctl_main(argc
, argv
);
1519 DEFINE_MAIN_FUNCTION(run
);