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 "string-table.h"
44 #include "string-util.h"
46 #include "terminal-util.h"
50 static bool arg_no_pager
= false;
51 static bool arg_legend
= true;
52 static bool arg_all
= false;
54 static void pager_open_if_enabled(void) {
62 static int link_get_type_string(int iftype
, sd_device
*d
, char **ret
) {
68 if (iftype
== ARPHRD_ETHER
&& d
) {
69 const char *devtype
= NULL
, *id
= NULL
;
70 /* WLANs have iftype ARPHRD_ETHER, but we want
71 * to show a more useful type string for
74 (void)sd_device_get_devtype(d
, &devtype
);
76 if (streq_ptr(devtype
, "wlan"))
78 else if (streq_ptr(devtype
, "wwan"))
91 t
= arphrd_to_name(iftype
);
107 typedef struct LinkInfo
{
113 static int link_info_compare(const void *a
, const void *b
) {
114 const LinkInfo
*x
= a
, *y
= b
;
116 return x
->ifindex
- y
->ifindex
;
119 static int decode_and_sort_links(sd_netlink_message
*m
, LinkInfo
**ret
) {
120 _cleanup_free_ LinkInfo
*links
= NULL
;
121 size_t size
= 0, c
= 0;
122 sd_netlink_message
*i
;
125 for (i
= m
; i
; i
= sd_netlink_message_next(i
)) {
131 r
= sd_netlink_message_get_type(i
, &type
);
135 if (type
!= RTM_NEWLINK
)
138 r
= sd_rtnl_message_link_get_ifindex(i
, &ifindex
);
142 r
= sd_netlink_message_read_string(i
, IFLA_IFNAME
, &name
);
146 r
= sd_rtnl_message_link_get_type(i
, &iftype
);
150 if (!GREEDY_REALLOC(links
, size
, c
+1))
153 links
[c
].name
= name
;
154 links
[c
].ifindex
= ifindex
;
155 links
[c
].iftype
= iftype
;
159 qsort_safe(links
, c
, sizeof(LinkInfo
), link_info_compare
);
167 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
171 if (streq_ptr(state
, "routable")) {
172 *on
= ansi_highlight_green();
173 *off
= ansi_normal();
174 } else if (streq_ptr(state
, "degraded")) {
175 *on
= ansi_highlight_yellow();
176 *off
= ansi_normal();
181 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
185 if (streq_ptr(state
, "configured")) {
186 *on
= ansi_highlight_green();
187 *off
= ansi_normal();
188 } else if (streq_ptr(state
, "configuring")) {
189 *on
= ansi_highlight_yellow();
190 *off
= ansi_normal();
191 } else if (streq_ptr(state
, "failed") || streq_ptr(state
, "linger")) {
192 *on
= ansi_highlight_red();
193 *off
= ansi_normal();
198 static int list_links(int argc
, char *argv
[], void *userdata
) {
199 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
200 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
201 _cleanup_free_ LinkInfo
*links
= NULL
;
204 pager_open_if_enabled();
206 r
= sd_netlink_open(&rtnl
);
208 return log_error_errno(r
, "Failed to connect to netlink: %m");
210 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
212 return rtnl_log_create_error(r
);
214 r
= sd_netlink_message_request_dump(req
, true);
216 return rtnl_log_create_error(r
);
218 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
220 return log_error_errno(r
, "Failed to enumerate links: %m");
223 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
225 c
= decode_and_sort_links(reply
, &links
);
227 return rtnl_log_parse_error(c
);
229 for (i
= 0; i
< c
; i
++) {
230 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
231 _cleanup_device_unref_ sd_device
*d
= NULL
;
232 const char *on_color_operational
, *off_color_operational
,
233 *on_color_setup
, *off_color_setup
;
234 char devid
[2 + DECIMAL_STR_MAX(int)];
235 _cleanup_free_
char *t
= NULL
;
237 sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
238 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
240 sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
241 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
243 sprintf(devid
, "n%i", links
[i
].ifindex
);
244 (void)sd_device_new_from_device_id(&d
, devid
);
246 link_get_type_string(links
[i
].iftype
, d
, &t
);
248 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
249 links
[i
].ifindex
, links
[i
].name
, strna(t
),
250 on_color_operational
, strna(operational_state
), off_color_operational
,
251 on_color_setup
, strna(setup_state
), off_color_setup
);
255 printf("\n%i links listed.\n", c
);
260 /* IEEE Organizationally Unique Identifier vendor string */
261 static int ieee_oui(sd_hwdb
*hwdb
, struct ether_addr
*mac
, char **ret
) {
262 const char *description
;
263 char modalias
[strlen("OUI:XXYYXXYYXXYY") + 1], *desc
;
274 /* skip commonly misused 00:00:00 (Xerox) prefix */
275 if (memcmp(mac
, "\0\0\0", 3) == 0)
278 snprintf(modalias
, sizeof(modalias
), "OUI:" ETHER_ADDR_FORMAT_STR
, ETHER_ADDR_FORMAT_VAL(*mac
));
280 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
284 desc
= strdup(description
);
293 static int get_gateway_description(
298 union in_addr_union
*gateway
,
299 char **gateway_description
) {
300 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
301 sd_netlink_message
*m
;
305 assert(ifindex
>= 0);
306 assert(family
== AF_INET
|| family
== AF_INET6
);
308 assert(gateway_description
);
310 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
314 r
= sd_netlink_message_request_dump(req
, true);
318 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
322 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
323 union in_addr_union gw
= {};
324 struct ether_addr mac
= {};
328 r
= sd_netlink_message_get_errno(m
);
330 log_error_errno(r
, "got error: %m");
334 r
= sd_netlink_message_get_type(m
, &type
);
336 log_error_errno(r
, "could not get type: %m");
340 if (type
!= RTM_NEWNEIGH
) {
341 log_error("type is not RTM_NEWNEIGH");
345 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
347 log_error_errno(r
, "could not get family: %m");
352 log_error("family is not correct");
356 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
358 log_error_errno(r
, "could not get ifindex: %m");
362 if (ifindex
> 0 && ifi
!= ifindex
)
367 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
373 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
382 if (!in_addr_equal(fam
, &gw
, gateway
))
385 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
389 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
399 static int dump_gateways(
404 _cleanup_free_
struct local_address
*local
= NULL
;
407 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
411 for (i
= 0; i
< n
; i
++) {
412 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
;
414 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
418 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
420 log_debug_errno(r
, "Could not get description of gateway: %m");
423 (int) strlen(prefix
),
424 i
== 0 ? prefix
: "",
428 printf(" (%s)", description
);
430 /* Show interface name for the entry if we show
431 * entries for all interfaces */
433 char name
[IF_NAMESIZE
+1];
435 if (if_indextoname(local
[i
].ifindex
, name
)) {
436 fputs(" on ", stdout
);
439 printf(" on %%%i", local
[i
].ifindex
);
448 static int dump_addresses(
453 _cleanup_free_
struct local_address
*local
= NULL
;
456 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
460 for (i
= 0; i
< n
; i
++) {
461 _cleanup_free_
char *pretty
= NULL
;
463 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
468 (int) strlen(prefix
),
469 i
== 0 ? prefix
: "",
473 char name
[IF_NAMESIZE
+1];
475 if (if_indextoname(local
[i
].ifindex
, name
)) {
476 fputs(" on ", stdout
);
479 printf(" on %%%i", local
[i
].ifindex
);
488 static void dump_list(const char *prefix
, char **l
) {
493 (int) strlen(prefix
),
494 i
== l
? prefix
: "",
499 static int link_status_one(
503 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
504 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
505 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
506 _cleanup_device_unref_ sd_device
*d
= NULL
;
507 char devid
[2 + DECIMAL_STR_MAX(int)];
508 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
509 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
510 const char *on_color_operational
, *off_color_operational
,
511 *on_color_setup
, *off_color_setup
;
512 _cleanup_strv_free_
char **carrier_bound_to
= NULL
;
513 _cleanup_strv_free_
char **carrier_bound_by
= NULL
;
523 if (parse_ifindex(name
, &ifindex
) >= 0)
524 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, ifindex
);
526 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
528 return rtnl_log_create_error(r
);
530 r
= sd_netlink_message_append_string(req
, IFLA_IFNAME
, name
);
534 return rtnl_log_create_error(r
);
536 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
538 return log_error_errno(r
, "Failed to query link: %m");
540 r
= sd_rtnl_message_link_get_ifindex(reply
, &ifindex
);
542 return rtnl_log_parse_error(r
);
544 r
= sd_netlink_message_read_string(reply
, IFLA_IFNAME
, &name
);
546 return rtnl_log_parse_error(r
);
548 r
= sd_rtnl_message_link_get_type(reply
, &iftype
);
550 return rtnl_log_parse_error(r
);
552 have_mac
= sd_netlink_message_read_ether_addr(reply
, IFLA_ADDRESS
, &e
) >= 0;
556 bool all_zeroes
= true;
558 for (p
= (uint8_t*) &e
; p
< (uint8_t*) &e
+ sizeof(e
); p
++)
568 sd_netlink_message_read_u32(reply
, IFLA_MTU
, &mtu
);
570 sd_network_link_get_operational_state(ifindex
, &operational_state
);
571 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
573 sd_network_link_get_setup_state(ifindex
, &setup_state
);
574 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
576 sd_network_link_get_dns(ifindex
, &dns
);
577 sd_network_link_get_domains(ifindex
, &domains
);
578 r
= sd_network_link_get_wildcard_domain(ifindex
);
582 wildcard
= strdup("*");
586 if (strv_consume(&domains
, wildcard
) < 0)
590 sprintf(devid
, "n%i", ifindex
);
592 (void)sd_device_new_from_device_id(&d
, devid
);
595 (void)sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
596 (void)sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
597 (void)sd_device_get_property_value(d
, "ID_PATH", &path
);
599 r
= sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
);
601 (void)sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
603 r
= sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
);
605 (void)sd_device_get_property_value(d
, "ID_MODEL", &model
);
608 link_get_type_string(iftype
, d
, &t
);
610 sd_network_link_get_network_file(ifindex
, &network
);
612 sd_network_link_get_carrier_bound_to(ifindex
, &carrier_bound_to
);
613 sd_network_link_get_carrier_bound_by(ifindex
, &carrier_bound_by
);
615 printf("%s%s%s %i: %s\n", on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
, ifindex
, name
);
617 printf(" Link File: %s\n"
618 " Network File: %s\n"
620 " State: %s%s%s (%s%s%s)\n",
624 on_color_operational
, strna(operational_state
), off_color_operational
,
625 on_color_setup
, strna(setup_state
), off_color_setup
);
628 printf(" Path: %s\n", path
);
630 printf(" Driver: %s\n", driver
);
632 printf(" Vendor: %s\n", vendor
);
634 printf(" Model: %s\n", model
);
637 _cleanup_free_
char *description
= NULL
;
638 char ea
[ETHER_ADDR_TO_STRING_MAX
];
640 ieee_oui(hwdb
, &e
, &description
);
643 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e
, ea
), description
);
645 printf(" HW Address: %s\n", ether_addr_to_string(&e
, ea
));
649 printf(" MTU: %u\n", mtu
);
651 dump_addresses(rtnl
, " Address: ", ifindex
);
652 dump_gateways(rtnl
, hwdb
, " Gateway: ", ifindex
);
654 if (!strv_isempty(dns
))
655 dump_list(" DNS: ", dns
);
656 if (!strv_isempty(domains
))
657 dump_list(" Domain: ", domains
);
659 (void) sd_network_link_get_ntp(ifindex
, &ntp
);
660 if (!strv_isempty(ntp
))
661 dump_list(" NTP: ", ntp
);
663 if (!strv_isempty(carrier_bound_to
))
664 dump_list("Carrier Bound To: ", carrier_bound_to
);
666 if (!strv_isempty(carrier_bound_by
))
667 dump_list("Carrier Bound By: ", carrier_bound_by
);
669 (void) sd_network_link_get_timezone(ifindex
, &tz
);
671 printf(" Time Zone: %s", tz
);
676 static int link_status(int argc
, char *argv
[], void *userdata
) {
677 _cleanup_hwdb_unref_ sd_hwdb
*hwdb
= NULL
;
678 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
682 r
= sd_netlink_open(&rtnl
);
684 return log_error_errno(r
, "Failed to connect to netlink: %m");
686 r
= sd_hwdb_new(&hwdb
);
688 log_debug_errno(r
, "Failed to open hardware database: %m");
690 if (argc
<= 1 && !arg_all
) {
691 _cleanup_free_
char *operational_state
= NULL
;
692 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
693 const char *on_color_operational
, *off_color_operational
;
695 sd_network_get_operational_state(&operational_state
);
696 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
698 printf("%s%s%s State: %s%s%s\n",
699 on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
,
700 on_color_operational
, strna(operational_state
), off_color_operational
);
702 dump_addresses(rtnl
, " Address: ", 0);
703 dump_gateways(rtnl
, hwdb
, " Gateway: ", 0);
705 sd_network_get_dns(&dns
);
706 if (!strv_isempty(dns
))
707 dump_list(" DNS: ", dns
);
709 sd_network_get_domains(&domains
);
710 if (!strv_isempty(domains
))
711 dump_list(" Domain: ", domains
);
713 sd_network_get_ntp(&ntp
);
714 if (!strv_isempty(ntp
))
715 dump_list(" NTP: ", ntp
);
720 pager_open_if_enabled();
723 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
724 _cleanup_free_ LinkInfo
*links
= NULL
;
727 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
729 return rtnl_log_create_error(r
);
731 r
= sd_netlink_message_request_dump(req
, true);
733 return rtnl_log_create_error(r
);
735 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
737 return log_error_errno(r
, "Failed to enumerate links: %m");
739 c
= decode_and_sort_links(reply
, &links
);
741 return rtnl_log_parse_error(c
);
743 for (i
= 0; i
< c
; i
++) {
747 link_status_one(rtnl
, hwdb
, links
[i
].name
);
750 STRV_FOREACH(name
, argv
+ 1) {
751 if (name
!= argv
+ 1)
754 link_status_one(rtnl
, hwdb
, *name
);
761 const char *lldp_system_capability_to_string(LLDPSystemCapabilities d
) _const_
;
762 LLDPSystemCapabilities
lldp_system_capability_from_string(const char *d
) _pure_
;
764 static const char* const lldp_system_capability_table
[_LLDP_SYSTEM_CAPABILITIES_MAX
+ 1] = {
765 [LLDP_SYSTEM_CAPABILITIES_OTHER
] = "O",
766 [LLDP_SYSTEM_CAPABILITIES_REPEATER
] = "P",
767 [LLDP_SYSTEM_CAPABILITIES_BRIDGE
] = "B",
768 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP
] = "W",
769 [LLDP_SYSTEM_CAPABILITIES_ROUTER
] = "R",
770 [LLDP_SYSTEM_CAPABILITIES_PHONE
] = "T",
771 [LLDP_SYSTEM_CAPABILITIES_DOCSIS
] = "D",
772 [LLDP_SYSTEM_CAPABILITIES_STATION
] = "A",
773 [LLDP_SYSTEM_CAPABILITIES_CVLAN
] = "C",
774 [LLDP_SYSTEM_CAPABILITIES_SVLAN
] = "S",
775 [LLDP_SYSTEM_CAPABILITIES_TPMR
] = "M",
776 [_LLDP_SYSTEM_CAPABILITIES_MAX
] = "N/A",
779 DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability
, LLDPSystemCapabilities
);
781 static char *lldp_system_caps(uint16_t cap
) {
782 _cleanup_free_
char *s
= NULL
, *t
= NULL
;
789 if (cap
& LLDP_SYSTEM_CAPABILITIES_OTHER
) {
790 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER
), " ", NULL
);
798 if (cap
& LLDP_SYSTEM_CAPABILITIES_REPEATER
) {
799 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER
), " ", NULL
);
807 if (cap
& LLDP_SYSTEM_CAPABILITIES_BRIDGE
) {
808 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE
), " ", NULL
);
816 if (cap
& LLDP_SYSTEM_CAPABILITIES_WLAN_AP
) {
817 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP
), " ", NULL
);
825 if (cap
& LLDP_SYSTEM_CAPABILITIES_ROUTER
) {
826 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER
), " ", NULL
);
834 if (cap
& LLDP_SYSTEM_CAPABILITIES_PHONE
) {
835 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE
), " ", NULL
);
843 if (cap
& LLDP_SYSTEM_CAPABILITIES_DOCSIS
) {
844 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS
), " ", NULL
);
852 if (cap
& LLDP_SYSTEM_CAPABILITIES_STATION
) {
853 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION
), " ", NULL
);
861 if (cap
& LLDP_SYSTEM_CAPABILITIES_CVLAN
) {
862 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN
), " ", NULL
);
870 if (cap
& LLDP_SYSTEM_CAPABILITIES_SVLAN
) {
871 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN
), " ", NULL
);
879 if (cap
& LLDP_SYSTEM_CAPABILITIES_TPMR
) {
880 s
= strappend(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR
));
888 s
= strappend(t
, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX
));
895 t
= strappend(s
, "]");
908 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
909 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
910 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
911 _cleanup_free_ LinkInfo
*links
= NULL
;
918 pager_open_if_enabled();
920 r
= sd_netlink_open(&rtnl
);
922 return log_error_errno(r
, "Failed to connect to netlink: %m");
924 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
926 return rtnl_log_create_error(r
);
928 r
= sd_netlink_message_request_dump(req
, true);
930 return rtnl_log_create_error(r
);
932 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
934 return log_error_errno(r
, "Failed to enumerate links: %m");
936 c
= decode_and_sort_links(reply
, &links
);
938 return rtnl_log_parse_error(c
);
941 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
943 for (i
= j
= 0; i
< c
; i
++) {
944 _cleanup_free_
char *chassis
= NULL
, *port
= NULL
, *cap
= NULL
, *lldp
= NULL
;
945 _cleanup_strv_free_
char **l
= NULL
;
947 r
= sd_network_link_get_lldp(links
[i
].ifindex
, &lldp
);
951 l
= strv_split_newlines(lldp
);
959 _cleanup_free_
char *a
= NULL
, *b
= NULL
, *word
= NULL
;
961 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
963 return log_error_errno(r
, "Failed to parse LLDP syntax \"%s\": %m", *s
);
968 r
= split_pair(word
, "=", &a
, &b
);
972 if (streq(a
, "_Chassis")) {
973 r
= free_and_strdup(&chassis
, b
);
977 } else if (streq(a
, "_Port")) {
978 r
= free_and_strdup(&port
, b
);
982 } else if (streq(a
, "_TTL")) {
983 long long unsigned x
= 0;
986 r
= safe_atollu(b
, &x
);
987 if (r
< 0 || (usec_t
) x
!= x
)
988 return log_warning_errno(r
< 0 ? r
: ERANGE
,
989 "Failed to parse TTL \"%s\": %m", b
);
991 time
= now(clock_boottime_or_monotonic());
995 ttl
= (double) (x
- time
) / USEC_PER_SEC
;
997 } else if (streq(a
, "_CAP")) {
998 sscanf(b
, "%x", &capability
);
1000 cap
= lldp_system_caps(capability
);
1006 printf("%10s %24s %16s %16f %16s\n",
1008 strna(chassis
), strna(port
),
1016 printf("\nCapability Codes:\n"
1017 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1018 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1019 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1021 printf("Total entries displayed: %d\n", j
);
1027 static void help(void) {
1028 printf("%s [OPTIONS...]\n\n"
1029 "Query and control the networking subsystem.\n\n"
1030 " -h --help Show this help\n"
1031 " --version Show package version\n"
1032 " --no-pager Do not pipe output into a pager\n"
1033 " --no-legend Do not show the headers and footers\n"
1034 " -a --all Show status for all links\n\n"
1036 " list List links\n"
1037 " status [LINK...] Show link status\n"
1038 " lldp Show lldp information\n"
1039 , program_invocation_short_name
);
1042 static int parse_argv(int argc
, char *argv
[]) {
1045 ARG_VERSION
= 0x100,
1050 static const struct option options
[] = {
1051 { "help", no_argument
, NULL
, 'h' },
1052 { "version", no_argument
, NULL
, ARG_VERSION
},
1053 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1054 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1055 { "all", no_argument
, NULL
, 'a' },
1064 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0) {
1076 arg_no_pager
= true;
1091 assert_not_reached("Unhandled option");
1098 static int networkctl_main(int argc
, char *argv
[]) {
1099 const Verb verbs
[] = {
1100 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_links
},
1101 { "status", 1, VERB_ANY
, 0, link_status
},
1102 { "lldp", VERB_ANY
, 1, VERB_DEFAULT
, link_lldp_status
},
1106 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1109 int main(int argc
, char* argv
[]) {
1112 log_parse_environment();
1115 r
= parse_argv(argc
, argv
);
1119 r
= networkctl_main(argc
, argv
);
1124 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;