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"
38 static bool arg_no_pager = false;
39 static bool arg_legend = true;
40 static bool arg_all = false;
42 static void pager_open_if_enabled(void) {
50 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
54 if (iftype == ARPHRD_ETHER && d) {
55 const char *devtype, *id = NULL;
56 /* WLANs have iftype ARPHRD_ETHER, but we want
57 * to show a more useful type string for
60 devtype = udev_device_get_devtype(d);
61 if (streq_ptr(devtype, "wlan"))
63 else if (streq_ptr(devtype, "wwan"))
76 t = arphrd_to_name(iftype);
92 typedef struct LinkInfo {
98 static int link_info_compare(const void *a, const void *b) {
99 const LinkInfo *x = a, *y = b;
101 return x->ifindex - y->ifindex;
104 static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
105 _cleanup_free_ LinkInfo *links = NULL;
106 size_t size = 0, c = 0;
110 for (i = m; i; i = sd_rtnl_message_next(i)) {
116 r = sd_rtnl_message_get_type(i, &type);
120 if (type != RTM_NEWLINK)
123 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
127 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
131 r = sd_rtnl_message_link_get_type(i, &iftype);
135 if (!GREEDY_REALLOC(links, size, c+1))
138 links[c].name = name;
139 links[c].ifindex = ifindex;
140 links[c].iftype = iftype;
144 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
152 static void operational_state_to_color(const char *state, const char **on, const char **off) {
156 if (streq_ptr(state, "routable")) {
157 *on = ansi_highlight_green();
158 *off = ansi_highlight_off();
159 } else if (streq_ptr(state, "degraded")) {
160 *on = ansi_highlight_yellow();
161 *off = ansi_highlight_off();
166 static void setup_state_to_color(const char *state, const char **on, const char **off) {
170 if (streq_ptr(state, "configured")) {
171 *on = ansi_highlight_green();
172 *off = ansi_highlight_off();
173 } else if (streq_ptr(state, "configuring")) {
174 *on = ansi_highlight_yellow();
175 *off = ansi_highlight_off();
176 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
177 *on = ansi_highlight_red();
178 *off = ansi_highlight_off();
183 static int list_links(char **args, unsigned n) {
184 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
185 _cleanup_udev_unref_ struct udev *udev = NULL;
186 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
187 _cleanup_free_ LinkInfo *links = NULL;
190 pager_open_if_enabled();
192 r = sd_rtnl_open(&rtnl, 0);
194 return log_error_errno(r, "Failed to connect to netlink: %m");
198 return log_error_errno(errno, "Failed to connect to udev: %m");
200 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
202 return rtnl_log_create_error(r);
204 r = sd_rtnl_message_request_dump(req, true);
206 return rtnl_log_create_error(r);
208 r = sd_rtnl_call(rtnl, req, 0, &reply);
210 return log_error_errno(r, "Failed to enumerate links: %m");
213 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
215 c = decode_and_sort_links(reply, &links);
217 return rtnl_log_parse_error(c);
219 for (i = 0; i < c; i++) {
220 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
221 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
222 const char *on_color_operational, *off_color_operational,
223 *on_color_setup, *off_color_setup;
224 char devid[2 + DECIMAL_STR_MAX(int)];
225 _cleanup_free_ char *t = NULL;
227 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
228 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
230 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
231 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
233 sprintf(devid, "n%i", links[i].ifindex);
234 d = udev_device_new_from_device_id(udev, devid);
236 link_get_type_string(links[i].iftype, d, &t);
238 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
239 links[i].ifindex, links[i].name, strna(t),
240 on_color_operational, strna(operational_state), off_color_operational,
241 on_color_setup, strna(setup_state), off_color_setup);
245 printf("\n%i links listed.\n", c);
250 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
251 _cleanup_free_ struct local_address *local = NULL;
254 n = local_addresses(rtnl, ifindex, &local);
258 for (i = 0; i < n; i++) {
259 _cleanup_free_ char *pretty = NULL;
261 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
266 (int) strlen(prefix),
267 i == 0 ? prefix : "",
274 static void dump_list(const char *prefix, char **l) {
279 (int) strlen(prefix),
280 i == l ? prefix : "",
285 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
286 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
287 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
288 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
289 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
290 char devid[2 + DECIMAL_STR_MAX(int)];
291 _cleanup_free_ char *t = NULL, *network = NULL;
292 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
293 const char *on_color_operational, *off_color_operational,
294 *on_color_setup, *off_color_setup;
305 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
306 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
308 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
310 return rtnl_log_create_error(r);
312 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
316 return rtnl_log_create_error(r);
318 r = sd_rtnl_call(rtnl, req, 0, &reply);
320 return log_error_errno(r, "Failed to query link: %m");
322 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
324 return rtnl_log_parse_error(r);
326 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
328 return rtnl_log_parse_error(r);
330 r = sd_rtnl_message_link_get_type(reply, &iftype);
332 return rtnl_log_parse_error(r);
334 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
338 bool all_zeroes = true;
340 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
350 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
352 sd_network_link_get_operational_state(ifindex, &operational_state);
353 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
355 sd_network_link_get_setup_state(ifindex, &setup_state);
356 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
358 sd_network_link_get_dns(ifindex, &dns);
359 sd_network_link_get_ntp(ifindex, &ntp);
360 sd_network_link_get_domains(ifindex, &domains);
361 r = sd_network_link_get_wildcard_domain(ifindex);
365 wildcard = strdup("*");
369 if (strv_consume(&domains, wildcard) < 0)
373 sprintf(devid, "n%i", ifindex);
374 d = udev_device_new_from_device_id(udev, devid);
376 link_get_type_string(iftype, d, &t);
379 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
380 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
381 path = udev_device_get_property_value(d, "ID_PATH");
383 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
385 vendor = udev_device_get_property_value(d, "ID_VENDOR");
387 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
389 model = udev_device_get_property_value(d, "ID_MODEL");
392 sd_network_link_get_network_file(ifindex, &network);
394 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
396 printf(" Link File: %s\n"
399 " State: %s%s%s (%s%s%s)\n",
403 on_color_operational, strna(operational_state), off_color_operational,
404 on_color_setup, strna(setup_state), off_color_setup);
407 printf(" Path: %s\n", path);
409 printf(" Driver: %s\n", driver);
411 printf(" Vendor: %s\n", vendor);
413 printf(" Model: %s\n", model);
416 char ea[ETHER_ADDR_TO_STRING_MAX];
417 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
421 printf(" MTU: %u\n", mtu);
423 dump_addresses(rtnl, " Address: ", ifindex);
425 if (!strv_isempty(dns))
426 dump_list(" DNS: ", dns);
427 if (!strv_isempty(domains))
428 dump_list(" Domain: ", domains);
429 if (!strv_isempty(ntp))
430 dump_list(" NTP: ", ntp);
435 static int link_status(char **args, unsigned n) {
436 _cleanup_udev_unref_ struct udev *udev = NULL;
437 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
441 r = sd_rtnl_open(&rtnl, 0);
443 return log_error_errno(r, "Failed to connect to netlink: %m");
447 return log_error_errno(errno, "Failed to connect to udev: %m");
449 if (n <= 1 && !arg_all) {
450 _cleanup_free_ char *operational_state = NULL;
451 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
452 _cleanup_free_ struct local_address *addresses = NULL;
453 const char *on_color_operational, *off_color_operational;
456 sd_network_get_operational_state(&operational_state);
457 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
459 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
461 c = local_addresses(rtnl, 0, &addresses);
462 for (i = 0; i < c; i++) {
463 _cleanup_free_ char *pretty = NULL;
465 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
470 i > 0 ? "" : "Address:", pretty);
473 sd_network_get_dns(&dns);
474 if (!strv_isempty(dns))
475 dump_list(" DNS: ", dns);
477 sd_network_get_domains(&domains);
478 if (!strv_isempty(domains))
479 dump_list(" Domain: ", domains);
481 sd_network_get_ntp(&ntp);
482 if (!strv_isempty(ntp))
483 dump_list(" NTP: ", ntp);
488 pager_open_if_enabled();
491 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
492 _cleanup_free_ LinkInfo *links = NULL;
495 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
497 return rtnl_log_create_error(r);
499 r = sd_rtnl_message_request_dump(req, true);
501 return rtnl_log_create_error(r);
503 r = sd_rtnl_call(rtnl, req, 0, &reply);
505 return log_error_errno(r, "Failed to enumerate links: %m");
507 c = decode_and_sort_links(reply, &links);
509 return rtnl_log_parse_error(c);
511 for (i = 0; i < c; i++) {
515 link_status_one(rtnl, udev, links[i].name);
519 STRV_FOREACH(name, args + 1) {
523 link_status_one(rtnl, udev, *name);
529 static void help(void) {
530 printf("%s [OPTIONS...]\n\n"
531 "Query and control the networking subsystem.\n\n"
532 " -h --help Show this help\n"
533 " --version Show package version\n"
534 " --no-pager Do not pipe output into a pager\n"
535 " --no-legend Do not show the headers and footers\n"
536 " -a --all Show status for all links\n\n"
539 " status LINK Show link status\n"
540 , program_invocation_short_name);
543 static int parse_argv(int argc, char *argv[]) {
551 static const struct option options[] = {
552 { "help", no_argument, NULL, 'h' },
553 { "version", no_argument, NULL, ARG_VERSION },
554 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
555 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
556 { "all", no_argument, NULL, 'a' },
565 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
574 puts(PACKAGE_STRING);
575 puts(SYSTEMD_FEATURES);
594 assert_not_reached("Unhandled option");
601 static int networkctl_main(int argc, char *argv[]) {
603 static const struct {
611 int (* const dispatch)(char **args, unsigned n);
613 { "list", LESS, 1, list_links },
614 { "status", MORE, 1, link_status },
623 left = argc - optind;
626 /* Special rule: no arguments means "list" */
629 if (streq(argv[optind], "help")) {
634 for (i = 0; i < ELEMENTSOF(verbs); i++)
635 if (streq(argv[optind], verbs[i].verb))
638 if (i >= ELEMENTSOF(verbs)) {
639 log_error("Unknown operation %s", argv[optind]);
644 switch (verbs[i].argc_cmp) {
647 if (left != verbs[i].argc) {
648 log_error("Invalid number of arguments.");
655 if (left < verbs[i].argc) {
656 log_error("Too few arguments.");
663 if (left > verbs[i].argc) {
664 log_error("Too many arguments.");
671 assert_not_reached("Unknown comparison operator.");
674 return verbs[i].dispatch(argv + optind, left);
677 int main(int argc, char* argv[]) {
680 log_parse_environment();
683 r = parse_argv(argc, argv);
687 r = networkctl_main(argc, argv);
692 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;