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(
281 struct udev_hwdb *hwdb,
284 union in_addr_union *gateway,
285 char **gateway_description) {
287 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
292 assert(ifindex >= 0);
293 assert(family == AF_INET || family == AF_INET6);
295 assert(gateway_description);
297 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
301 r = sd_rtnl_message_request_dump(req, true);
305 r = sd_rtnl_call(rtnl, req, 0, &reply);
309 for (m = reply; m; m = sd_rtnl_message_next(m)) {
310 union in_addr_union gw = {};
311 struct ether_addr mac = {};
315 r = sd_rtnl_message_get_errno(m);
317 log_error_errno(r, "got error: %m");
321 r = sd_rtnl_message_get_type(m, &type);
323 log_error_errno(r, "could not get type: %m");
327 if (type != RTM_NEWNEIGH) {
328 log_error("type is not RTM_NEWNEIGH");
332 r = sd_rtnl_message_neigh_get_family(m, &fam);
334 log_error_errno(r, "could not get family: %m");
339 log_error("family is not correct");
343 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
345 log_error_errno(r, "could not get ifindex: %m");
349 if (ifindex > 0 && ifi != ifindex)
354 r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
360 r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
369 if (!in_addr_equal(fam, &gw, gateway))
372 r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
376 r = ieee_oui(hwdb, &mac, gateway_description);
386 static int dump_gateways(
388 struct udev_hwdb *hwdb,
392 _cleanup_free_ struct local_address *local = NULL;
395 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
399 for (i = 0; i < n; i++) {
400 _cleanup_free_ char *gateway = NULL, *description = NULL;
402 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
406 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
408 log_debug_errno(r, "Could not get description of gateway: %m");
411 printf("%*s%s (%s)\n",
412 (int) strlen(prefix),
413 i == 0 ? prefix : "",
414 gateway, description);
417 (int) strlen(prefix),
418 i == 0 ? prefix : "",
425 static int dump_addresses(
430 _cleanup_free_ struct local_address *local = NULL;
433 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
437 for (i = 0; i < n; i++) {
438 _cleanup_free_ char *pretty = NULL;
440 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
445 (int) strlen(prefix),
446 i == 0 ? prefix : "",
453 static void dump_list(const char *prefix, char **l) {
458 (int) strlen(prefix),
459 i == l ? prefix : "",
464 static int link_status_one(
467 struct udev_hwdb *hwdb,
470 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
471 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
472 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
473 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
474 char devid[2 + DECIMAL_STR_MAX(int)];
475 _cleanup_free_ char *t = NULL, *network = NULL;
476 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
477 const char *on_color_operational, *off_color_operational,
478 *on_color_setup, *off_color_setup;
489 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
490 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
492 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
494 return rtnl_log_create_error(r);
496 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
500 return rtnl_log_create_error(r);
502 r = sd_rtnl_call(rtnl, req, 0, &reply);
504 return log_error_errno(r, "Failed to query link: %m");
506 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
508 return rtnl_log_parse_error(r);
510 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
512 return rtnl_log_parse_error(r);
514 r = sd_rtnl_message_link_get_type(reply, &iftype);
516 return rtnl_log_parse_error(r);
518 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
522 bool all_zeroes = true;
524 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
534 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
536 sd_network_link_get_operational_state(ifindex, &operational_state);
537 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
539 sd_network_link_get_setup_state(ifindex, &setup_state);
540 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
542 sd_network_link_get_dns(ifindex, &dns);
543 sd_network_link_get_ntp(ifindex, &ntp);
544 sd_network_link_get_domains(ifindex, &domains);
545 r = sd_network_link_get_wildcard_domain(ifindex);
549 wildcard = strdup("*");
553 if (strv_consume(&domains, wildcard) < 0)
557 sprintf(devid, "n%i", ifindex);
558 d = udev_device_new_from_device_id(udev, devid);
560 link_get_type_string(iftype, d, &t);
563 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
564 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
565 path = udev_device_get_property_value(d, "ID_PATH");
567 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
569 vendor = udev_device_get_property_value(d, "ID_VENDOR");
571 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
573 model = udev_device_get_property_value(d, "ID_MODEL");
576 sd_network_link_get_network_file(ifindex, &network);
578 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
580 printf(" Link File: %s\n"
583 " State: %s%s%s (%s%s%s)\n",
587 on_color_operational, strna(operational_state), off_color_operational,
588 on_color_setup, strna(setup_state), off_color_setup);
591 printf(" Path: %s\n", path);
593 printf(" Driver: %s\n", driver);
595 printf(" Vendor: %s\n", vendor);
597 printf(" Model: %s\n", model);
600 _cleanup_free_ char *description = NULL;
601 char ea[ETHER_ADDR_TO_STRING_MAX];
603 ieee_oui(hwdb, &e, &description);
606 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
608 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
612 printf(" MTU: %u\n", mtu);
614 dump_addresses(rtnl, " Address: ", ifindex);
615 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
617 if (!strv_isempty(dns))
618 dump_list(" DNS: ", dns);
619 if (!strv_isempty(domains))
620 dump_list(" Domain: ", domains);
621 if (!strv_isempty(ntp))
622 dump_list(" NTP: ", ntp);
627 static int link_status(char **args, unsigned n) {
628 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
629 _cleanup_udev_unref_ struct udev *udev = NULL;
630 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
634 r = sd_rtnl_open(&rtnl, 0);
636 return log_error_errno(r, "Failed to connect to netlink: %m");
640 return log_error_errno(errno, "Failed to connect to udev: %m");
642 hwdb = udev_hwdb_new(udev);
644 log_debug_errno(errno, "Failed to open hardware database: %m");
646 if (n <= 1 && !arg_all) {
647 _cleanup_free_ char *operational_state = NULL;
648 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
649 _cleanup_free_ struct local_address *addresses = NULL;
650 const char *on_color_operational, *off_color_operational;
653 sd_network_get_operational_state(&operational_state);
654 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
656 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
658 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
659 for (i = 0; i < c; i++) {
660 _cleanup_free_ char *pretty = NULL;
662 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
667 i > 0 ? "" : "Address:", pretty);
670 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
672 sd_network_get_dns(&dns);
673 if (!strv_isempty(dns))
674 dump_list(" DNS: ", dns);
676 sd_network_get_domains(&domains);
677 if (!strv_isempty(domains))
678 dump_list(" Domain: ", domains);
680 sd_network_get_ntp(&ntp);
681 if (!strv_isempty(ntp))
682 dump_list(" NTP: ", ntp);
687 pager_open_if_enabled();
690 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
691 _cleanup_free_ LinkInfo *links = NULL;
694 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
696 return rtnl_log_create_error(r);
698 r = sd_rtnl_message_request_dump(req, true);
700 return rtnl_log_create_error(r);
702 r = sd_rtnl_call(rtnl, req, 0, &reply);
704 return log_error_errno(r, "Failed to enumerate links: %m");
706 c = decode_and_sort_links(reply, &links);
708 return rtnl_log_parse_error(c);
710 for (i = 0; i < c; i++) {
714 link_status_one(rtnl, udev, hwdb, links[i].name);
717 STRV_FOREACH(name, args + 1) {
721 link_status_one(rtnl, udev, hwdb, *name);
728 static void help(void) {
729 printf("%s [OPTIONS...]\n\n"
730 "Query and control the networking subsystem.\n\n"
731 " -h --help Show this help\n"
732 " --version Show package version\n"
733 " --no-pager Do not pipe output into a pager\n"
734 " --no-legend Do not show the headers and footers\n"
735 " -a --all Show status for all links\n\n"
738 " status LINK Show link status\n"
739 , program_invocation_short_name);
742 static int parse_argv(int argc, char *argv[]) {
750 static const struct option options[] = {
751 { "help", no_argument, NULL, 'h' },
752 { "version", no_argument, NULL, ARG_VERSION },
753 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
754 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
755 { "all", no_argument, NULL, 'a' },
764 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
773 puts(PACKAGE_STRING);
774 puts(SYSTEMD_FEATURES);
793 assert_not_reached("Unhandled option");
800 static int networkctl_main(int argc, char *argv[]) {
802 static const struct {
810 int (* const dispatch)(char **args, unsigned n);
812 { "list", LESS, 1, list_links },
813 { "status", MORE, 1, link_status },
822 left = argc - optind;
825 /* Special rule: no arguments means "list" */
828 if (streq(argv[optind], "help")) {
833 for (i = 0; i < ELEMENTSOF(verbs); i++)
834 if (streq(argv[optind], verbs[i].verb))
837 if (i >= ELEMENTSOF(verbs)) {
838 log_error("Unknown operation %s", argv[optind]);
843 switch (verbs[i].argc_cmp) {
846 if (left != verbs[i].argc) {
847 log_error("Invalid number of arguments.");
854 if (left < verbs[i].argc) {
855 log_error("Too few arguments.");
862 if (left > verbs[i].argc) {
863 log_error("Too many arguments.");
870 assert_not_reached("Unknown comparison operator.");
873 return verbs[i].dispatch(argv + optind, left);
876 int main(int argc, char* argv[]) {
879 log_parse_environment();
882 r = parse_argv(argc, argv);
886 r = networkctl_main(argc, argv);
891 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;