1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "sd-device.h"
28 #include "sd-netlink.h"
29 #include "sd-network.h"
31 #include "arphrd-list.h"
32 #include "device-util.h"
33 #include "ether-addr-util.h"
34 #include "hwdb-util.h"
36 #include "local-addresses.h"
37 #include "netlink-util.h"
39 #include "socket-util.h"
41 #include "terminal-util.h"
45 static bool arg_no_pager
= false;
46 static bool arg_legend
= true;
47 static bool arg_all
= false;
49 static void pager_open_if_enabled(void) {
57 static int link_get_type_string(int iftype
, sd_device
*d
, char **ret
) {
63 if (iftype
== ARPHRD_ETHER
&& d
) {
64 const char *devtype
= NULL
, *id
= NULL
;
65 /* WLANs have iftype ARPHRD_ETHER, but we want
66 * to show a more useful type string for
69 (void)sd_device_get_devtype(d
, &devtype
);
71 if (streq_ptr(devtype
, "wlan"))
73 else if (streq_ptr(devtype
, "wwan"))
86 t
= arphrd_to_name(iftype
);
102 typedef struct LinkInfo
{
108 static int link_info_compare(const void *a
, const void *b
) {
109 const LinkInfo
*x
= a
, *y
= b
;
111 return x
->ifindex
- y
->ifindex
;
114 static int decode_and_sort_links(sd_netlink_message
*m
, LinkInfo
**ret
) {
115 _cleanup_free_ LinkInfo
*links
= NULL
;
116 size_t size
= 0, c
= 0;
117 sd_netlink_message
*i
;
120 for (i
= m
; i
; i
= sd_netlink_message_next(i
)) {
126 r
= sd_netlink_message_get_type(i
, &type
);
130 if (type
!= RTM_NEWLINK
)
133 r
= sd_rtnl_message_link_get_ifindex(i
, &ifindex
);
137 r
= sd_netlink_message_read_string(i
, IFLA_IFNAME
, &name
);
141 r
= sd_rtnl_message_link_get_type(i
, &iftype
);
145 if (!GREEDY_REALLOC(links
, size
, c
+1))
148 links
[c
].name
= name
;
149 links
[c
].ifindex
= ifindex
;
150 links
[c
].iftype
= iftype
;
154 qsort_safe(links
, c
, sizeof(LinkInfo
), link_info_compare
);
162 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
166 if (streq_ptr(state
, "routable")) {
167 *on
= ansi_highlight_green();
168 *off
= ansi_normal();
169 } else if (streq_ptr(state
, "degraded")) {
170 *on
= ansi_highlight_yellow();
171 *off
= ansi_normal();
176 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
180 if (streq_ptr(state
, "configured")) {
181 *on
= ansi_highlight_green();
182 *off
= ansi_normal();
183 } else if (streq_ptr(state
, "configuring")) {
184 *on
= ansi_highlight_yellow();
185 *off
= ansi_normal();
186 } else if (streq_ptr(state
, "failed") || streq_ptr(state
, "linger")) {
187 *on
= ansi_highlight_red();
188 *off
= ansi_normal();
193 static int list_links(int argc
, char *argv
[], void *userdata
) {
194 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
195 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
196 _cleanup_free_ LinkInfo
*links
= NULL
;
199 pager_open_if_enabled();
201 r
= sd_netlink_open(&rtnl
);
203 return log_error_errno(r
, "Failed to connect to netlink: %m");
205 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
207 return rtnl_log_create_error(r
);
209 r
= sd_netlink_message_request_dump(req
, true);
211 return rtnl_log_create_error(r
);
213 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
215 return log_error_errno(r
, "Failed to enumerate links: %m");
218 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
220 c
= decode_and_sort_links(reply
, &links
);
222 return rtnl_log_parse_error(c
);
224 for (i
= 0; i
< c
; i
++) {
225 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
226 _cleanup_device_unref_ sd_device
*d
= NULL
;
227 const char *on_color_operational
, *off_color_operational
,
228 *on_color_setup
, *off_color_setup
;
229 char devid
[2 + DECIMAL_STR_MAX(int)];
230 _cleanup_free_
char *t
= NULL
;
232 sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
233 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
235 sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
236 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
238 sprintf(devid
, "n%i", links
[i
].ifindex
);
239 (void)sd_device_new_from_device_id(&d
, devid
);
241 link_get_type_string(links
[i
].iftype
, d
, &t
);
243 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
244 links
[i
].ifindex
, links
[i
].name
, strna(t
),
245 on_color_operational
, strna(operational_state
), off_color_operational
,
246 on_color_setup
, strna(setup_state
), off_color_setup
);
250 printf("\n%i links listed.\n", c
);
255 /* IEEE Organizationally Unique Identifier vendor string */
256 static int ieee_oui(sd_hwdb
*hwdb
, struct ether_addr
*mac
, char **ret
) {
257 const char *description
;
258 char modalias
[strlen("OUI:XXYYXXYYXXYY") + 1], *desc
;
269 /* skip commonly misused 00:00:00 (Xerox) prefix */
270 if (memcmp(mac
, "\0\0\0", 3) == 0)
273 snprintf(modalias
, sizeof(modalias
), "OUI:" ETHER_ADDR_FORMAT_STR
, ETHER_ADDR_FORMAT_VAL(*mac
));
275 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
279 desc
= strdup(description
);
288 static int get_gateway_description(
293 union in_addr_union
*gateway
,
294 char **gateway_description
) {
295 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
296 sd_netlink_message
*m
;
300 assert(ifindex
>= 0);
301 assert(family
== AF_INET
|| family
== AF_INET6
);
303 assert(gateway_description
);
305 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
309 r
= sd_netlink_message_request_dump(req
, true);
313 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
317 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
318 union in_addr_union gw
= {};
319 struct ether_addr mac
= {};
323 r
= sd_netlink_message_get_errno(m
);
325 log_error_errno(r
, "got error: %m");
329 r
= sd_netlink_message_get_type(m
, &type
);
331 log_error_errno(r
, "could not get type: %m");
335 if (type
!= RTM_NEWNEIGH
) {
336 log_error("type is not RTM_NEWNEIGH");
340 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
342 log_error_errno(r
, "could not get family: %m");
347 log_error("family is not correct");
351 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
353 log_error_errno(r
, "could not get ifindex: %m");
357 if (ifindex
> 0 && ifi
!= ifindex
)
362 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
368 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
377 if (!in_addr_equal(fam
, &gw
, gateway
))
380 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
384 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
394 static int dump_gateways(
399 _cleanup_free_
struct local_address
*local
= NULL
;
402 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
406 for (i
= 0; i
< n
; i
++) {
407 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
;
409 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
413 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
415 log_debug_errno(r
, "Could not get description of gateway: %m");
418 (int) strlen(prefix
),
419 i
== 0 ? prefix
: "",
423 printf(" (%s)", description
);
425 /* Show interface name for the entry if we show
426 * entries for all interfaces */
428 char name
[IF_NAMESIZE
+1];
430 if (if_indextoname(local
[i
].ifindex
, name
)) {
431 fputs(" on ", stdout
);
434 printf(" on %%%i", local
[i
].ifindex
);
443 static int dump_addresses(
448 _cleanup_free_
struct local_address
*local
= NULL
;
451 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
455 for (i
= 0; i
< n
; i
++) {
456 _cleanup_free_
char *pretty
= NULL
;
458 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
463 (int) strlen(prefix
),
464 i
== 0 ? prefix
: "",
468 char name
[IF_NAMESIZE
+1];
470 if (if_indextoname(local
[i
].ifindex
, name
)) {
471 fputs(" on ", stdout
);
474 printf(" on %%%i", local
[i
].ifindex
);
483 static void dump_list(const char *prefix
, char **l
) {
488 (int) strlen(prefix
),
489 i
== l
? prefix
: "",
494 static int link_status_one(
498 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
499 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
500 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
501 _cleanup_device_unref_ sd_device
*d
= NULL
;
502 char devid
[2 + DECIMAL_STR_MAX(int)];
503 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
504 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
505 const char *on_color_operational
, *off_color_operational
,
506 *on_color_setup
, *off_color_setup
;
507 _cleanup_strv_free_
char **carrier_bound_to
= NULL
;
508 _cleanup_strv_free_
char **carrier_bound_by
= NULL
;
518 if (safe_atoi(name
, &ifindex
) >= 0 && ifindex
> 0)
519 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, ifindex
);
521 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
523 return rtnl_log_create_error(r
);
525 r
= sd_netlink_message_append_string(req
, IFLA_IFNAME
, name
);
529 return rtnl_log_create_error(r
);
531 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
533 return log_error_errno(r
, "Failed to query link: %m");
535 r
= sd_rtnl_message_link_get_ifindex(reply
, &ifindex
);
537 return rtnl_log_parse_error(r
);
539 r
= sd_netlink_message_read_string(reply
, IFLA_IFNAME
, &name
);
541 return rtnl_log_parse_error(r
);
543 r
= sd_rtnl_message_link_get_type(reply
, &iftype
);
545 return rtnl_log_parse_error(r
);
547 have_mac
= sd_netlink_message_read_ether_addr(reply
, IFLA_ADDRESS
, &e
) >= 0;
551 bool all_zeroes
= true;
553 for (p
= (uint8_t*) &e
; p
< (uint8_t*) &e
+ sizeof(e
); p
++)
563 sd_netlink_message_read_u32(reply
, IFLA_MTU
, &mtu
);
565 sd_network_link_get_operational_state(ifindex
, &operational_state
);
566 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
568 sd_network_link_get_setup_state(ifindex
, &setup_state
);
569 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
571 sd_network_link_get_dns(ifindex
, &dns
);
572 sd_network_link_get_domains(ifindex
, &domains
);
573 r
= sd_network_link_get_wildcard_domain(ifindex
);
577 wildcard
= strdup("*");
581 if (strv_consume(&domains
, wildcard
) < 0)
585 sprintf(devid
, "n%i", ifindex
);
587 (void)sd_device_new_from_device_id(&d
, devid
);
590 (void)sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
591 (void)sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
592 (void)sd_device_get_property_value(d
, "ID_PATH", &path
);
594 r
= sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
);
596 (void)sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
598 r
= sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
);
600 (void)sd_device_get_property_value(d
, "ID_MODEL", &model
);
603 link_get_type_string(iftype
, d
, &t
);
605 sd_network_link_get_network_file(ifindex
, &network
);
607 sd_network_link_get_carrier_bound_to(ifindex
, &carrier_bound_to
);
608 sd_network_link_get_carrier_bound_by(ifindex
, &carrier_bound_by
);
610 printf("%s%s%s %i: %s\n", on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
, ifindex
, name
);
612 printf(" Link File: %s\n"
613 " Network File: %s\n"
615 " State: %s%s%s (%s%s%s)\n",
619 on_color_operational
, strna(operational_state
), off_color_operational
,
620 on_color_setup
, strna(setup_state
), off_color_setup
);
623 printf(" Path: %s\n", path
);
625 printf(" Driver: %s\n", driver
);
627 printf(" Vendor: %s\n", vendor
);
629 printf(" Model: %s\n", model
);
632 _cleanup_free_
char *description
= NULL
;
633 char ea
[ETHER_ADDR_TO_STRING_MAX
];
635 ieee_oui(hwdb
, &e
, &description
);
638 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e
, ea
), description
);
640 printf(" HW Address: %s\n", ether_addr_to_string(&e
, ea
));
644 printf(" MTU: %u\n", mtu
);
646 dump_addresses(rtnl
, " Address: ", ifindex
);
647 dump_gateways(rtnl
, hwdb
, " Gateway: ", ifindex
);
649 if (!strv_isempty(dns
))
650 dump_list(" DNS: ", dns
);
651 if (!strv_isempty(domains
))
652 dump_list(" Domain: ", domains
);
654 (void) sd_network_link_get_ntp(ifindex
, &ntp
);
655 if (!strv_isempty(ntp
))
656 dump_list(" NTP: ", ntp
);
658 if (!strv_isempty(carrier_bound_to
))
659 dump_list("Carrier Bound To: ", carrier_bound_to
);
661 if (!strv_isempty(carrier_bound_by
))
662 dump_list("Carrier Bound By: ", carrier_bound_by
);
664 (void) sd_network_link_get_timezone(ifindex
, &tz
);
666 printf(" Time Zone: %s", tz
);
671 static int link_status(int argc
, char *argv
[], void *userdata
) {
672 _cleanup_hwdb_unref_ sd_hwdb
*hwdb
= NULL
;
673 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
677 r
= sd_netlink_open(&rtnl
);
679 return log_error_errno(r
, "Failed to connect to netlink: %m");
681 r
= sd_hwdb_new(&hwdb
);
683 log_debug_errno(r
, "Failed to open hardware database: %m");
685 if (argc
<= 1 && !arg_all
) {
686 _cleanup_free_
char *operational_state
= NULL
;
687 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
688 const char *on_color_operational
, *off_color_operational
;
690 sd_network_get_operational_state(&operational_state
);
691 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
693 printf("%s%s%s State: %s%s%s\n",
694 on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
,
695 on_color_operational
, strna(operational_state
), off_color_operational
);
697 dump_addresses(rtnl
, " Address: ", 0);
698 dump_gateways(rtnl
, hwdb
, " Gateway: ", 0);
700 sd_network_get_dns(&dns
);
701 if (!strv_isempty(dns
))
702 dump_list(" DNS: ", dns
);
704 sd_network_get_domains(&domains
);
705 if (!strv_isempty(domains
))
706 dump_list(" Domain: ", domains
);
708 sd_network_get_ntp(&ntp
);
709 if (!strv_isempty(ntp
))
710 dump_list(" NTP: ", ntp
);
715 pager_open_if_enabled();
718 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
719 _cleanup_free_ LinkInfo
*links
= NULL
;
722 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
724 return rtnl_log_create_error(r
);
726 r
= sd_netlink_message_request_dump(req
, true);
728 return rtnl_log_create_error(r
);
730 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
732 return log_error_errno(r
, "Failed to enumerate links: %m");
734 c
= decode_and_sort_links(reply
, &links
);
736 return rtnl_log_parse_error(c
);
738 for (i
= 0; i
< c
; i
++) {
742 link_status_one(rtnl
, hwdb
, links
[i
].name
);
745 STRV_FOREACH(name
, argv
+ 1) {
746 if (name
!= argv
+ 1)
749 link_status_one(rtnl
, hwdb
, *name
);
756 const char *lldp_system_capability_to_string(LLDPSystemCapabilities d
) _const_
;
757 LLDPSystemCapabilities
lldp_system_capability_from_string(const char *d
) _pure_
;
759 static const char* const lldp_system_capability_table
[_LLDP_SYSTEM_CAPABILITIES_MAX
+ 1] = {
760 [LLDP_SYSTEM_CAPABILITIES_OTHER
] = "O",
761 [LLDP_SYSTEM_CAPABILITIES_REPEATER
] = "P",
762 [LLDP_SYSTEM_CAPABILITIES_BRIDGE
] = "B",
763 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP
] = "W",
764 [LLDP_SYSTEM_CAPABILITIES_ROUTER
] = "R",
765 [LLDP_SYSTEM_CAPABILITIES_PHONE
] = "T",
766 [LLDP_SYSTEM_CAPABILITIES_DOCSIS
] = "D",
767 [LLDP_SYSTEM_CAPABILITIES_STATION
] = "A",
768 [LLDP_SYSTEM_CAPABILITIES_CVLAN
] = "C",
769 [LLDP_SYSTEM_CAPABILITIES_SVLAN
] = "S",
770 [LLDP_SYSTEM_CAPABILITIES_TPMR
] = "M",
771 [_LLDP_SYSTEM_CAPABILITIES_MAX
] = "N/A",
774 DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability
, LLDPSystemCapabilities
);
776 static char *lldp_system_caps(uint16_t cap
) {
777 _cleanup_free_
char *s
= NULL
, *t
= NULL
;
784 if (cap
& LLDP_SYSTEM_CAPABILITIES_OTHER
) {
785 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER
), " ", NULL
);
793 if (cap
& LLDP_SYSTEM_CAPABILITIES_REPEATER
) {
794 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER
), " ", NULL
);
802 if (cap
& LLDP_SYSTEM_CAPABILITIES_BRIDGE
) {
803 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE
), " ", NULL
);
811 if (cap
& LLDP_SYSTEM_CAPABILITIES_WLAN_AP
) {
812 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP
), " ", NULL
);
820 if (cap
& LLDP_SYSTEM_CAPABILITIES_ROUTER
) {
821 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER
), " ", NULL
);
829 if (cap
& LLDP_SYSTEM_CAPABILITIES_PHONE
) {
830 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE
), " ", NULL
);
838 if (cap
& LLDP_SYSTEM_CAPABILITIES_DOCSIS
) {
839 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS
), " ", NULL
);
847 if (cap
& LLDP_SYSTEM_CAPABILITIES_STATION
) {
848 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION
), " ", NULL
);
856 if (cap
& LLDP_SYSTEM_CAPABILITIES_CVLAN
) {
857 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN
), " ", NULL
);
865 if (cap
& LLDP_SYSTEM_CAPABILITIES_SVLAN
) {
866 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN
), " ", NULL
);
874 if (cap
& LLDP_SYSTEM_CAPABILITIES_TPMR
) {
875 s
= strappend(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR
));
883 s
= strappend(t
, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX
));
890 t
= strappend(s
, "]");
903 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
904 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
905 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
906 _cleanup_free_ LinkInfo
*links
= NULL
;
907 const char *state
, *word
;
915 pager_open_if_enabled();
917 r
= sd_netlink_open(&rtnl
);
919 return log_error_errno(r
, "Failed to connect to netlink: %m");
921 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
923 return rtnl_log_create_error(r
);
925 r
= sd_netlink_message_request_dump(req
, true);
927 return rtnl_log_create_error(r
);
929 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
931 return log_error_errno(r
, "Failed to enumerate links: %m");
933 c
= decode_and_sort_links(reply
, &links
);
935 return rtnl_log_parse_error(c
);
938 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
940 for (i
= j
= 0; i
< c
; i
++) {
941 _cleanup_free_
char *chassis
= NULL
, *port
= NULL
, *cap
= NULL
, *lldp
= NULL
;
942 _cleanup_strv_free_
char **l
= NULL
;
944 r
= sd_network_link_get_lldp(links
[i
].ifindex
, &lldp
);
948 l
= strv_split_newlines(lldp
);
953 FOREACH_WORD_QUOTED(word
, ll
, *s
, state
) {
954 _cleanup_free_
char *t
= NULL
, *a
= NULL
, *b
= NULL
;
956 t
= strndup(word
, ll
);
960 r
= split_pair(t
, "=", &a
, &b
);
964 if (streq(a
, "_Chassis")) {
965 r
= free_and_strdup(&chassis
, b
);
969 } else if (streq(a
, "_Port")) {
970 r
= free_and_strdup(&port
, b
);
974 } else if (streq(a
, "_TTL")) {
975 long long unsigned x
= 0;
978 r
= safe_atollu(b
, &x
);
979 if (r
< 0 || (usec_t
) x
!= x
)
980 return log_warning_errno(r
< 0 ? r
: ERANGE
,
981 "Failed to parse TTL \"%s\": %m", b
);
983 time
= now(clock_boottime_or_monotonic());
987 ttl
= (double) (x
- time
) / USEC_PER_SEC
;
989 } else if (streq(a
, "_CAP")) {
990 sscanf(b
, "%x", &capability
);
992 cap
= lldp_system_caps(capability
);
998 printf("%10s %24s %16s %16f %16s\n",
1000 strna(chassis
), strna(port
),
1008 printf("\nCapability Codes:\n"
1009 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1010 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1011 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1013 printf("Total entries displayed: %d\n", j
);
1019 static void help(void) {
1020 printf("%s [OPTIONS...]\n\n"
1021 "Query and control the networking subsystem.\n\n"
1022 " -h --help Show this help\n"
1023 " --version Show package version\n"
1024 " --no-pager Do not pipe output into a pager\n"
1025 " --no-legend Do not show the headers and footers\n"
1026 " -a --all Show status for all links\n\n"
1028 " list List links\n"
1029 " status [LINK...] Show link status\n"
1030 " lldp Show lldp information\n"
1031 , program_invocation_short_name
);
1034 static int parse_argv(int argc
, char *argv
[]) {
1037 ARG_VERSION
= 0x100,
1042 static const struct option options
[] = {
1043 { "help", no_argument
, NULL
, 'h' },
1044 { "version", no_argument
, NULL
, ARG_VERSION
},
1045 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1046 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1047 { "all", no_argument
, NULL
, 'a' },
1056 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0) {
1068 arg_no_pager
= true;
1083 assert_not_reached("Unhandled option");
1090 static int networkctl_main(int argc
, char *argv
[]) {
1091 const Verb verbs
[] = {
1092 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_links
},
1093 { "status", 1, VERB_ANY
, 0, link_status
},
1094 { "lldp", VERB_ANY
, 1, VERB_DEFAULT
, link_lldp_status
},
1098 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1101 int main(int argc
, char* argv
[]) {
1104 log_parse_environment();
1107 r
= parse_argv(argc
, argv
);
1111 r
= networkctl_main(argc
, argv
);
1116 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;