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];
257 /* skip commonly misused 00:00:00 (Xerox) prefix */
258 if (memcmp(mac, "\0\0\0", 3) == 0)
261 snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
263 udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, str, 0))
264 if (strcmp(udev_list_entry_get_name(entry), "ID_OUI_FROM_DATABASE") == 0) {
265 description = strdup(udev_list_entry_get_value(entry));
276 static int get_gateway_description(sd_rtnl *rtnl, struct udev_hwdb *hwdb, int ifindex, int family,
277 union in_addr_union *gateway, char **gateway_description) {
278 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
283 assert(ifindex >= 0);
284 assert(family == AF_INET || family == AF_INET6);
286 assert(gateway_description);
288 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
292 r = sd_rtnl_message_request_dump(req, true);
296 r = sd_rtnl_call(rtnl, req, 0, &reply);
300 for (m = reply; m; m = sd_rtnl_message_next(m)) {
301 union in_addr_union gw = {};
302 struct ether_addr mac = {};
306 r = sd_rtnl_message_get_errno(m);
308 log_error_errno(r, "got error: %m");
312 r = sd_rtnl_message_get_type(m, &type);
314 log_error_errno(r, "could not get type: %m");
318 if (type != RTM_NEWNEIGH) {
319 log_error("type is not RTM_NEWNEIGH");
323 r = sd_rtnl_message_neigh_get_family(m, &fam);
325 log_error_errno(r, "could not get family: %m");
330 log_error("family is not correct");
334 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
336 log_error_errno(r, "could not get ifindex: %m");
340 if (ifindex > 0 && ifi != ifindex)
345 r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
351 r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
360 if (!in_addr_equal(fam, &gw, gateway))
363 r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
367 r = ieee_oui(hwdb, &mac, gateway_description);
377 static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
378 _cleanup_free_ struct local_address *local = NULL;
381 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
385 for (i = 0; i < n; i++) {
386 _cleanup_free_ char *gateway = NULL, *description = NULL;
388 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
392 r = get_gateway_description(rtnl, hwdb, ifindex, local[i].family, &local[i].address, &description);
394 log_debug_errno(r, "Could not get description of gateway: %m");
397 printf("%*s%s (%s)\n",
398 (int) strlen(prefix),
399 i == 0 ? prefix : "",
400 gateway, description);
403 (int) strlen(prefix),
404 i == 0 ? prefix : "",
411 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
412 _cleanup_free_ struct local_address *local = NULL;
415 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
419 for (i = 0; i < n; i++) {
420 _cleanup_free_ char *pretty = NULL;
422 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
427 (int) strlen(prefix),
428 i == 0 ? prefix : "",
435 static void dump_list(const char *prefix, char **l) {
440 (int) strlen(prefix),
441 i == l ? prefix : "",
446 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
447 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
448 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
449 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
450 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
451 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
452 char devid[2 + DECIMAL_STR_MAX(int)];
453 _cleanup_free_ char *t = NULL, *network = NULL;
454 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
455 const char *on_color_operational, *off_color_operational,
456 *on_color_setup, *off_color_setup;
467 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
468 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
470 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
472 return rtnl_log_create_error(r);
474 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
478 return rtnl_log_create_error(r);
480 r = sd_rtnl_call(rtnl, req, 0, &reply);
482 return log_error_errno(r, "Failed to query link: %m");
484 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
486 return rtnl_log_parse_error(r);
488 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
490 return rtnl_log_parse_error(r);
492 r = sd_rtnl_message_link_get_type(reply, &iftype);
494 return rtnl_log_parse_error(r);
496 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
500 bool all_zeroes = true;
502 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
512 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
514 sd_network_link_get_operational_state(ifindex, &operational_state);
515 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
517 sd_network_link_get_setup_state(ifindex, &setup_state);
518 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
520 sd_network_link_get_dns(ifindex, &dns);
521 sd_network_link_get_ntp(ifindex, &ntp);
522 sd_network_link_get_domains(ifindex, &domains);
523 r = sd_network_link_get_wildcard_domain(ifindex);
527 wildcard = strdup("*");
531 if (strv_consume(&domains, wildcard) < 0)
535 sprintf(devid, "n%i", ifindex);
536 d = udev_device_new_from_device_id(udev, devid);
538 link_get_type_string(iftype, d, &t);
541 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
542 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
543 path = udev_device_get_property_value(d, "ID_PATH");
545 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
547 vendor = udev_device_get_property_value(d, "ID_VENDOR");
549 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
551 model = udev_device_get_property_value(d, "ID_MODEL");
554 sd_network_link_get_network_file(ifindex, &network);
556 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
558 printf(" Link File: %s\n"
561 " State: %s%s%s (%s%s%s)\n",
565 on_color_operational, strna(operational_state), off_color_operational,
566 on_color_setup, strna(setup_state), off_color_setup);
569 printf(" Path: %s\n", path);
571 printf(" Driver: %s\n", driver);
573 printf(" Vendor: %s\n", vendor);
575 printf(" Model: %s\n", model);
578 char ea[ETHER_ADDR_TO_STRING_MAX];
579 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
583 printf(" MTU: %u\n", mtu);
585 hwdb = udev_hwdb_new(udev);
587 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
589 dump_addresses(rtnl, " Address: ", ifindex);
591 if (!strv_isempty(dns))
592 dump_list(" DNS: ", dns);
593 if (!strv_isempty(domains))
594 dump_list(" Domain: ", domains);
595 if (!strv_isempty(ntp))
596 dump_list(" NTP: ", ntp);
601 static int link_status(char **args, unsigned n) {
602 _cleanup_udev_unref_ struct udev *udev = NULL;
603 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
607 r = sd_rtnl_open(&rtnl, 0);
609 return log_error_errno(r, "Failed to connect to netlink: %m");
613 return log_error_errno(errno, "Failed to connect to udev: %m");
615 if (n <= 1 && !arg_all) {
616 _cleanup_free_ char *operational_state = NULL;
617 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
618 _cleanup_free_ struct local_address *addresses = NULL;
619 const char *on_color_operational, *off_color_operational;
622 sd_network_get_operational_state(&operational_state);
623 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
625 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
627 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
628 for (i = 0; i < c; i++) {
629 _cleanup_free_ char *pretty = NULL;
631 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
636 i > 0 ? "" : "Address:", pretty);
639 sd_network_get_dns(&dns);
640 if (!strv_isempty(dns))
641 dump_list(" DNS: ", dns);
643 sd_network_get_domains(&domains);
644 if (!strv_isempty(domains))
645 dump_list(" Domain: ", domains);
647 sd_network_get_ntp(&ntp);
648 if (!strv_isempty(ntp))
649 dump_list(" NTP: ", ntp);
654 pager_open_if_enabled();
657 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
658 _cleanup_free_ LinkInfo *links = NULL;
661 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
663 return rtnl_log_create_error(r);
665 r = sd_rtnl_message_request_dump(req, true);
667 return rtnl_log_create_error(r);
669 r = sd_rtnl_call(rtnl, req, 0, &reply);
671 return log_error_errno(r, "Failed to enumerate links: %m");
673 c = decode_and_sort_links(reply, &links);
675 return rtnl_log_parse_error(c);
677 for (i = 0; i < c; i++) {
681 link_status_one(rtnl, udev, links[i].name);
685 STRV_FOREACH(name, args + 1) {
689 link_status_one(rtnl, udev, *name);
695 static void help(void) {
696 printf("%s [OPTIONS...]\n\n"
697 "Query and control the networking subsystem.\n\n"
698 " -h --help Show this help\n"
699 " --version Show package version\n"
700 " --no-pager Do not pipe output into a pager\n"
701 " --no-legend Do not show the headers and footers\n"
702 " -a --all Show status for all links\n\n"
705 " status LINK Show link status\n"
706 , program_invocation_short_name);
709 static int parse_argv(int argc, char *argv[]) {
717 static const struct option options[] = {
718 { "help", no_argument, NULL, 'h' },
719 { "version", no_argument, NULL, ARG_VERSION },
720 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
721 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
722 { "all", no_argument, NULL, 'a' },
731 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
740 puts(PACKAGE_STRING);
741 puts(SYSTEMD_FEATURES);
760 assert_not_reached("Unhandled option");
767 static int networkctl_main(int argc, char *argv[]) {
769 static const struct {
777 int (* const dispatch)(char **args, unsigned n);
779 { "list", LESS, 1, list_links },
780 { "status", MORE, 1, link_status },
789 left = argc - optind;
792 /* Special rule: no arguments means "list" */
795 if (streq(argv[optind], "help")) {
800 for (i = 0; i < ELEMENTSOF(verbs); i++)
801 if (streq(argv[optind], verbs[i].verb))
804 if (i >= ELEMENTSOF(verbs)) {
805 log_error("Unknown operation %s", argv[optind]);
810 switch (verbs[i].argc_cmp) {
813 if (left != verbs[i].argc) {
814 log_error("Invalid number of arguments.");
821 if (left < verbs[i].argc) {
822 log_error("Too few arguments.");
829 if (left > verbs[i].argc) {
830 log_error("Too many arguments.");
837 assert_not_reached("Unknown comparison operator.");
840 return verbs[i].dispatch(argv + optind, left);
843 int main(int argc, char* argv[]) {
846 log_parse_environment();
849 r = parse_argv(argc, argv);
853 r = networkctl_main(argc, argv);
858 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;