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-network.h"
27 #include "sd-netlink.h"
29 #include "sd-device.h"
36 #include "netlink-util.h"
37 #include "device-util.h"
38 #include "hwdb-util.h"
39 #include "arphrd-list.h"
40 #include "local-addresses.h"
41 #include "socket-util.h"
42 #include "ether-addr-util.h"
44 #include "terminal-util.h"
46 static bool arg_no_pager
= false;
47 static bool arg_legend
= true;
48 static bool arg_all
= false;
50 static void pager_open_if_enabled(void) {
58 static int link_get_type_string(int iftype
, sd_device
*d
, char **ret
) {
64 if (iftype
== ARPHRD_ETHER
&& d
) {
65 const char *devtype
= NULL
, *id
= NULL
;
66 /* WLANs have iftype ARPHRD_ETHER, but we want
67 * to show a more useful type string for
70 (void)sd_device_get_devtype(d
, &devtype
);
72 if (streq_ptr(devtype
, "wlan"))
74 else if (streq_ptr(devtype
, "wwan"))
87 t
= arphrd_to_name(iftype
);
103 typedef struct LinkInfo
{
109 static int link_info_compare(const void *a
, const void *b
) {
110 const LinkInfo
*x
= a
, *y
= b
;
112 return x
->ifindex
- y
->ifindex
;
115 static int decode_and_sort_links(sd_netlink_message
*m
, LinkInfo
**ret
) {
116 _cleanup_free_ LinkInfo
*links
= NULL
;
117 size_t size
= 0, c
= 0;
118 sd_netlink_message
*i
;
121 for (i
= m
; i
; i
= sd_netlink_message_next(i
)) {
127 r
= sd_netlink_message_get_type(i
, &type
);
131 if (type
!= RTM_NEWLINK
)
134 r
= sd_rtnl_message_link_get_ifindex(i
, &ifindex
);
138 r
= sd_netlink_message_read_string(i
, IFLA_IFNAME
, &name
);
142 r
= sd_rtnl_message_link_get_type(i
, &iftype
);
146 if (!GREEDY_REALLOC(links
, size
, c
+1))
149 links
[c
].name
= name
;
150 links
[c
].ifindex
= ifindex
;
151 links
[c
].iftype
= iftype
;
155 qsort_safe(links
, c
, sizeof(LinkInfo
), link_info_compare
);
163 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
167 if (streq_ptr(state
, "routable")) {
168 *on
= ansi_highlight_green();
169 *off
= ansi_highlight_off();
170 } else if (streq_ptr(state
, "degraded")) {
171 *on
= ansi_highlight_yellow();
172 *off
= ansi_highlight_off();
177 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
181 if (streq_ptr(state
, "configured")) {
182 *on
= ansi_highlight_green();
183 *off
= ansi_highlight_off();
184 } else if (streq_ptr(state
, "configuring")) {
185 *on
= ansi_highlight_yellow();
186 *off
= ansi_highlight_off();
187 } else if (streq_ptr(state
, "failed") || streq_ptr(state
, "linger")) {
188 *on
= ansi_highlight_red();
189 *off
= ansi_highlight_off();
194 static int list_links(int argc
, char *argv
[], void *userdata
) {
195 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
196 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
197 _cleanup_free_ LinkInfo
*links
= NULL
;
200 pager_open_if_enabled();
202 r
= sd_netlink_open(&rtnl
);
204 return log_error_errno(r
, "Failed to connect to netlink: %m");
206 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
208 return rtnl_log_create_error(r
);
210 r
= sd_netlink_message_request_dump(req
, true);
212 return rtnl_log_create_error(r
);
214 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
216 return log_error_errno(r
, "Failed to enumerate links: %m");
219 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
221 c
= decode_and_sort_links(reply
, &links
);
223 return rtnl_log_parse_error(c
);
225 for (i
= 0; i
< c
; i
++) {
226 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
227 _cleanup_device_unref_ sd_device
*d
= NULL
;
228 const char *on_color_operational
, *off_color_operational
,
229 *on_color_setup
, *off_color_setup
;
230 char devid
[2 + DECIMAL_STR_MAX(int)];
231 _cleanup_free_
char *t
= NULL
;
233 sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
234 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
236 sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
237 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
239 sprintf(devid
, "n%i", links
[i
].ifindex
);
240 (void)sd_device_new_from_device_id(&d
, devid
);
242 link_get_type_string(links
[i
].iftype
, d
, &t
);
244 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
245 links
[i
].ifindex
, links
[i
].name
, strna(t
),
246 on_color_operational
, strna(operational_state
), off_color_operational
,
247 on_color_setup
, strna(setup_state
), off_color_setup
);
251 printf("\n%i links listed.\n", c
);
256 /* IEEE Organizationally Unique Identifier vendor string */
257 static int ieee_oui(sd_hwdb
*hwdb
, struct ether_addr
*mac
, char **ret
) {
258 const char *description
;
259 char modalias
[strlen("OUI:XXYYXXYYXXYY") + 1], *desc
;
270 /* skip commonly misused 00:00:00 (Xerox) prefix */
271 if (memcmp(mac
, "\0\0\0", 3) == 0)
274 snprintf(modalias
, sizeof(modalias
), "OUI:" ETHER_ADDR_FORMAT_STR
, ETHER_ADDR_FORMAT_VAL(*mac
));
276 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
280 desc
= strdup(description
);
289 static int get_gateway_description(
294 union in_addr_union
*gateway
,
295 char **gateway_description
) {
296 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
297 sd_netlink_message
*m
;
301 assert(ifindex
>= 0);
302 assert(family
== AF_INET
|| family
== AF_INET6
);
304 assert(gateway_description
);
306 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
310 r
= sd_netlink_message_request_dump(req
, true);
314 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
318 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
319 union in_addr_union gw
= {};
320 struct ether_addr mac
= {};
324 r
= sd_netlink_message_get_errno(m
);
326 log_error_errno(r
, "got error: %m");
330 r
= sd_netlink_message_get_type(m
, &type
);
332 log_error_errno(r
, "could not get type: %m");
336 if (type
!= RTM_NEWNEIGH
) {
337 log_error("type is not RTM_NEWNEIGH");
341 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
343 log_error_errno(r
, "could not get family: %m");
348 log_error("family is not correct");
352 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
354 log_error_errno(r
, "could not get ifindex: %m");
358 if (ifindex
> 0 && ifi
!= ifindex
)
363 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
369 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
378 if (!in_addr_equal(fam
, &gw
, gateway
))
381 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
385 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
395 static int dump_gateways(
400 _cleanup_free_
struct local_address
*local
= NULL
;
403 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
407 for (i
= 0; i
< n
; i
++) {
408 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
;
410 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
414 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
416 log_debug_errno(r
, "Could not get description of gateway: %m");
419 (int) strlen(prefix
),
420 i
== 0 ? prefix
: "",
424 printf(" (%s)", description
);
426 /* Show interface name for the entry if we show
427 * entries for all interfaces */
429 char name
[IF_NAMESIZE
+1];
431 if (if_indextoname(local
[i
].ifindex
, name
)) {
432 fputs(" on ", stdout
);
435 printf(" on %%%i", local
[i
].ifindex
);
444 static int dump_addresses(
449 _cleanup_free_
struct local_address
*local
= NULL
;
452 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
456 for (i
= 0; i
< n
; i
++) {
457 _cleanup_free_
char *pretty
= NULL
;
459 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
464 (int) strlen(prefix
),
465 i
== 0 ? prefix
: "",
469 char name
[IF_NAMESIZE
+1];
471 if (if_indextoname(local
[i
].ifindex
, name
)) {
472 fputs(" on ", stdout
);
475 printf(" on %%%i", local
[i
].ifindex
);
484 static void dump_list(const char *prefix
, char **l
) {
489 (int) strlen(prefix
),
490 i
== l
? prefix
: "",
495 static int link_status_one(
499 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
500 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
501 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
502 _cleanup_device_unref_ sd_device
*d
= NULL
;
503 char devid
[2 + DECIMAL_STR_MAX(int)];
504 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
505 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
506 const char *on_color_operational
, *off_color_operational
,
507 *on_color_setup
, *off_color_setup
;
508 _cleanup_strv_free_
char **carrier_bound_to
= NULL
;
509 _cleanup_strv_free_
char **carrier_bound_by
= NULL
;
519 if (safe_atoi(name
, &ifindex
) >= 0 && ifindex
> 0)
520 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, ifindex
);
522 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
524 return rtnl_log_create_error(r
);
526 r
= sd_netlink_message_append_string(req
, IFLA_IFNAME
, name
);
530 return rtnl_log_create_error(r
);
532 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
534 return log_error_errno(r
, "Failed to query link: %m");
536 r
= sd_rtnl_message_link_get_ifindex(reply
, &ifindex
);
538 return rtnl_log_parse_error(r
);
540 r
= sd_netlink_message_read_string(reply
, IFLA_IFNAME
, &name
);
542 return rtnl_log_parse_error(r
);
544 r
= sd_rtnl_message_link_get_type(reply
, &iftype
);
546 return rtnl_log_parse_error(r
);
548 have_mac
= sd_netlink_message_read_ether_addr(reply
, IFLA_ADDRESS
, &e
) >= 0;
552 bool all_zeroes
= true;
554 for (p
= (uint8_t*) &e
; p
< (uint8_t*) &e
+ sizeof(e
); p
++)
564 sd_netlink_message_read_u32(reply
, IFLA_MTU
, &mtu
);
566 sd_network_link_get_operational_state(ifindex
, &operational_state
);
567 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
569 sd_network_link_get_setup_state(ifindex
, &setup_state
);
570 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
572 sd_network_link_get_dns(ifindex
, &dns
);
573 sd_network_link_get_ntp(ifindex
, &ntp
);
574 sd_network_link_get_domains(ifindex
, &domains
);
575 r
= sd_network_link_get_wildcard_domain(ifindex
);
579 wildcard
= strdup("*");
583 if (strv_consume(&domains
, wildcard
) < 0)
587 sprintf(devid
, "n%i", ifindex
);
589 (void)sd_device_new_from_device_id(&d
, devid
);
592 (void)sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
593 (void)sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
594 (void)sd_device_get_property_value(d
, "ID_PATH", &path
);
596 r
= sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
);
598 (void)sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
600 r
= sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
);
602 (void)sd_device_get_property_value(d
, "ID_MODEL", &model
);
605 link_get_type_string(iftype
, d
, &t
);
607 sd_network_link_get_network_file(ifindex
, &network
);
609 sd_network_link_get_carrier_bound_to(ifindex
, &carrier_bound_to
);
610 sd_network_link_get_carrier_bound_by(ifindex
, &carrier_bound_by
);
612 printf("%s%s%s %i: %s\n", on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
, ifindex
, name
);
614 printf(" Link File: %s\n"
615 " Network File: %s\n"
617 " State: %s%s%s (%s%s%s)\n",
621 on_color_operational
, strna(operational_state
), off_color_operational
,
622 on_color_setup
, strna(setup_state
), off_color_setup
);
625 printf(" Path: %s\n", path
);
627 printf(" Driver: %s\n", driver
);
629 printf(" Vendor: %s\n", vendor
);
631 printf(" Model: %s\n", model
);
634 _cleanup_free_
char *description
= NULL
;
635 char ea
[ETHER_ADDR_TO_STRING_MAX
];
637 ieee_oui(hwdb
, &e
, &description
);
640 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e
, ea
), description
);
642 printf(" HW Address: %s\n", ether_addr_to_string(&e
, ea
));
646 printf(" MTU: %u\n", mtu
);
648 dump_addresses(rtnl
, " Address: ", ifindex
);
649 dump_gateways(rtnl
, hwdb
, " Gateway: ", ifindex
);
651 if (!strv_isempty(dns
))
652 dump_list(" DNS: ", dns
);
653 if (!strv_isempty(domains
))
654 dump_list(" Domain: ", domains
);
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
);
667 static int link_status(int argc
, char *argv
[], void *userdata
) {
668 _cleanup_hwdb_unref_ sd_hwdb
*hwdb
= NULL
;
669 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
673 r
= sd_netlink_open(&rtnl
);
675 return log_error_errno(r
, "Failed to connect to netlink: %m");
677 r
= sd_hwdb_new(&hwdb
);
679 log_debug_errno(r
, "Failed to open hardware database: %m");
681 if (argc
<= 1 && !arg_all
) {
682 _cleanup_free_
char *operational_state
= NULL
;
683 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **domains
= NULL
;
684 const char *on_color_operational
, *off_color_operational
;
686 sd_network_get_operational_state(&operational_state
);
687 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
689 printf("%s%s%s State: %s%s%s\n",
690 on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
,
691 on_color_operational
, strna(operational_state
), off_color_operational
);
693 dump_addresses(rtnl
, " Address: ", 0);
694 dump_gateways(rtnl
, hwdb
, " Gateway: ", 0);
696 sd_network_get_dns(&dns
);
697 if (!strv_isempty(dns
))
698 dump_list(" DNS: ", dns
);
700 sd_network_get_domains(&domains
);
701 if (!strv_isempty(domains
))
702 dump_list(" Domain: ", domains
);
704 sd_network_get_ntp(&ntp
);
705 if (!strv_isempty(ntp
))
706 dump_list(" NTP: ", ntp
);
711 pager_open_if_enabled();
714 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
715 _cleanup_free_ LinkInfo
*links
= NULL
;
718 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
720 return rtnl_log_create_error(r
);
722 r
= sd_netlink_message_request_dump(req
, true);
724 return rtnl_log_create_error(r
);
726 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
728 return log_error_errno(r
, "Failed to enumerate links: %m");
730 c
= decode_and_sort_links(reply
, &links
);
732 return rtnl_log_parse_error(c
);
734 for (i
= 0; i
< c
; i
++) {
738 link_status_one(rtnl
, hwdb
, links
[i
].name
);
741 STRV_FOREACH(name
, argv
+ 1) {
742 if (name
!= argv
+ 1)
745 link_status_one(rtnl
, hwdb
, *name
);
752 const char *lldp_system_capability_to_string(LLDPSystemCapabilities d
) _const_
;
753 LLDPSystemCapabilities
lldp_system_capability_from_string(const char *d
) _pure_
;
755 static const char* const lldp_system_capability_table
[_LLDP_SYSTEM_CAPABILITIES_MAX
+ 1] = {
756 [LLDP_SYSTEM_CAPABILITIES_OTHER
] = "O",
757 [LLDP_SYSTEM_CAPABILITIES_REPEATER
] = "P",
758 [LLDP_SYSTEM_CAPABILITIES_BRIDGE
] = "B",
759 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP
] = "W",
760 [LLDP_SYSTEM_CAPABILITIES_ROUTER
] = "R",
761 [LLDP_SYSTEM_CAPABILITIES_PHONE
] = "T",
762 [LLDP_SYSTEM_CAPABILITIES_DOCSIS
] = "D",
763 [LLDP_SYSTEM_CAPABILITIES_STATION
] = "A",
764 [LLDP_SYSTEM_CAPABILITIES_CVLAN
] = "C",
765 [LLDP_SYSTEM_CAPABILITIES_SVLAN
] = "S",
766 [LLDP_SYSTEM_CAPABILITIES_TPMR
] = "M",
767 [_LLDP_SYSTEM_CAPABILITIES_MAX
] = "N/A",
770 DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability
, LLDPSystemCapabilities
);
772 static char *lldp_system_caps(uint16_t cap
) {
773 _cleanup_free_
char *s
= NULL
, *t
= NULL
;
780 if (cap
& LLDP_SYSTEM_CAPABILITIES_OTHER
) {
781 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER
), " ", NULL
);
789 if (cap
& LLDP_SYSTEM_CAPABILITIES_REPEATER
) {
790 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER
), " ", NULL
);
798 if (cap
& LLDP_SYSTEM_CAPABILITIES_BRIDGE
) {
799 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE
), " ", NULL
);
807 if (cap
& LLDP_SYSTEM_CAPABILITIES_WLAN_AP
) {
808 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP
), " ", NULL
);
816 if (cap
& LLDP_SYSTEM_CAPABILITIES_ROUTER
) {
817 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER
), " ", NULL
);
825 if (cap
& LLDP_SYSTEM_CAPABILITIES_PHONE
) {
826 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE
), " ", NULL
);
834 if (cap
& LLDP_SYSTEM_CAPABILITIES_DOCSIS
) {
835 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS
), " ", NULL
);
843 if (cap
& LLDP_SYSTEM_CAPABILITIES_STATION
) {
844 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION
), " ", NULL
);
852 if (cap
& LLDP_SYSTEM_CAPABILITIES_CVLAN
) {
853 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN
), " ", NULL
);
861 if (cap
& LLDP_SYSTEM_CAPABILITIES_SVLAN
) {
862 s
= strjoin(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN
), " ", NULL
);
870 if (cap
& LLDP_SYSTEM_CAPABILITIES_TPMR
) {
871 s
= strappend(t
, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR
));
879 s
= strappend(t
, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX
));
886 t
= strappend(s
, "]");
899 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
900 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
, *reply
= NULL
;
901 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
902 _cleanup_free_ LinkInfo
*links
= NULL
;
903 const char *state
, *word
;
911 pager_open_if_enabled();
913 r
= sd_netlink_open(&rtnl
);
915 return log_error_errno(r
, "Failed to connect to netlink: %m");
917 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
919 return rtnl_log_create_error(r
);
921 r
= sd_netlink_message_request_dump(req
, true);
923 return rtnl_log_create_error(r
);
925 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
927 return log_error_errno(r
, "Failed to enumerate links: %m");
929 c
= decode_and_sort_links(reply
, &links
);
931 return rtnl_log_parse_error(c
);
934 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
936 for (i
= j
= 0; i
< c
; i
++) {
937 _cleanup_free_
char *chassis
= NULL
, *port
= NULL
, *cap
= NULL
, *lldp
= NULL
;
938 _cleanup_strv_free_
char **l
= NULL
;
940 r
= sd_network_link_get_lldp(links
[i
].ifindex
, &lldp
);
944 l
= strv_split_newlines(lldp
);
949 FOREACH_WORD_QUOTED(word
, ll
, *s
, state
) {
950 _cleanup_free_
char *t
= NULL
, *a
= NULL
, *b
= NULL
;
952 t
= strndup(word
, ll
);
956 r
= split_pair(t
, "=", &a
, &b
);
960 if (streq(a
, "_Chassis")) {
961 r
= free_and_strdup(&chassis
, b
);
965 } else if (streq(a
, "_Port")) {
966 r
= free_and_strdup(&port
, b
);
970 } else if (streq(a
, "_TTL")) {
971 long long unsigned x
= 0;
974 r
= safe_atollu(b
, &x
);
975 if (r
< 0 || (usec_t
) x
!= x
)
976 return log_warning_errno(r
< 0 ? r
: ERANGE
,
977 "Failed to parse TTL \"%s\": %m", b
);
979 time
= now(CLOCK_BOOTTIME
);
983 ttl
= (double) (x
- time
) / USEC_PER_SEC
;
985 } else if (streq(a
, "_CAP")) {
986 sscanf(b
, "%x", &capability
);
988 cap
= lldp_system_caps(capability
);
994 printf("%10s %24s %16s %16f %16s\n",
996 strna(chassis
), strna(port
),
1004 printf("\nCapability Codes:\n"
1005 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1006 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1007 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1009 printf("Total entries displayed: %d\n", j
);
1015 static void help(void) {
1016 printf("%s [OPTIONS...]\n\n"
1017 "Query and control the networking subsystem.\n\n"
1018 " -h --help Show this help\n"
1019 " --version Show package version\n"
1020 " --no-pager Do not pipe output into a pager\n"
1021 " --no-legend Do not show the headers and footers\n"
1022 " -a --all Show status for all links\n\n"
1024 " list List links\n"
1025 " status [LINK...] Show link status\n"
1026 " lldp Show lldp information\n"
1027 , program_invocation_short_name
);
1030 static int parse_argv(int argc
, char *argv
[]) {
1033 ARG_VERSION
= 0x100,
1038 static const struct option options
[] = {
1039 { "help", no_argument
, NULL
, 'h' },
1040 { "version", no_argument
, NULL
, ARG_VERSION
},
1041 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1042 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
1043 { "all", no_argument
, NULL
, 'a' },
1052 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0) {
1061 puts(PACKAGE_STRING
);
1062 puts(SYSTEMD_FEATURES
);
1066 arg_no_pager
= true;
1081 assert_not_reached("Unhandled option");
1088 static int networkctl_main(int argc
, char *argv
[]) {
1089 const Verb verbs
[] = {
1090 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_links
},
1091 { "status", 1, VERB_ANY
, 0, link_status
},
1092 { "lldp", VERB_ANY
, 1, VERB_DEFAULT
, link_lldp_status
},
1096 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1099 int main(int argc
, char* argv
[]) {
1102 log_parse_environment();
1105 r
= parse_argv(argc
, argv
);
1109 r
= networkctl_main(argc
, argv
);
1114 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;