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 /* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
51 #define NETDEV_KIND_MAX 64
53 static PagerFlags arg_pager_flags
= 0;
54 static bool arg_legend
= true;
55 static bool arg_all
= false;
56 static bool arg_stats
= false;
58 static char *link_get_type_string(unsigned short iftype
, sd_device
*d
) {
59 const char *t
, *devtype
;
63 sd_device_get_devtype(d
, &devtype
) >= 0 &&
65 return strdup(devtype
);
67 t
= arphrd_to_name(iftype
);
79 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
83 if (STRPTR_IN_SET(state
, "routable", "enslaved")) {
84 *on
= ansi_highlight_green();
86 } else if (streq_ptr(state
, "degraded")) {
87 *on
= ansi_highlight_yellow();
93 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
97 if (streq_ptr(state
, "configured")) {
98 *on
= ansi_highlight_green();
100 } else if (streq_ptr(state
, "configuring")) {
101 *on
= ansi_highlight_yellow();
102 *off
= ansi_normal();
103 } else if (STRPTR_IN_SET(state
, "failed", "linger")) {
104 *on
= ansi_highlight_red();
105 *off
= ansi_normal();
110 typedef struct VxLanInfo
{
117 union in_addr_union local
;
118 union in_addr_union group
;
124 typedef struct LinkInfo
{
125 char name
[IFNAMSIZ
+1];
126 char netdev_kind
[NETDEV_KIND_MAX
];
128 unsigned short iftype
;
129 struct ether_addr mac_address
;
137 struct rtnl_link_stats64 stats64
;
138 struct rtnl_link_stats stats
;
145 uint32_t forward_delay
;
148 uint32_t ageing_time
;
151 uint8_t mcast_igmp_version
;
154 VxLanInfo vxlan_info
;
162 bool has_mac_address
:1;
163 bool has_tx_queues
:1;
164 bool has_rx_queues
:1;
168 bool has_ethtool_link_info
:1;
171 static int link_info_compare(const LinkInfo
*a
, const LinkInfo
*b
) {
172 return CMP(a
->ifindex
, b
->ifindex
);
175 static int decode_netdev(sd_netlink_message
*m
, LinkInfo
*info
) {
176 const char *received_kind
;
182 r
= sd_netlink_message_enter_container(m
, IFLA_LINKINFO
);
186 r
= sd_netlink_message_read_string(m
, IFLA_INFO_KIND
, &received_kind
);
190 r
= sd_netlink_message_enter_container(m
, IFLA_INFO_DATA
);
194 if (streq(received_kind
, "bridge")) {
195 (void) sd_netlink_message_read_u32(m
, IFLA_BR_FORWARD_DELAY
, &info
->forward_delay
);
196 (void) sd_netlink_message_read_u32(m
, IFLA_BR_HELLO_TIME
, &info
->hello_time
);
197 (void) sd_netlink_message_read_u32(m
, IFLA_BR_MAX_AGE
, &info
->max_age
);
198 (void) sd_netlink_message_read_u32(m
, IFLA_BR_AGEING_TIME
, &info
->ageing_time
);
199 (void) sd_netlink_message_read_u32(m
, IFLA_BR_STP_STATE
, &info
->stp_state
);
200 (void) sd_netlink_message_read_u16(m
, IFLA_BR_PRIORITY
, &info
->priority
);
201 (void) sd_netlink_message_read_u8(m
, IFLA_BR_MCAST_IGMP_VERSION
, &info
->mcast_igmp_version
);
203 } else if (streq(received_kind
, "vxlan")) {
204 (void) sd_netlink_message_read_u32(m
, IFLA_VXLAN_ID
, &info
->vxlan_info
.vni
);
206 r
= sd_netlink_message_read_in_addr(m
, IFLA_VXLAN_GROUP
, &info
->vxlan_info
.group
.in
);
208 info
->vxlan_info
.group_family
= AF_INET
;
210 r
= sd_netlink_message_read_in6_addr(m
, IFLA_VXLAN_GROUP6
, &info
->vxlan_info
.group
.in6
);
212 info
->vxlan_info
.group_family
= AF_INET6
;
215 r
= sd_netlink_message_read_in_addr(m
, IFLA_VXLAN_LOCAL
, &info
->vxlan_info
.local
.in
);
217 info
->vxlan_info
.local_family
= AF_INET
;
219 r
= sd_netlink_message_read_in6_addr(m
, IFLA_VXLAN_LOCAL6
, &info
->vxlan_info
.local
.in6
);
221 info
->vxlan_info
.local_family
= AF_INET6
;
224 (void) sd_netlink_message_read_u32(m
, IFLA_VXLAN_LINK
, &info
->vxlan_info
.link
);
225 (void) sd_netlink_message_read_u16(m
, IFLA_VXLAN_PORT
, &info
->vxlan_info
.dest_port
);
228 strncpy(info
->netdev_kind
, received_kind
, IFNAMSIZ
);
230 (void) sd_netlink_message_exit_container(m
);
231 (void) sd_netlink_message_exit_container(m
);
236 static int decode_link(sd_netlink_message
*m
, LinkInfo
*info
, char **patterns
) {
244 r
= sd_netlink_message_get_type(m
, &type
);
248 if (type
!= RTM_NEWLINK
)
251 r
= sd_rtnl_message_link_get_ifindex(m
, &ifindex
);
255 r
= sd_netlink_message_read_string(m
, IFLA_IFNAME
, &name
);
260 char str
[DECIMAL_STR_MAX(int)];
262 xsprintf(str
, "%i", ifindex
);
264 if (!strv_fnmatch(patterns
, str
, 0) && !strv_fnmatch(patterns
, name
, 0))
268 r
= sd_rtnl_message_link_get_type(m
, &info
->iftype
);
272 strscpy(info
->name
, sizeof info
->name
, name
);
273 info
->ifindex
= ifindex
;
275 info
->has_mac_address
=
276 sd_netlink_message_read_ether_addr(m
, IFLA_ADDRESS
, &info
->mac_address
) >= 0 &&
277 memcmp(&info
->mac_address
, ÐER_ADDR_NULL
, sizeof(struct ether_addr
)) != 0;
279 (void) sd_netlink_message_read_u32(m
, IFLA_MTU
, &info
->mtu
);
280 (void) sd_netlink_message_read_u32(m
, IFLA_MIN_MTU
, &info
->min_mtu
);
281 (void) sd_netlink_message_read_u32(m
, IFLA_MAX_MTU
, &info
->max_mtu
);
283 info
->has_rx_queues
=
284 sd_netlink_message_read_u32(m
, IFLA_NUM_RX_QUEUES
, &info
->rx_queues
) >= 0 &&
287 info
->has_tx_queues
=
288 sd_netlink_message_read_u32(m
, IFLA_NUM_TX_QUEUES
, &info
->tx_queues
) >= 0 &&
291 if (sd_netlink_message_read(m
, IFLA_STATS64
, sizeof info
->stats64
, &info
->stats64
) >= 0)
292 info
->has_stats64
= true;
293 else if (sd_netlink_message_read(m
, IFLA_STATS
, sizeof info
->stats
, &info
->stats
) >= 0)
294 info
->has_stats
= true;
297 (void) decode_netdev(m
, info
);
302 static int acquire_link_bitrates(sd_bus
*bus
, LinkInfo
*link
) {
303 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
304 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
305 _cleanup_free_
char *path
= NULL
, *ifindex_str
= NULL
;
308 if (asprintf(&ifindex_str
, "%i", link
->ifindex
) < 0)
311 r
= sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str
, &path
);
315 r
= sd_bus_call_method(
317 "org.freedesktop.network1",
319 "org.freedesktop.DBus.Properties",
324 "org.freedesktop.network1.Link",
327 bool quiet
= sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_PROPERTY
) ||
328 sd_bus_error_has_name(&error
, BUS_ERROR_SPEED_METER_INACTIVE
);
330 return log_full_errno(quiet
? LOG_DEBUG
: LOG_WARNING
,
331 r
, "Failed to query link bit rates: %s", bus_error_message(&error
, r
));
334 r
= sd_bus_message_enter_container(reply
, 'v', "(tt)");
336 return bus_log_parse_error(r
);
338 r
= sd_bus_message_read(reply
, "(tt)", &link
->tx_bitrate
, &link
->rx_bitrate
);
340 return bus_log_parse_error(r
);
342 r
= sd_bus_message_exit_container(reply
);
344 return bus_log_parse_error(r
);
346 link
->has_bitrates
= true;
351 static int acquire_link_info(sd_bus
*bus
, sd_netlink
*rtnl
, char **patterns
, LinkInfo
**ret
) {
352 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
353 _cleanup_free_ LinkInfo
*links
= NULL
;
354 _cleanup_close_
int fd
= -1;
355 size_t allocated
= 0, c
= 0, j
;
356 sd_netlink_message
*i
;
362 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
364 return rtnl_log_create_error(r
);
366 r
= sd_netlink_message_request_dump(req
, true);
368 return rtnl_log_create_error(r
);
370 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
372 return log_error_errno(r
, "Failed to enumerate links: %m");
374 for (i
= reply
; i
; i
= sd_netlink_message_next(i
)) {
375 if (!GREEDY_REALLOC0(links
, allocated
, c
+1))
378 r
= decode_link(i
, links
+ c
, patterns
);
384 r
= ethtool_get_link_info(&fd
, links
[c
].name
,
385 &links
[c
].autonegotiation
, &links
[c
].speed
,
386 &links
[c
].duplex
, &links
[c
].port
);
388 links
[c
].has_ethtool_link_info
= true;
393 typesafe_qsort(links
, c
, link_info_compare
);
396 for (j
= 0; j
< c
; j
++)
397 (void) acquire_link_bitrates(bus
, links
+ j
);
399 *ret
= TAKE_PTR(links
);
404 static int list_links(int argc
, char *argv
[], void *userdata
) {
405 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
406 _cleanup_free_ LinkInfo
*links
= NULL
;
407 _cleanup_(table_unrefp
) Table
*table
= NULL
;
411 r
= sd_netlink_open(&rtnl
);
413 return log_error_errno(r
, "Failed to connect to netlink: %m");
415 c
= acquire_link_info(NULL
, rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
419 (void) pager_open(arg_pager_flags
);
421 table
= table_new("idx", "link", "type", "operational", "setup");
425 table_set_header(table
, arg_legend
);
427 assert_se(cell
= table_get_cell(table
, 0, 0));
428 (void) table_set_minimum_width(table
, cell
, 3);
429 (void) table_set_weight(table
, cell
, 0);
430 (void) table_set_ellipsize_percent(table
, cell
, 100);
431 (void) table_set_align_percent(table
, cell
, 100);
433 assert_se(cell
= table_get_cell(table
, 0, 1));
434 (void) table_set_ellipsize_percent(table
, cell
, 100);
436 for (i
= 0; i
< c
; i
++) {
437 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
438 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
439 const char *on_color_operational
, *off_color_operational
,
440 *on_color_setup
, *off_color_setup
;
441 char devid
[2 + DECIMAL_STR_MAX(int)];
442 _cleanup_free_
char *t
= NULL
;
444 (void) sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
445 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
447 r
= sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
448 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
449 setup_state
= strdup("unmanaged");
450 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
452 xsprintf(devid
, "n%i", links
[i
].ifindex
);
453 (void) sd_device_new_from_device_id(&d
, devid
);
455 t
= link_get_type_string(links
[i
].iftype
, d
);
457 r
= table_add_many(table
,
458 TABLE_INT
, links
[i
].ifindex
,
459 TABLE_STRING
, links
[i
].name
,
460 TABLE_STRING
, strna(t
),
461 TABLE_STRING
, strna(operational_state
),
462 TABLE_SET_COLOR
, on_color_operational
,
463 TABLE_STRING
, strna(setup_state
),
464 TABLE_SET_COLOR
, on_color_setup
);
469 r
= table_print(table
, NULL
);
471 return log_error_errno(r
, "Failed to print table: %m");
474 printf("\n%i links listed.\n", c
);
479 /* IEEE Organizationally Unique Identifier vendor string */
480 static int ieee_oui(sd_hwdb
*hwdb
, const struct ether_addr
*mac
, char **ret
) {
481 const char *description
;
482 char modalias
[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc
;
493 /* skip commonly misused 00:00:00 (Xerox) prefix */
494 if (memcmp(mac
, "\0\0\0", 3) == 0)
497 xsprintf(modalias
, "OUI:" ETHER_ADDR_FORMAT_STR
,
498 ETHER_ADDR_FORMAT_VAL(*mac
));
500 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
504 desc
= strdup(description
);
513 static int get_gateway_description(
518 union in_addr_union
*gateway
,
519 char **gateway_description
) {
520 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
521 sd_netlink_message
*m
;
525 assert(ifindex
>= 0);
526 assert(IN_SET(family
, AF_INET
, AF_INET6
));
528 assert(gateway_description
);
530 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
534 r
= sd_netlink_message_request_dump(req
, true);
538 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
542 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
543 union in_addr_union gw
= IN_ADDR_NULL
;
544 struct ether_addr mac
= ETHER_ADDR_NULL
;
548 r
= sd_netlink_message_get_errno(m
);
550 log_error_errno(r
, "got error: %m");
554 r
= sd_netlink_message_get_type(m
, &type
);
556 log_error_errno(r
, "could not get type: %m");
560 if (type
!= RTM_NEWNEIGH
) {
561 log_error("type is not RTM_NEWNEIGH");
565 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
567 log_error_errno(r
, "could not get family: %m");
572 log_error("family is not correct");
576 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
578 log_error_errno(r
, "could not get ifindex: %m");
582 if (ifindex
> 0 && ifi
!= ifindex
)
587 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
593 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
602 if (!in_addr_equal(fam
, &gw
, gateway
))
605 r
= sd_netlink_message_read(m
, NDA_LLADDR
, sizeof(mac
), &mac
);
609 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
619 static int dump_gateways(
624 _cleanup_free_
struct local_address
*local
= NULL
;
630 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
634 for (i
= 0; i
< n
; i
++) {
635 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
, *with_description
= NULL
;
637 r
= table_add_many(table
,
639 TABLE_STRING
, i
== 0 ? "Gateway:" : "");
643 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
647 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
649 log_debug_errno(r
, "Could not get description of gateway: %m");
652 with_description
= strjoin(gateway
, " (", description
, ")");
653 if (!with_description
)
657 /* Show interface name for the entry if we show
658 * entries for all interfaces */
660 char name
[IF_NAMESIZE
+1];
662 if (format_ifname(local
[i
].ifindex
, name
))
663 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", with_description
?: gateway
, name
);
665 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", with_description
?: gateway
, local
[i
].ifindex
);
667 r
= table_add_cell(table
, NULL
, TABLE_STRING
, with_description
?: gateway
);
675 static int dump_addresses(
680 _cleanup_free_
struct local_address
*local
= NULL
;
686 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
690 for (i
= 0; i
< n
; i
++) {
691 _cleanup_free_
char *pretty
= NULL
;
693 r
= table_add_many(table
,
695 TABLE_STRING
, i
== 0 ? "Address:" : "");
699 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
704 char name
[IF_NAMESIZE
+1];
706 if (format_ifname(local
[i
].ifindex
, name
))
707 r
= table_add_cell_stringf(table
, NULL
, "%s on %s", pretty
, name
);
709 r
= table_add_cell_stringf(table
, NULL
, "%s on %%%i", pretty
, local
[i
].ifindex
);
711 r
= table_add_cell(table
, NULL
, TABLE_STRING
, pretty
);
719 static int dump_address_labels(sd_netlink
*rtnl
) {
720 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
721 _cleanup_(table_unrefp
) Table
*table
= NULL
;
722 sd_netlink_message
*m
;
728 r
= sd_rtnl_message_new_addrlabel(rtnl
, &req
, RTM_GETADDRLABEL
, 0, AF_INET6
);
730 return log_error_errno(r
, "Could not allocate RTM_GETADDRLABEL message: %m");
732 r
= sd_netlink_message_request_dump(req
, true);
736 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
740 table
= table_new("label", "prefix/prefixlen");
744 r
= table_set_sort(table
, 0, SIZE_MAX
);
748 assert_se(cell
= table_get_cell(table
, 0, 0));
749 (void) table_set_align_percent(table
, cell
, 100);
750 (void) table_set_ellipsize_percent(table
, cell
, 100);
752 assert_se(cell
= table_get_cell(table
, 0, 1));
753 (void) table_set_align_percent(table
, cell
, 100);
755 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
756 _cleanup_free_
char *pretty
= NULL
;
757 union in_addr_union prefix
= IN_ADDR_NULL
;
761 r
= sd_netlink_message_get_errno(m
);
763 log_error_errno(r
, "got error: %m");
767 r
= sd_netlink_message_read_u32(m
, IFAL_LABEL
, &label
);
768 if (r
< 0 && r
!= -ENODATA
) {
769 log_error_errno(r
, "Could not read IFAL_LABEL, ignoring: %m");
773 r
= sd_netlink_message_read_in6_addr(m
, IFAL_ADDRESS
, &prefix
.in6
);
777 r
= in_addr_to_string(AF_INET6
, &prefix
, &pretty
);
781 r
= sd_rtnl_message_addrlabel_get_prefixlen(m
, &prefixlen
);
785 r
= table_add_cell(table
, NULL
, TABLE_UINT32
, &label
);
789 r
= table_add_cell_stringf(table
, NULL
, "%s/%u", pretty
, prefixlen
);
794 return table_print(table
, NULL
);
797 static int list_address_labels(int argc
, char *argv
[], void *userdata
) {
798 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
801 r
= sd_netlink_open(&rtnl
);
803 return log_error_errno(r
, "Failed to connect to netlink: %m");
805 dump_address_labels(rtnl
);
810 static int open_lldp_neighbors(int ifindex
, FILE **ret
) {
811 _cleanup_free_
char *p
= NULL
;
814 if (asprintf(&p
, "/run/systemd/netif/lldp/%i", ifindex
) < 0)
825 static int next_lldp_neighbor(FILE *f
, sd_lldp_neighbor
**ret
) {
826 _cleanup_free_
void *raw
= NULL
;
834 l
= fread(&u
, 1, sizeof(u
), f
);
835 if (l
== 0 && feof(f
))
840 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
841 if (le64toh(u
) >= 4096)
844 raw
= new(uint8_t, le64toh(u
));
848 if (fread(raw
, 1, le64toh(u
), f
) != le64toh(u
))
851 r
= sd_lldp_neighbor_from_raw(ret
, raw
, le64toh(u
));
858 static int dump_lldp_neighbors(Table
*table
, const char *prefix
, int ifindex
) {
859 _cleanup_fclose_
FILE *f
= NULL
;
866 r
= open_lldp_neighbors(ifindex
, &f
);
873 const char *system_name
= NULL
, *port_id
= NULL
, *port_description
= NULL
;
874 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
876 r
= next_lldp_neighbor(f
, &n
);
882 r
= table_add_many(table
,
884 TABLE_STRING
, c
== 0 ? prefix
: "");
888 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
889 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
890 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
892 r
= table_add_cell_stringf(table
, NULL
,
893 "%s on port %s%s%s%s",
894 strna(system_name
), strna(port_id
),
895 isempty(port_description
) ? "" : " (",
897 isempty(port_description
) ? "" : ")");
907 static int dump_ifindexes(Table
*table
, const char *prefix
, const int *ifindexes
) {
913 if (!ifindexes
|| ifindexes
[0] <= 0)
916 for (c
= 0; ifindexes
[c
] > 0; c
++) {
917 r
= table_add_many(table
,
919 TABLE_STRING
, c
== 0 ? prefix
: "",
920 TABLE_IFINDEX
, ifindexes
[c
]);
928 static int dump_list(Table
*table
, const char *prefix
, char **l
) {
936 r
= table_add_many(table
,
938 TABLE_STRING
, i
== l
? prefix
: "",
947 #define DUMP_STATS_ONE(name, val_name) \
948 r = table_add_many(table, \
950 TABLE_STRING, name ":"); \
953 r = table_add_cell(table, NULL, \
954 info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
955 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
959 static int dump_statistics(Table
*table
, const LinkInfo
*info
) {
965 if (!info
->has_stats64
&& !info
->has_stats
)
968 DUMP_STATS_ONE("Rx Packets", rx_packets
);
969 DUMP_STATS_ONE("Tx Packets", tx_packets
);
970 DUMP_STATS_ONE("Rx Bytes", rx_bytes
);
971 DUMP_STATS_ONE("Tx Bytes", tx_bytes
);
972 DUMP_STATS_ONE("Rx Errors", rx_errors
);
973 DUMP_STATS_ONE("Tx Errors", tx_errors
);
974 DUMP_STATS_ONE("Rx Dropped", rx_dropped
);
975 DUMP_STATS_ONE("Tx Dropped", tx_dropped
);
976 DUMP_STATS_ONE("Multicast Packets", multicast
);
977 DUMP_STATS_ONE("Collisions", collisions
);
982 static int link_status_one(
985 const LinkInfo
*info
) {
987 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
988 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
989 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
990 char devid
[2 + DECIMAL_STR_MAX(int)];
991 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
992 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
993 const char *on_color_operational
, *off_color_operational
,
994 *on_color_setup
, *off_color_setup
;
995 _cleanup_free_
int *carrier_bound_to
= NULL
, *carrier_bound_by
= NULL
;
996 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1003 (void) sd_network_link_get_operational_state(info
->ifindex
, &operational_state
);
1004 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
1006 r
= sd_network_link_get_setup_state(info
->ifindex
, &setup_state
);
1007 if (r
== -ENODATA
) /* If there's no info available about this iface, it's unmanaged by networkd */
1008 setup_state
= strdup("unmanaged");
1009 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
1011 (void) sd_network_link_get_dns(info
->ifindex
, &dns
);
1012 (void) sd_network_link_get_search_domains(info
->ifindex
, &search_domains
);
1013 (void) sd_network_link_get_route_domains(info
->ifindex
, &route_domains
);
1014 (void) sd_network_link_get_ntp(info
->ifindex
, &ntp
);
1016 xsprintf(devid
, "n%i", info
->ifindex
);
1018 (void) sd_device_new_from_device_id(&d
, devid
);
1021 (void) sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
1022 (void) sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
1023 (void) sd_device_get_property_value(d
, "ID_PATH", &path
);
1025 if (sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
) < 0)
1026 (void) sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
1028 if (sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
) < 0)
1029 (void) sd_device_get_property_value(d
, "ID_MODEL", &model
);
1032 t
= link_get_type_string(info
->iftype
, d
);
1034 (void) sd_network_link_get_network_file(info
->ifindex
, &network
);
1036 (void) sd_network_link_get_carrier_bound_to(info
->ifindex
, &carrier_bound_to
);
1037 (void) sd_network_link_get_carrier_bound_by(info
->ifindex
, &carrier_bound_by
);
1039 table
= table_new("dot", "key", "value");
1043 assert_se(cell
= table_get_cell(table
, 0, 0));
1044 (void) table_set_ellipsize_percent(table
, cell
, 100);
1046 assert_se(cell
= table_get_cell(table
, 0, 1));
1047 (void) table_set_ellipsize_percent(table
, cell
, 100);
1049 table_set_header(table
, false);
1051 r
= table_add_many(table
,
1052 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
),
1053 TABLE_SET_COLOR
, on_color_operational
);
1056 r
= table_add_cell_stringf(table
, &cell
, "%i: %s", info
->ifindex
, info
->name
);
1059 (void) table_set_align_percent(table
, cell
, 0);
1061 r
= table_add_many(table
,
1064 TABLE_STRING
, "Link File:",
1065 TABLE_SET_ALIGN_PERCENT
, 100,
1066 TABLE_STRING
, strna(link
),
1068 TABLE_STRING
, "Network File:",
1069 TABLE_STRING
, strna(network
),
1071 TABLE_STRING
, "Type:",
1072 TABLE_STRING
, strna(t
),
1074 TABLE_STRING
, "State:");
1077 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s (%s%s%s)",
1078 on_color_operational
, strna(operational_state
), off_color_operational
,
1079 on_color_setup
, strna(setup_state
), off_color_setup
);
1084 r
= table_add_many(table
,
1086 TABLE_STRING
, "Path:",
1087 TABLE_STRING
, path
);
1092 r
= table_add_many(table
,
1094 TABLE_STRING
, "Driver:",
1095 TABLE_STRING
, driver
);
1100 r
= table_add_many(table
,
1102 TABLE_STRING
, "Vendor:",
1103 TABLE_STRING
, vendor
);
1108 r
= table_add_many(table
,
1110 TABLE_STRING
, "Model:",
1111 TABLE_STRING
, model
);
1116 if (info
->has_mac_address
) {
1117 _cleanup_free_
char *description
= NULL
;
1118 char ea
[ETHER_ADDR_TO_STRING_MAX
];
1120 (void) ieee_oui(hwdb
, &info
->mac_address
, &description
);
1122 r
= table_add_many(table
,
1124 TABLE_STRING
, "HW Address:");
1127 r
= table_add_cell_stringf(table
, NULL
, "%s%s%s%s",
1128 ether_addr_to_string(&info
->mac_address
, ea
),
1129 description
? " (" : "",
1130 strempty(description
),
1131 description
? ")" : "");
1136 if (info
->mtu
> 0) {
1137 char min_str
[DECIMAL_STR_MAX(uint32_t)], max_str
[DECIMAL_STR_MAX(uint32_t)];
1139 xsprintf(min_str
, "%" PRIu32
, info
->min_mtu
);
1140 xsprintf(max_str
, "%" PRIu32
, info
->max_mtu
);
1142 r
= table_add_many(table
,
1144 TABLE_STRING
, "MTU:");
1147 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"%s%s%s%s%s%s%s",
1149 info
->min_mtu
> 0 || info
->max_mtu
> 0 ? " (" : "",
1150 info
->min_mtu
> 0 ? "min: " : "",
1151 info
->min_mtu
> 0 ? min_str
: "",
1152 info
->min_mtu
> 0 && info
->max_mtu
> 0 ? ", " : "",
1153 info
->max_mtu
> 0 ? "max: " : "",
1154 info
->max_mtu
> 0 ? max_str
: "",
1155 info
->min_mtu
> 0 || info
->max_mtu
> 0 ? ")" : "");
1160 if (streq_ptr(info
->netdev_kind
, "bridge")) {
1161 r
= table_add_many(table
,
1163 TABLE_STRING
, "Forward Delay:",
1164 TABLE_TIMESPAN_MSEC
, jiffies_to_usec(info
->forward_delay
),
1166 TABLE_STRING
, "Hello Time:",
1167 TABLE_TIMESPAN_MSEC
, jiffies_to_usec(info
->hello_time
),
1169 TABLE_STRING
, "Max Age:",
1170 TABLE_TIMESPAN_MSEC
, jiffies_to_usec(info
->max_age
),
1172 TABLE_STRING
, "Ageing Time:",
1173 TABLE_TIMESPAN_MSEC
, jiffies_to_usec(info
->ageing_time
),
1175 TABLE_STRING
, "Priority:",
1176 TABLE_UINT16
, info
->priority
,
1178 TABLE_STRING
, "STP:",
1179 TABLE_BOOLEAN
, info
->stp_state
> 0,
1181 TABLE_STRING
, "Multicast IGMP Version:",
1182 TABLE_UINT8
, info
->mcast_igmp_version
);
1186 } else if (streq_ptr(info
->netdev_kind
, "vxlan")) {
1187 if (info
->vxlan_info
.vni
> 0) {
1188 r
= table_add_many(table
,
1190 TABLE_STRING
, "VNI:",
1191 TABLE_UINT32
, info
->vxlan_info
.vni
);
1196 if (IN_SET(info
->vxlan_info
.group_family
, AF_INET
, AF_INET6
)) {
1197 r
= table_add_many(table
,
1199 TABLE_STRING
, "Group:",
1200 info
->vxlan_info
.group_family
== AF_INET
? TABLE_IN_ADDR
: TABLE_IN6_ADDR
,
1201 &info
->vxlan_info
.group
);
1206 if (IN_SET(info
->vxlan_info
.local_family
, AF_INET
, AF_INET6
)) {
1207 r
= table_add_many(table
,
1209 TABLE_STRING
, "Local:",
1210 info
->vxlan_info
.local_family
== AF_INET
? TABLE_IN_ADDR
: TABLE_IN6_ADDR
,
1211 &info
->vxlan_info
.local
);
1216 if (info
->vxlan_info
.dest_port
> 0) {
1217 r
= table_add_many(table
,
1219 TABLE_STRING
, "Destination Port:",
1220 TABLE_UINT16
, be16toh(info
->vxlan_info
.dest_port
));
1225 if (info
->vxlan_info
.link
> 0) {
1226 r
= table_add_many(table
,
1228 TABLE_STRING
, "Underlying Device:",
1229 TABLE_IFINDEX
, info
->vxlan_info
.link
);
1235 if (info
->has_bitrates
) {
1236 char tx
[FORMAT_BYTES_MAX
], rx
[FORMAT_BYTES_MAX
];
1238 r
= table_add_many(table
,
1240 TABLE_STRING
, "Bit Rate (Tx/Rx):");
1243 r
= table_add_cell_stringf(table
, NULL
, "%sbps/%sbps",
1244 format_bytes_full(tx
, sizeof tx
, info
->tx_bitrate
, 0),
1245 format_bytes_full(rx
, sizeof rx
, info
->rx_bitrate
, 0));
1250 if (info
->has_tx_queues
|| info
->has_rx_queues
) {
1251 r
= table_add_many(table
,
1253 TABLE_STRING
, "Queue Length (Tx/Rx):");
1256 r
= table_add_cell_stringf(table
, NULL
, "%" PRIu32
"/%" PRIu32
, info
->tx_queues
, info
->rx_queues
);
1261 if (info
->has_ethtool_link_info
) {
1262 const char *duplex
= duplex_to_string(info
->duplex
);
1263 const char *port
= port_to_string(info
->port
);
1265 if (IN_SET(info
->autonegotiation
, AUTONEG_DISABLE
, AUTONEG_ENABLE
)) {
1266 r
= table_add_many(table
,
1268 TABLE_STRING
, "Auto negotiation:",
1269 TABLE_BOOLEAN
, info
->autonegotiation
== AUTONEG_ENABLE
);
1274 if (info
->speed
> 0) {
1275 r
= table_add_many(table
,
1277 TABLE_STRING
, "Speed:",
1278 TABLE_BPS
, info
->speed
);
1284 r
= table_add_many(table
,
1286 TABLE_STRING
, "Duplex:",
1287 TABLE_STRING
, duplex
);
1293 r
= table_add_many(table
,
1295 TABLE_STRING
, "Port:",
1296 TABLE_STRING
, port
);
1302 r
= dump_addresses(rtnl
, table
, info
->ifindex
);
1305 r
= dump_gateways(rtnl
, hwdb
, table
, info
->ifindex
);
1308 r
= dump_list(table
, "DNS:", dns
);
1311 r
= dump_list(table
, "Search Domains:", search_domains
);
1314 r
= dump_list(table
, "Route Domains:", route_domains
);
1317 r
= dump_list(table
, "NTP:", ntp
);
1320 r
= dump_ifindexes(table
, "Carrier Bound To:", carrier_bound_to
);
1323 r
= dump_ifindexes(table
, "Carrier Bound By:", carrier_bound_by
);
1327 (void) sd_network_link_get_timezone(info
->ifindex
, &tz
);
1329 r
= table_add_many(table
,
1331 TABLE_STRING
, "Time Zone:",
1337 r
= dump_lldp_neighbors(table
, "Connected To:", info
->ifindex
);
1341 r
= dump_statistics(table
, info
);
1345 return table_print(table
, NULL
);
1348 static int system_status(sd_netlink
*rtnl
, sd_hwdb
*hwdb
) {
1349 _cleanup_free_
char *operational_state
= NULL
;
1350 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
1351 const char *on_color_operational
, *off_color_operational
;
1352 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1358 (void) sd_network_get_operational_state(&operational_state
);
1359 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
1361 table
= table_new("dot", "key", "value");
1365 assert_se(cell
= table_get_cell(table
, 0, 0));
1366 (void) table_set_ellipsize_percent(table
, cell
, 100);
1368 assert_se(cell
= table_get_cell(table
, 0, 1));
1369 (void) table_set_align_percent(table
, cell
, 100);
1370 (void) table_set_ellipsize_percent(table
, cell
, 100);
1372 table_set_header(table
, false);
1374 r
= table_add_many(table
,
1375 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE
),
1376 TABLE_SET_COLOR
, on_color_operational
,
1377 TABLE_STRING
, "State:",
1378 TABLE_STRING
, strna(operational_state
),
1379 TABLE_SET_COLOR
, on_color_operational
);
1381 r
= dump_addresses(rtnl
, table
, 0);
1384 r
= dump_gateways(rtnl
, hwdb
, table
, 0);
1388 (void) sd_network_get_dns(&dns
);
1389 r
= dump_list(table
, "DNS:", dns
);
1393 (void) sd_network_get_search_domains(&search_domains
);
1394 r
= dump_list(table
, "Search Domains:", search_domains
);
1398 (void) sd_network_get_route_domains(&route_domains
);
1399 r
= dump_list(table
, "Route Domains:", route_domains
);
1403 (void) sd_network_get_ntp(&ntp
);
1404 r
= dump_list(table
, "NTP:", ntp
);
1408 return table_print(table
, NULL
);
1411 static int link_status(int argc
, char *argv
[], void *userdata
) {
1412 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1413 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1414 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
1415 _cleanup_free_ LinkInfo
*links
= NULL
;
1418 (void) pager_open(arg_pager_flags
);
1420 r
= sd_bus_open_system(&bus
);
1422 return log_error_errno(r
, "Failed to connect system bus: %m");
1424 r
= sd_netlink_open(&rtnl
);
1426 return log_error_errno(r
, "Failed to connect to netlink: %m");
1428 r
= sd_hwdb_new(&hwdb
);
1430 log_debug_errno(r
, "Failed to open hardware database: %m");
1433 c
= acquire_link_info(bus
, rtnl
, NULL
, &links
);
1435 return system_status(rtnl
, hwdb
);
1437 c
= acquire_link_info(bus
, rtnl
, argv
+ 1, &links
);
1441 for (i
= 0; i
< c
; i
++) {
1443 fputc('\n', stdout
);
1445 link_status_one(rtnl
, hwdb
, links
+ i
);
1451 static char *lldp_capabilities_to_string(uint16_t x
) {
1452 static const char characters
[] = {
1453 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1458 ret
= new(char, ELEMENTSOF(characters
) + 1);
1462 for (i
= 0; i
< ELEMENTSOF(characters
); i
++)
1463 ret
[i
] = (x
& (1U << i
)) ? characters
[i
] : '.';
1469 static void lldp_capabilities_legend(uint16_t x
) {
1470 unsigned w
, i
, cols
= columns();
1471 static const char* const table
[] = {
1475 "w - WLAN Access Point",
1478 "d - DOCSIS cable device",
1480 "c - Customer VLAN",
1482 "m - Two-port MAC Relay (TPMR)",
1488 printf("\nCapability Flags:\n");
1489 for (w
= 0, i
= 0; i
< ELEMENTSOF(table
); i
++)
1490 if (x
& (1U << i
) || arg_all
) {
1493 newline
= w
+ strlen(table
[i
]) + (w
== 0 ? 0 : 2) > cols
;
1496 w
+= printf("%s%s%s", newline
? "\n" : "", w
== 0 ? "" : "; ", table
[i
]);
1501 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
1502 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1503 _cleanup_free_ LinkInfo
*links
= NULL
;
1504 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1509 r
= sd_netlink_open(&rtnl
);
1511 return log_error_errno(r
, "Failed to connect to netlink: %m");
1513 c
= acquire_link_info(NULL
, rtnl
, argc
> 1 ? argv
+ 1 : NULL
, &links
);
1517 (void) pager_open(arg_pager_flags
);
1519 table
= table_new("link",
1524 "port description");
1528 table_set_header(table
, arg_legend
);
1530 assert_se(cell
= table_get_cell(table
, 0, 0));
1531 table_set_minimum_width(table
, cell
, 16);
1533 assert_se(cell
= table_get_cell(table
, 0, 1));
1534 table_set_minimum_width(table
, cell
, 17);
1536 assert_se(cell
= table_get_cell(table
, 0, 2));
1537 table_set_minimum_width(table
, cell
, 16);
1539 assert_se(cell
= table_get_cell(table
, 0, 3));
1540 table_set_minimum_width(table
, cell
, 11);
1542 assert_se(cell
= table_get_cell(table
, 0, 4));
1543 table_set_minimum_width(table
, cell
, 17);
1545 assert_se(cell
= table_get_cell(table
, 0, 5));
1546 table_set_minimum_width(table
, cell
, 16);
1548 for (i
= 0; i
< c
; i
++) {
1549 _cleanup_fclose_
FILE *f
= NULL
;
1551 r
= open_lldp_neighbors(links
[i
].ifindex
, &f
);
1555 log_warning_errno(r
, "Failed to open LLDP data for %i, ignoring: %m", links
[i
].ifindex
);
1560 _cleanup_free_
char *cid
= NULL
, *pid
= NULL
, *sname
= NULL
, *pdesc
= NULL
, *capabilities
= NULL
;
1561 const char *chassis_id
= NULL
, *port_id
= NULL
, *system_name
= NULL
, *port_description
= NULL
;
1562 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
1565 r
= next_lldp_neighbor(f
, &n
);
1567 log_warning_errno(r
, "Failed to read neighbor data: %m");
1573 (void) sd_lldp_neighbor_get_chassis_id_as_string(n
, &chassis_id
);
1574 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
1575 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
1576 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
1579 cid
= ellipsize(chassis_id
, 17, 100);
1585 pid
= ellipsize(port_id
, 17, 100);
1591 sname
= ellipsize(system_name
, 16, 100);
1593 system_name
= sname
;
1596 if (port_description
) {
1597 pdesc
= ellipsize(port_description
, 16, 100);
1599 port_description
= pdesc
;
1602 if (sd_lldp_neighbor_get_enabled_capabilities(n
, &cc
) >= 0) {
1603 capabilities
= lldp_capabilities_to_string(cc
);
1607 r
= table_add_many(table
,
1608 TABLE_STRING
, links
[i
].name
,
1609 TABLE_STRING
, strna(chassis_id
),
1610 TABLE_STRING
, strna(system_name
),
1611 TABLE_STRING
, strna(capabilities
),
1612 TABLE_STRING
, strna(port_id
),
1613 TABLE_STRING
, strna(port_description
));
1621 r
= table_print(table
, NULL
);
1626 lldp_capabilities_legend(all
);
1627 printf("\n%i neighbors listed.\n", m
);
1633 static int link_delete_send_message(sd_netlink
*rtnl
, int index
) {
1634 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
1639 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_DELLINK
, index
);
1641 return rtnl_log_create_error(r
);
1643 r
= sd_netlink_call(rtnl
, req
, 0, NULL
);
1650 static int link_delete(int argc
, char *argv
[], void *userdata
) {
1651 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1652 _cleanup_set_free_ Set
*indexes
= NULL
;
1657 r
= sd_netlink_open(&rtnl
);
1659 return log_error_errno(r
, "Failed to connect to netlink: %m");
1661 indexes
= set_new(NULL
);
1665 for (i
= 1; i
< argc
; i
++) {
1666 r
= parse_ifindex_or_ifname(argv
[i
], &index
);
1668 return log_error_errno(r
, "Failed to resolve interface %s", argv
[i
]);
1670 r
= set_put(indexes
, INT_TO_PTR(index
));
1675 SET_FOREACH(p
, indexes
, j
) {
1676 r
= link_delete_send_message(rtnl
, PTR_TO_INT(p
));
1678 char ifname
[IF_NAMESIZE
+ 1];
1680 if (format_ifname(index
, ifname
))
1681 return log_error_errno(r
, "Failed to delete interface %s: %m", ifname
);
1683 return log_error_errno(r
, "Failed to delete interface %d: %m", index
);
1690 static int help(void) {
1691 _cleanup_free_
char *link
= NULL
;
1694 r
= terminal_urlify_man("networkctl", "1", &link
);
1698 printf("%s [OPTIONS...]\n\n"
1699 "Query and control the networking subsystem.\n\n"
1700 " -h --help Show this help\n"
1701 " --version Show package version\n"
1702 " --no-pager Do not pipe output into a pager\n"
1703 " --no-legend Do not show the headers and footers\n"
1704 " -a --all Show status for all links\n"
1705 " -s --stats Show detailed link statics\n"
1707 " list [PATTERN...] List links\n"
1708 " status [PATTERN...] Show link status\n"
1709 " lldp [PATTERN...] Show LLDP neighbors\n"
1710 " label Show current address label entries in the kernel\n"
1711 " delete DEVICES Delete virtual netdevs\n"
1712 "\nSee the %s for details.\n"
1713 , program_invocation_short_name
1720 static int parse_argv(int argc
, char *argv
[]) {
1723 ARG_VERSION
= 0x100,
1728 static const struct option options
[] = {
1729 { "help", no_argument
, NULL
, 'h' },
1730 { "version", no_argument
, NULL
, ARG_VERSION
},
1731 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1732 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1733 { "all", no_argument
, NULL
, 'a' },
1734 { "stats", no_argument
, NULL
, 's' },
1743 while ((c
= getopt_long(argc
, argv
, "has", options
, NULL
)) >= 0) {
1754 arg_pager_flags
|= PAGER_DISABLE
;
1773 assert_not_reached("Unhandled option");
1780 static int networkctl_main(int argc
, char *argv
[]) {
1781 static const Verb verbs
[] = {
1782 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, list_links
},
1783 { "status", VERB_ANY
, VERB_ANY
, 0, link_status
},
1784 { "lldp", VERB_ANY
, VERB_ANY
, 0, link_lldp_status
},
1785 { "label", VERB_ANY
, VERB_ANY
, 0, list_address_labels
},
1786 { "delete", 2, VERB_ANY
, 0, link_delete
},
1790 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1793 static void warn_networkd_missing(void) {
1795 if (access("/run/systemd/netif/state", F_OK
) >= 0)
1798 fprintf(stderr
, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1801 static int run(int argc
, char* argv
[]) {
1804 log_show_color(true);
1805 log_parse_environment();
1808 r
= parse_argv(argc
, argv
);
1812 warn_networkd_missing();
1814 return networkctl_main(argc
, argv
);
1817 DEFINE_MAIN_FUNCTION(run
);