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 "alloc-util.h"
32 #include "arphrd-list.h"
33 #include "device-util.h"
34 #include "ether-addr-util.h"
35 #include "hwdb-util.h"
37 #include "local-addresses.h"
38 #include "locale-util.h"
39 #include "netlink-util.h"
41 #include "parse-util.h"
42 #include "socket-util.h"
43 #include "stdio-util.h"
44 #include "string-table.h"
45 #include "string-util.h"
47 #include "terminal-util.h"
51 static bool arg_no_pager
= false;
52 static bool arg_legend
= true;
53 static bool arg_all
= false;
55 static void pager_open_if_enabled(void) {
63 static int link_get_type_string(int iftype
, sd_device
*d
, char **ret
) {
69 if (iftype
== ARPHRD_ETHER
&& d
) {
70 const char *devtype
= NULL
, *id
= NULL
;
71 /* WLANs have iftype ARPHRD_ETHER, but we want
72 * to show a more useful type string for
75 (void)sd_device_get_devtype(d
, &devtype
);
77 if (streq_ptr(devtype
, "wlan"))
79 else if (streq_ptr(devtype
, "wwan"))
92 t
= arphrd_to_name(iftype
);
108 typedef struct LinkInfo
{
114 static int link_info_compare(const void *a
, const void *b
) {
115 const LinkInfo
*x
= a
, *y
= b
;
117 return x
->ifindex
- y
->ifindex
;
120 static int decode_and_sort_links(sd_netlink_message
*m
, LinkInfo
**ret
) {
121 _cleanup_free_ LinkInfo
*links
= NULL
;
122 size_t size
= 0, c
= 0;
123 sd_netlink_message
*i
;
126 for (i
= m
; i
; i
= sd_netlink_message_next(i
)) {
132 r
= sd_netlink_message_get_type(i
, &type
);
136 if (type
!= RTM_NEWLINK
)
139 r
= sd_rtnl_message_link_get_ifindex(i
, &ifindex
);
143 r
= sd_netlink_message_read_string(i
, IFLA_IFNAME
, &name
);
147 r
= sd_rtnl_message_link_get_type(i
, &iftype
);
151 if (!GREEDY_REALLOC(links
, size
, c
+1))
154 links
[c
].name
= name
;
155 links
[c
].ifindex
= ifindex
;
156 links
[c
].iftype
= iftype
;
160 qsort_safe(links
, c
, sizeof(LinkInfo
), link_info_compare
);
168 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
172 if (streq_ptr(state
, "routable")) {
173 *on
= ansi_highlight_green();
174 *off
= ansi_normal();
175 } else if (streq_ptr(state
, "degraded")) {
176 *on
= ansi_highlight_yellow();
177 *off
= ansi_normal();
182 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
186 if (streq_ptr(state
, "configured")) {
187 *on
= ansi_highlight_green();
188 *off
= ansi_normal();
189 } else if (streq_ptr(state
, "configuring")) {
190 *on
= ansi_highlight_yellow();
191 *off
= ansi_normal();
192 } else if (streq_ptr(state
, "failed") || streq_ptr(state
, "linger")) {
193 *on
= ansi_highlight_red();
194 *off
= ansi_normal();
199 static int list_links(int argc
, char *argv
[], void *userdata
) {
200 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
201 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
202 _cleanup_free_ LinkInfo
*links
= NULL
;
205 pager_open_if_enabled();
207 r
= sd_netlink_open(&rtnl
);
209 return log_error_errno(r
, "Failed to connect to netlink: %m");
211 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
213 return rtnl_log_create_error(r
);
215 r
= sd_netlink_message_request_dump(req
, true);
217 return rtnl_log_create_error(r
);
219 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
221 return log_error_errno(r
, "Failed to enumerate links: %m");
224 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
226 c
= decode_and_sort_links(reply
, &links
);
228 return rtnl_log_parse_error(c
);
230 for (i
= 0; i
< c
; i
++) {
231 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
232 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
233 const char *on_color_operational
, *off_color_operational
,
234 *on_color_setup
, *off_color_setup
;
235 char devid
[2 + DECIMAL_STR_MAX(int)];
236 _cleanup_free_
char *t
= NULL
;
238 sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
239 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
241 sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
242 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
244 sprintf(devid
, "n%i", links
[i
].ifindex
);
245 (void)sd_device_new_from_device_id(&d
, devid
);
247 link_get_type_string(links
[i
].iftype
, d
, &t
);
249 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
250 links
[i
].ifindex
, links
[i
].name
, strna(t
),
251 on_color_operational
, strna(operational_state
), off_color_operational
,
252 on_color_setup
, strna(setup_state
), off_color_setup
);
256 printf("\n%i links listed.\n", c
);
261 /* IEEE Organizationally Unique Identifier vendor string */
262 static int ieee_oui(sd_hwdb
*hwdb
, struct ether_addr
*mac
, char **ret
) {
263 const char *description
;
264 char modalias
[strlen("OUI:XXYYXXYYXXYY") + 1], *desc
;
275 /* skip commonly misused 00:00:00 (Xerox) prefix */
276 if (memcmp(mac
, "\0\0\0", 3) == 0)
279 xsprintf(modalias
, "OUI:" ETHER_ADDR_FORMAT_STR
,
280 ETHER_ADDR_FORMAT_VAL(*mac
));
282 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
286 desc
= strdup(description
);
295 static int get_gateway_description(
300 union in_addr_union
*gateway
,
301 char **gateway_description
) {
302 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
303 sd_netlink_message
*m
;
307 assert(ifindex
>= 0);
308 assert(family
== AF_INET
|| family
== AF_INET6
);
310 assert(gateway_description
);
312 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
316 r
= sd_netlink_message_request_dump(req
, true);
320 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
324 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
325 union in_addr_union gw
= {};
326 struct ether_addr mac
= {};
330 r
= sd_netlink_message_get_errno(m
);
332 log_error_errno(r
, "got error: %m");
336 r
= sd_netlink_message_get_type(m
, &type
);
338 log_error_errno(r
, "could not get type: %m");
342 if (type
!= RTM_NEWNEIGH
) {
343 log_error("type is not RTM_NEWNEIGH");
347 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
349 log_error_errno(r
, "could not get family: %m");
354 log_error("family is not correct");
358 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
360 log_error_errno(r
, "could not get ifindex: %m");
364 if (ifindex
> 0 && ifi
!= ifindex
)
369 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
375 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
384 if (!in_addr_equal(fam
, &gw
, gateway
))
387 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
391 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
401 static int dump_gateways(
406 _cleanup_free_
struct local_address
*local
= NULL
;
409 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
413 for (i
= 0; i
< n
; i
++) {
414 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
;
416 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
420 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
422 log_debug_errno(r
, "Could not get description of gateway: %m");
425 (int) strlen(prefix
),
426 i
== 0 ? prefix
: "",
430 printf(" (%s)", description
);
432 /* Show interface name for the entry if we show
433 * entries for all interfaces */
435 char name
[IF_NAMESIZE
+1];
437 if (if_indextoname(local
[i
].ifindex
, name
)) {
438 fputs(" on ", stdout
);
441 printf(" on %%%i", local
[i
].ifindex
);
450 static int dump_addresses(
455 _cleanup_free_
struct local_address
*local
= NULL
;
458 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
462 for (i
= 0; i
< n
; i
++) {
463 _cleanup_free_
char *pretty
= NULL
;
465 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
470 (int) strlen(prefix
),
471 i
== 0 ? prefix
: "",
475 char name
[IF_NAMESIZE
+1];
477 if (if_indextoname(local
[i
].ifindex
, name
)) {
478 fputs(" on ", stdout
);
481 printf(" on %%%i", local
[i
].ifindex
);
490 static void dump_list(const char *prefix
, char **l
) {
495 (int) strlen(prefix
),
496 i
== l
? prefix
: "",
501 static int link_status_one(
505 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
506 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
507 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
508 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
509 char devid
[2 + DECIMAL_STR_MAX(int)];
510 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
511 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
512 const char *on_color_operational
, *off_color_operational
,
513 *on_color_setup
, *off_color_setup
;
514 _cleanup_strv_free_
char **carrier_bound_to
= NULL
;
515 _cleanup_strv_free_
char **carrier_bound_by
= NULL
;
525 if (parse_ifindex(name
, &ifindex
) >= 0)
526 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, ifindex
);
528 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
530 return rtnl_log_create_error(r
);
532 r
= sd_netlink_message_append_string(req
, IFLA_IFNAME
, name
);
536 return rtnl_log_create_error(r
);
538 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
540 return log_error_errno(r
, "Failed to query link: %m");
542 r
= sd_rtnl_message_link_get_ifindex(reply
, &ifindex
);
544 return rtnl_log_parse_error(r
);
546 r
= sd_netlink_message_read_string(reply
, IFLA_IFNAME
, &name
);
548 return rtnl_log_parse_error(r
);
550 r
= sd_rtnl_message_link_get_type(reply
, &iftype
);
552 return rtnl_log_parse_error(r
);
554 have_mac
= sd_netlink_message_read_ether_addr(reply
, IFLA_ADDRESS
, &e
) >= 0;
558 bool all_zeroes
= true;
560 for (p
= (uint8_t*) &e
; p
< (uint8_t*) &e
+ sizeof(e
); p
++)
570 sd_netlink_message_read_u32(reply
, IFLA_MTU
, &mtu
);
572 sd_network_link_get_operational_state(ifindex
, &operational_state
);
573 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
575 sd_network_link_get_setup_state(ifindex
, &setup_state
);
576 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
578 sd_network_link_get_dns(ifindex
, &dns
);
579 sd_network_link_get_domains(ifindex
, &domains
);
580 r
= sd_network_link_get_wildcard_domain(ifindex
);
584 wildcard
= strdup("*");
588 if (strv_consume(&domains
, wildcard
) < 0)
592 sprintf(devid
, "n%i", ifindex
);
594 (void)sd_device_new_from_device_id(&d
, devid
);
597 (void)sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
598 (void)sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
599 (void)sd_device_get_property_value(d
, "ID_PATH", &path
);
601 r
= sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
);
603 (void)sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
605 r
= sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
);
607 (void)sd_device_get_property_value(d
, "ID_MODEL", &model
);
610 link_get_type_string(iftype
, d
, &t
);
612 sd_network_link_get_network_file(ifindex
, &network
);
614 sd_network_link_get_carrier_bound_to(ifindex
, &carrier_bound_to
);
615 sd_network_link_get_carrier_bound_by(ifindex
, &carrier_bound_by
);
617 printf("%s%s%s %i: %s\n", on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
, ifindex
, name
);
619 printf(" Link File: %s\n"
620 " Network File: %s\n"
622 " State: %s%s%s (%s%s%s)\n",
626 on_color_operational
, strna(operational_state
), off_color_operational
,
627 on_color_setup
, strna(setup_state
), off_color_setup
);
630 printf(" Path: %s\n", path
);
632 printf(" Driver: %s\n", driver
);
634 printf(" Vendor: %s\n", vendor
);
636 printf(" Model: %s\n", model
);
639 _cleanup_free_
char *description
= NULL
;
640 char ea
[ETHER_ADDR_TO_STRING_MAX
];
642 ieee_oui(hwdb
, &e
, &description
);
645 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e
, ea
), description
);
647 printf(" HW Address: %s\n", ether_addr_to_string(&e
, ea
));
651 printf(" MTU: %u\n", mtu
);
653 dump_addresses(rtnl
, " Address: ", ifindex
);
654 dump_gateways(rtnl
, hwdb
, " Gateway: ", ifindex
);
656 if (!strv_isempty(dns
))
657 dump_list(" DNS: ", dns
);
658 if (!strv_isempty(domains
))
659 dump_list(" Domain: ", domains
);
661 (void) sd_network_link_get_ntp(ifindex
, &ntp
);
662 if (!strv_isempty(ntp
))
663 dump_list(" NTP: ", ntp
);
665 if (!strv_isempty(carrier_bound_to
))
666 dump_list("Carrier Bound To: ", carrier_bound_to
);
668 if (!strv_isempty(carrier_bound_by
))
669 dump_list("Carrier Bound By: ", carrier_bound_by
);
671 (void) sd_network_link_get_timezone(ifindex
, &tz
);
673 printf(" Time Zone: %s", tz
);
678 static int link_status(int argc
, char *argv
[], void *userdata
) {
679 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
680 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
684 r
= sd_netlink_open(&rtnl
);
686 return log_error_errno(r
, "Failed to connect to netlink: %m");
688 r
= sd_hwdb_new(&hwdb
);
690 log_debug_errno(r
, "Failed to open hardware database: %m");
692 if (argc
<= 1 && !arg_all
) {
693 _cleanup_free_
char *operational_state
= NULL
;
694 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
695 const char *on_color_operational
, *off_color_operational
;
697 sd_network_get_operational_state(&operational_state
);
698 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
700 printf("%s%s%s State: %s%s%s\n",
701 on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
,
702 on_color_operational
, strna(operational_state
), off_color_operational
);
704 dump_addresses(rtnl
, " Address: ", 0);
705 dump_gateways(rtnl
, hwdb
, " Gateway: ", 0);
707 sd_network_get_dns(&dns
);
708 if (!strv_isempty(dns
))
709 dump_list(" DNS: ", dns
);
711 sd_network_get_domains(&domains
);
712 if (!strv_isempty(domains
))
713 dump_list(" Domain: ", domains
);
715 sd_network_get_ntp(&ntp
);
716 if (!strv_isempty(ntp
))
717 dump_list(" NTP: ", ntp
);
722 pager_open_if_enabled();
725 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
726 _cleanup_free_ LinkInfo
*links
= NULL
;
729 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
731 return rtnl_log_create_error(r
);
733 r
= sd_netlink_message_request_dump(req
, true);
735 return rtnl_log_create_error(r
);
737 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
739 return log_error_errno(r
, "Failed to enumerate links: %m");
741 c
= decode_and_sort_links(reply
, &links
);
743 return rtnl_log_parse_error(c
);
745 for (i
= 0; i
< c
; i
++) {
749 link_status_one(rtnl
, hwdb
, links
[i
].name
);
752 STRV_FOREACH(name
, argv
+ 1) {
753 if (name
!= argv
+ 1)
756 link_status_one(rtnl
, hwdb
, *name
);
763 const char *lldp_system_capability_to_string(LLDPSystemCapabilities d
) _const_
;
764 LLDPSystemCapabilities
lldp_system_capability_from_string(const char *d
) _pure_
;
766 static const char* const lldp_system_capability_table
[_LLDP_SYSTEM_CAPABILITIES_MAX
+ 1] = {
767 [LLDP_SYSTEM_CAPABILITIES_OTHER
] = "O",
768 [LLDP_SYSTEM_CAPABILITIES_REPEATER
] = "P",
769 [LLDP_SYSTEM_CAPABILITIES_BRIDGE
] = "B",
770 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP
] = "W",
771 [LLDP_SYSTEM_CAPABILITIES_ROUTER
] = "R",
772 [LLDP_SYSTEM_CAPABILITIES_PHONE
] = "T",
773 [LLDP_SYSTEM_CAPABILITIES_DOCSIS
] = "D",
774 [LLDP_SYSTEM_CAPABILITIES_STATION
] = "A",
775 [LLDP_SYSTEM_CAPABILITIES_CVLAN
] = "C",
776 [LLDP_SYSTEM_CAPABILITIES_SVLAN
] = "S",
777 [LLDP_SYSTEM_CAPABILITIES_TPMR
] = "M",
778 [_LLDP_SYSTEM_CAPABILITIES_MAX
] = "N/A",
781 DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability
, LLDPSystemCapabilities
);
783 static char *lldp_system_caps(uint16_t cap
) {
784 _cleanup_free_
char *s
= NULL
, *t
= NULL
;
791 if (cap
& LLDP_SYSTEM_CAPABILITIES_OTHER
) {
792 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER
), " ", NULL
);
800 if (cap
& LLDP_SYSTEM_CAPABILITIES_REPEATER
) {
801 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER
), " ", NULL
);
809 if (cap
& LLDP_SYSTEM_CAPABILITIES_BRIDGE
) {
810 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE
), " ", NULL
);
818 if (cap
& LLDP_SYSTEM_CAPABILITIES_WLAN_AP
) {
819 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP
), " ", NULL
);
827 if (cap
& LLDP_SYSTEM_CAPABILITIES_ROUTER
) {
828 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER
), " ", NULL
);
836 if (cap
& LLDP_SYSTEM_CAPABILITIES_PHONE
) {
837 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE
), " ", NULL
);
845 if (cap
& LLDP_SYSTEM_CAPABILITIES_DOCSIS
) {
846 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS
), " ", NULL
);
854 if (cap
& LLDP_SYSTEM_CAPABILITIES_STATION
) {
855 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION
), " ", NULL
);
863 if (cap
& LLDP_SYSTEM_CAPABILITIES_CVLAN
) {
864 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN
), " ", NULL
);
872 if (cap
& LLDP_SYSTEM_CAPABILITIES_SVLAN
) {
873 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN
), " ", NULL
);
881 if (cap
& LLDP_SYSTEM_CAPABILITIES_TPMR
) {
882 s
= strappend(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR
));
890 s
= strappend(t
, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX
));
897 t
= strappend(s
, "]");
910 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
911 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
912 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
913 _cleanup_free_ LinkInfo
*links
= NULL
;
920 pager_open_if_enabled();
922 r
= sd_netlink_open(&rtnl
);
924 return log_error_errno(r
, "Failed to connect to netlink: %m");
926 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
928 return rtnl_log_create_error(r
);
930 r
= sd_netlink_message_request_dump(req
, true);
932 return rtnl_log_create_error(r
);
934 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
936 return log_error_errno(r
, "Failed to enumerate links: %m");
938 c
= decode_and_sort_links(reply
, &links
);
940 return rtnl_log_parse_error(c
);
943 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
945 for (i
= j
= 0; i
< c
; i
++) {
946 _cleanup_free_
char *chassis
= NULL
, *port
= NULL
, *cap
= NULL
, *lldp
= NULL
;
947 _cleanup_strv_free_
char **l
= NULL
;
949 r
= sd_network_link_get_lldp(links
[i
].ifindex
, &lldp
);
953 l
= strv_split_newlines(lldp
);
961 _cleanup_free_
char *a
= NULL
, *b
= NULL
, *word
= NULL
;
963 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
965 return log_error_errno(r
, "Failed to parse LLDP syntax \"%s\": %m", *s
);
970 r
= split_pair(word
, "=", &a
, &b
);
974 if (streq(a
, "_Chassis")) {
975 r
= free_and_strdup(&chassis
, b
);
979 } else if (streq(a
, "_Port")) {
980 r
= free_and_strdup(&port
, b
);
984 } else if (streq(a
, "_TTL")) {
985 long long unsigned x
= 0;
988 r
= safe_atollu(b
, &x
);
989 if (r
< 0 || (usec_t
) x
!= x
)
990 return log_warning_errno(r
< 0 ? r
: ERANGE
,
991 "Failed to parse TTL \"%s\": %m", b
);
993 time
= now(clock_boottime_or_monotonic());
997 ttl
= (double) (x
- time
) / USEC_PER_SEC
;
999 } else if (streq(a
, "_CAP")) {
1000 sscanf(b
, "%x", &capability
);
1002 cap
= lldp_system_caps(capability
);
1008 printf("%10s %24s %16s %16f %16s\n",
1010 strna(chassis
), strna(port
),
1018 printf("\nCapability Codes:\n"
1019 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1020 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1021 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1023 printf("Total entries displayed: %d\n", j
);
1029 static void help(void) {
1030 printf("%s [OPTIONS...]\n\n"
1031 "Query and control the networking subsystem.\n\n"
1032 " -h --help Show this help\n"
1033 " --version Show package version\n"
1034 " --no-pager Do not pipe output into a pager\n"
1035 " --no-legend Do not show the headers and footers\n"
1036 " -a --all Show status for all links\n\n"
1038 " list List links\n"
1039 " status [LINK...] Show link status\n"
1040 " lldp Show lldp information\n"
1041 , program_invocation_short_name
);
1044 static int parse_argv(int argc
, char *argv
[]) {
1047 ARG_VERSION
= 0x100,
1052 static const struct option options
[] = {
1053 { "help", no_argument
, NULL
, 'h' },
1054 { "version", no_argument
, NULL
, ARG_VERSION
},
1055 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1056 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1057 { "all", no_argument
, NULL
, 'a' },
1066 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0) {
1078 arg_no_pager
= true;
1093 assert_not_reached("Unhandled option");
1100 static int networkctl_main(int argc
, char *argv
[]) {
1101 const Verb verbs
[] = {
1102 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_links
},
1103 { "status", 1, VERB_ANY
, 0, link_status
},
1104 { "lldp", VERB_ANY
, 1, VERB_DEFAULT
, link_lldp_status
},
1108 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1111 int main(int argc
, char* argv
[]) {
1114 log_parse_environment();
1117 r
= parse_argv(argc
, argv
);
1121 r
= networkctl_main(argc
, argv
);
1126 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;