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/>.
25 #include "sd-network.h"
32 #include "rtnl-util.h"
33 #include "udev-util.h"
34 #include "arphrd-list.h"
35 #include "local-addresses.h"
36 #include "socket-util.h"
37 #include "ether-addr-util.h"
39 static bool arg_no_pager = false;
40 static bool arg_legend = true;
41 static bool arg_all = false;
43 static void pager_open_if_enabled(void) {
51 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
55 if (iftype == ARPHRD_ETHER && d) {
56 const char *devtype, *id = NULL;
57 /* WLANs have iftype ARPHRD_ETHER, but we want
58 * to show a more useful type string for
61 devtype = udev_device_get_devtype(d);
62 if (streq_ptr(devtype, "wlan"))
64 else if (streq_ptr(devtype, "wwan"))
77 t = arphrd_to_name(iftype);
93 typedef struct LinkInfo {
99 static int link_info_compare(const void *a, const void *b) {
100 const LinkInfo *x = a, *y = b;
102 return x->ifindex - y->ifindex;
105 static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
106 _cleanup_free_ LinkInfo *links = NULL;
107 size_t size = 0, c = 0;
111 for (i = m; i; i = sd_rtnl_message_next(i)) {
117 r = sd_rtnl_message_get_type(i, &type);
121 if (type != RTM_NEWLINK)
124 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
128 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
132 r = sd_rtnl_message_link_get_type(i, &iftype);
136 if (!GREEDY_REALLOC(links, size, c+1))
139 links[c].name = name;
140 links[c].ifindex = ifindex;
141 links[c].iftype = iftype;
145 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
153 static void operational_state_to_color(const char *state, const char **on, const char **off) {
157 if (streq_ptr(state, "routable")) {
158 *on = ansi_highlight_green();
159 *off = ansi_highlight_off();
160 } else if (streq_ptr(state, "degraded")) {
161 *on = ansi_highlight_yellow();
162 *off = ansi_highlight_off();
167 static void setup_state_to_color(const char *state, const char **on, const char **off) {
171 if (streq_ptr(state, "configured")) {
172 *on = ansi_highlight_green();
173 *off = ansi_highlight_off();
174 } else if (streq_ptr(state, "configuring")) {
175 *on = ansi_highlight_yellow();
176 *off = ansi_highlight_off();
177 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
178 *on = ansi_highlight_red();
179 *off = ansi_highlight_off();
184 static int list_links(char **args, unsigned n) {
185 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
186 _cleanup_udev_unref_ struct udev *udev = NULL;
187 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
188 _cleanup_free_ LinkInfo *links = NULL;
191 pager_open_if_enabled();
193 r = sd_rtnl_open(&rtnl, 0);
195 return log_error_errno(r, "Failed to connect to netlink: %m");
199 return log_error_errno(errno, "Failed to connect to udev: %m");
201 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
203 return rtnl_log_create_error(r);
205 r = sd_rtnl_message_request_dump(req, true);
207 return rtnl_log_create_error(r);
209 r = sd_rtnl_call(rtnl, req, 0, &reply);
211 return log_error_errno(r, "Failed to enumerate links: %m");
214 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
216 c = decode_and_sort_links(reply, &links);
218 return rtnl_log_parse_error(c);
220 for (i = 0; i < c; i++) {
221 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
222 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
223 const char *on_color_operational, *off_color_operational,
224 *on_color_setup, *off_color_setup;
225 char devid[2 + DECIMAL_STR_MAX(int)];
226 _cleanup_free_ char *t = NULL;
228 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
229 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
231 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
232 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
234 sprintf(devid, "n%i", links[i].ifindex);
235 d = udev_device_new_from_device_id(udev, devid);
237 link_get_type_string(links[i].iftype, d, &t);
239 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
240 links[i].ifindex, links[i].name, strna(t),
241 on_color_operational, strna(operational_state), off_color_operational,
242 on_color_setup, strna(setup_state), off_color_setup);
246 printf("\n%i links listed.\n", c);
251 /* IEEE Organizationally Unique Identifier vendor string */
252 static int ieee_oui(struct udev_hwdb *hwdb, struct ether_addr *mac, char **ret) {
253 struct udev_list_entry *entry;
255 char str[strlen("OUI:XXYYXXYYXXYY") + 1];
260 /* skip commonly misused 00:00:00 (Xerox) prefix */
261 if (memcmp(mac, "\0\0\0", 3) == 0)
264 snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
266 udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, str, 0))
267 if (strcmp(udev_list_entry_get_name(entry), "ID_OUI_FROM_DATABASE") == 0) {
268 description = strdup(udev_list_entry_get_value(entry));
279 static int get_gateway_description(sd_rtnl *rtnl, struct udev_hwdb *hwdb, int ifindex, int family,
280 union in_addr_union *gateway, char **gateway_description) {
281 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
286 assert(ifindex >= 0);
287 assert(family == AF_INET || family == AF_INET6);
289 assert(gateway_description);
291 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
295 r = sd_rtnl_message_request_dump(req, true);
299 r = sd_rtnl_call(rtnl, req, 0, &reply);
303 for (m = reply; m; m = sd_rtnl_message_next(m)) {
304 union in_addr_union gw = {};
305 struct ether_addr mac = {};
309 r = sd_rtnl_message_get_errno(m);
311 log_error_errno(r, "got error: %m");
315 r = sd_rtnl_message_get_type(m, &type);
317 log_error_errno(r, "could not get type: %m");
321 if (type != RTM_NEWNEIGH) {
322 log_error("type is not RTM_NEWNEIGH");
326 r = sd_rtnl_message_neigh_get_family(m, &fam);
328 log_error_errno(r, "could not get family: %m");
333 log_error("family is not correct");
337 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
339 log_error_errno(r, "could not get ifindex: %m");
343 if (ifindex > 0 && ifi != ifindex)
348 r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
354 r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
363 if (!in_addr_equal(fam, &gw, gateway))
366 r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
370 r = ieee_oui(hwdb, &mac, gateway_description);
380 static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
381 _cleanup_free_ struct local_address *local = NULL;
384 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
388 for (i = 0; i < n; i++) {
389 _cleanup_free_ char *gateway = NULL, *description = NULL;
391 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
395 r = get_gateway_description(rtnl, hwdb, ifindex, local[i].family, &local[i].address, &description);
397 log_debug_errno(r, "Could not get description of gateway: %m");
400 printf("%*s%s (%s)\n",
401 (int) strlen(prefix),
402 i == 0 ? prefix : "",
403 gateway, description);
406 (int) strlen(prefix),
407 i == 0 ? prefix : "",
414 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
415 _cleanup_free_ struct local_address *local = NULL;
418 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
422 for (i = 0; i < n; i++) {
423 _cleanup_free_ char *pretty = NULL;
425 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
430 (int) strlen(prefix),
431 i == 0 ? prefix : "",
438 static void dump_list(const char *prefix, char **l) {
443 (int) strlen(prefix),
444 i == l ? prefix : "",
449 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
450 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
451 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
452 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
453 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
454 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
455 char devid[2 + DECIMAL_STR_MAX(int)];
456 _cleanup_free_ char *t = NULL, *network = NULL;
457 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
458 const char *on_color_operational, *off_color_operational,
459 *on_color_setup, *off_color_setup;
470 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
471 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
473 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
475 return rtnl_log_create_error(r);
477 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
481 return rtnl_log_create_error(r);
483 r = sd_rtnl_call(rtnl, req, 0, &reply);
485 return log_error_errno(r, "Failed to query link: %m");
487 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
489 return rtnl_log_parse_error(r);
491 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
493 return rtnl_log_parse_error(r);
495 r = sd_rtnl_message_link_get_type(reply, &iftype);
497 return rtnl_log_parse_error(r);
499 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
503 bool all_zeroes = true;
505 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
515 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
517 sd_network_link_get_operational_state(ifindex, &operational_state);
518 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
520 sd_network_link_get_setup_state(ifindex, &setup_state);
521 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
523 sd_network_link_get_dns(ifindex, &dns);
524 sd_network_link_get_ntp(ifindex, &ntp);
525 sd_network_link_get_domains(ifindex, &domains);
526 r = sd_network_link_get_wildcard_domain(ifindex);
530 wildcard = strdup("*");
534 if (strv_consume(&domains, wildcard) < 0)
538 sprintf(devid, "n%i", ifindex);
539 d = udev_device_new_from_device_id(udev, devid);
541 link_get_type_string(iftype, d, &t);
544 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
545 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
546 path = udev_device_get_property_value(d, "ID_PATH");
548 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
550 vendor = udev_device_get_property_value(d, "ID_VENDOR");
552 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
554 model = udev_device_get_property_value(d, "ID_MODEL");
557 sd_network_link_get_network_file(ifindex, &network);
559 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
561 printf(" Link File: %s\n"
564 " State: %s%s%s (%s%s%s)\n",
568 on_color_operational, strna(operational_state), off_color_operational,
569 on_color_setup, strna(setup_state), off_color_setup);
572 printf(" Path: %s\n", path);
574 printf(" Driver: %s\n", driver);
576 printf(" Vendor: %s\n", vendor);
578 printf(" Model: %s\n", model);
580 hwdb = udev_hwdb_new(udev);
583 _cleanup_free_ char *description = NULL;
584 char ea[ETHER_ADDR_TO_STRING_MAX];
586 ieee_oui(hwdb, &e, &description);
589 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
591 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
595 printf(" MTU: %u\n", mtu);
597 dump_addresses(rtnl, " Address: ", ifindex);
598 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
600 if (!strv_isempty(dns))
601 dump_list(" DNS: ", dns);
602 if (!strv_isempty(domains))
603 dump_list(" Domain: ", domains);
604 if (!strv_isempty(ntp))
605 dump_list(" NTP: ", ntp);
610 static int link_status(char **args, unsigned n) {
611 _cleanup_udev_unref_ struct udev *udev = NULL;
612 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
616 r = sd_rtnl_open(&rtnl, 0);
618 return log_error_errno(r, "Failed to connect to netlink: %m");
622 return log_error_errno(errno, "Failed to connect to udev: %m");
624 if (n <= 1 && !arg_all) {
625 _cleanup_free_ char *operational_state = NULL;
626 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
627 _cleanup_free_ struct local_address *addresses = NULL;
628 const char *on_color_operational, *off_color_operational;
631 sd_network_get_operational_state(&operational_state);
632 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
634 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
636 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
637 for (i = 0; i < c; i++) {
638 _cleanup_free_ char *pretty = NULL;
640 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
645 i > 0 ? "" : "Address:", pretty);
648 sd_network_get_dns(&dns);
649 if (!strv_isempty(dns))
650 dump_list(" DNS: ", dns);
652 sd_network_get_domains(&domains);
653 if (!strv_isempty(domains))
654 dump_list(" Domain: ", domains);
656 sd_network_get_ntp(&ntp);
657 if (!strv_isempty(ntp))
658 dump_list(" NTP: ", ntp);
663 pager_open_if_enabled();
666 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
667 _cleanup_free_ LinkInfo *links = NULL;
670 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
672 return rtnl_log_create_error(r);
674 r = sd_rtnl_message_request_dump(req, true);
676 return rtnl_log_create_error(r);
678 r = sd_rtnl_call(rtnl, req, 0, &reply);
680 return log_error_errno(r, "Failed to enumerate links: %m");
682 c = decode_and_sort_links(reply, &links);
684 return rtnl_log_parse_error(c);
686 for (i = 0; i < c; i++) {
690 link_status_one(rtnl, udev, links[i].name);
694 STRV_FOREACH(name, args + 1) {
698 link_status_one(rtnl, udev, *name);
704 static void help(void) {
705 printf("%s [OPTIONS...]\n\n"
706 "Query and control the networking subsystem.\n\n"
707 " -h --help Show this help\n"
708 " --version Show package version\n"
709 " --no-pager Do not pipe output into a pager\n"
710 " --no-legend Do not show the headers and footers\n"
711 " -a --all Show status for all links\n\n"
714 " status LINK Show link status\n"
715 , program_invocation_short_name);
718 static int parse_argv(int argc, char *argv[]) {
726 static const struct option options[] = {
727 { "help", no_argument, NULL, 'h' },
728 { "version", no_argument, NULL, ARG_VERSION },
729 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
730 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
731 { "all", no_argument, NULL, 'a' },
740 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
749 puts(PACKAGE_STRING);
750 puts(SYSTEMD_FEATURES);
769 assert_not_reached("Unhandled option");
776 static int networkctl_main(int argc, char *argv[]) {
778 static const struct {
786 int (* const dispatch)(char **args, unsigned n);
788 { "list", LESS, 1, list_links },
789 { "status", MORE, 1, link_status },
798 left = argc - optind;
801 /* Special rule: no arguments means "list" */
804 if (streq(argv[optind], "help")) {
809 for (i = 0; i < ELEMENTSOF(verbs); i++)
810 if (streq(argv[optind], verbs[i].verb))
813 if (i >= ELEMENTSOF(verbs)) {
814 log_error("Unknown operation %s", argv[optind]);
819 switch (verbs[i].argc_cmp) {
822 if (left != verbs[i].argc) {
823 log_error("Invalid number of arguments.");
830 if (left < verbs[i].argc) {
831 log_error("Too few arguments.");
838 if (left > verbs[i].argc) {
839 log_error("Too many arguments.");
846 assert_not_reached("Unknown comparison operator.");
849 return verbs[i].dispatch(argv + optind, left);
852 int main(int argc, char* argv[]) {
855 log_parse_environment();
858 r = parse_argv(argc, argv);
862 r = networkctl_main(argc, argv);
867 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;