]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
networkd: rename UseDomainName to UseDomains
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
ee8c4568
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <stdbool.h>
23#include <getopt.h>
24
25#include "sd-network.h"
26#include "sd-rtnl.h"
27#include "libudev.h"
28
29#include "build.h"
30#include "util.h"
31#include "pager.h"
32#include "rtnl-util.h"
33#include "udev-util.h"
34#include "arphrd-list.h"
35#include "local-addresses.h"
36
37static bool arg_no_pager = false;
38static bool arg_legend = true;
9085f64a 39static bool arg_all = false;
ee8c4568
LP
40
41static void pager_open_if_enabled(void) {
42
43 if (arg_no_pager)
44 return;
45
46 pager_open(false);
47}
48
49static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
50 const char *t;
51 char *p;
52
53 if (iftype == ARPHRD_ETHER && d) {
54 const char *devtype, *id = NULL;
55 /* WLANs have iftype ARPHRD_ETHER, but we want
56 * to show a more useful type string for
57 * them */
58
59 devtype = udev_device_get_devtype(d);
60 if (streq_ptr(devtype, "wlan"))
61 id = "wlan";
62 else if (streq_ptr(devtype, "wwan"))
63 id = "wwan";
64
65 if (id) {
66 p = strdup(id);
67 if (!p)
68 return -ENOMEM;
69
70 *ret = p;
71 return 1;
72 }
73 }
74
75 t = arphrd_to_name(iftype);
76 if (!t) {
77 *ret = NULL;
78 return 0;
79 }
80
81 p = strdup(t);
82 if (!p)
83 return -ENOMEM;
84
85 ascii_strlower(p);
86 *ret = p;
87
88 return 0;
89}
90
6d0c65ff
LP
91typedef struct LinkInfo {
92 const char *name;
93 int ifindex;
94 unsigned iftype;
95} LinkInfo;
96
97static int link_info_compare(const void *a, const void *b) {
98 const LinkInfo *x = a, *y = b;
99
100 return x->ifindex - y->ifindex;
101}
102
103static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
104 _cleanup_free_ LinkInfo *links = NULL;
105 size_t size = 0, c = 0;
106 sd_rtnl_message *i;
107 int r;
108
109 for (i = m; i; i = sd_rtnl_message_next(i)) {
110 const char *name;
111 unsigned iftype;
112 uint16_t type;
113 int ifindex;
114
115 r = sd_rtnl_message_get_type(i, &type);
116 if (r < 0)
117 return r;
118
119 if (type != RTM_NEWLINK)
120 continue;
121
122 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
123 if (r < 0)
124 return r;
125
126 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
127 if (r < 0)
128 return r;
129
130 r = sd_rtnl_message_link_get_type(i, &iftype);
131 if (r < 0)
132 return r;
133
134 if (!GREEDY_REALLOC(links, size, c+1))
135 return -ENOMEM;
136
137 links[c].name = name;
138 links[c].ifindex = ifindex;
139 links[c].iftype = iftype;
140 c++;
141 }
142
143 qsort(links, c, sizeof(LinkInfo), link_info_compare);
144
145 *ret = links;
146 links = NULL;
147
148 return (int) c;
149}
150
d57c365b
LP
151static void operational_state_to_color(const char *state, const char **on, const char **off) {
152 assert(on);
153 assert(off);
154
155 if (streq_ptr(state, "routable")) {
156 *on = ansi_highlight_green();
157 *off = ansi_highlight_off();
158 } else if (streq_ptr(state, "degraded")) {
159 *on = ansi_highlight_yellow();
160 *off = ansi_highlight_off();
161 } else
162 *on = *off = "";
163}
164
165static void setup_state_to_color(const char *state, const char **on, const char **off) {
166 assert(on);
167 assert(off);
168
169 if (streq_ptr(state, "configured")) {
170 *on = ansi_highlight_green();
171 *off = ansi_highlight_off();
172 } else if (streq_ptr(state, "configuring")) {
173 *on = ansi_highlight_yellow();
174 *off = ansi_highlight_off();
175 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
176 *on = ansi_highlight_red();
177 *off = ansi_highlight_off();
178 } else
179 *on = *off = "";
180}
181
ee8c4568
LP
182static int list_links(char **args, unsigned n) {
183 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
184 _cleanup_udev_unref_ struct udev *udev = NULL;
185 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
6d0c65ff
LP
186 _cleanup_free_ LinkInfo *links = NULL;
187 int r, c, i;
ee8c4568
LP
188
189 pager_open_if_enabled();
190
191 r = sd_rtnl_open(&rtnl, 0);
192 if (r < 0) {
193 log_error("Failed to connect to netlink: %s", strerror(-r));
194 return r;
195 }
196
197 udev = udev_new();
198 if (!udev) {
199 log_error("Failed to connect to udev: %m");
200 return -errno;
201 }
202
203 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
204 if (r < 0)
205 return rtnl_log_create_error(r);
206
207 r = sd_rtnl_message_request_dump(req, true);
208 if (r < 0)
209 return rtnl_log_create_error(r);
210
211 r = sd_rtnl_call(rtnl, req, 0, &reply);
212 if (r < 0) {
213 log_error("Failed to enumerate links: %s", strerror(-r));
214 return r;
215 }
216
217 if (arg_legend)
d57c365b 218 printf("%3s %-16s %-10s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 219
6d0c65ff
LP
220 c = decode_and_sort_links(reply, &links);
221 if (c < 0)
222 return rtnl_log_parse_error(c);
223
224 for (i = 0; i < c; i++) {
ab1525bc 225 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
ee8c4568 226 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
d57c365b
LP
227 const char *on_color_operational, *off_color_operational,
228 *on_color_setup, *off_color_setup;
df3fb561 229 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 230 _cleanup_free_ char *t = NULL;
ee8c4568 231
d6731e4c 232 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
233 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
234
235 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
236 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
ee8c4568 237
6d0c65ff 238 sprintf(devid, "n%i", links[i].ifindex);
ee8c4568
LP
239 d = udev_device_new_from_device_id(udev, devid);
240
6d0c65ff 241 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 242
d57c365b
LP
243 printf("%3i %-16s %-10s %s%-11s%s %s%-10s%s\n",
244 links[i].ifindex, links[i].name, strna(t),
245 on_color_operational, strna(operational_state), off_color_operational,
246 on_color_setup, strna(setup_state), off_color_setup);
ee8c4568
LP
247 }
248
249 if (arg_legend)
6d0c65ff 250 printf("\n%i links listed.\n", c);
ee8c4568
LP
251
252 return 0;
253}
254
255static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
256 _cleanup_free_ struct local_address *local = NULL;
257 int r, n, i;
258
259 n = local_addresses(rtnl, ifindex, &local);
260 if (n < 0)
261 return n;
262
263 for (i = 0; i < n; i++) {
264 _cleanup_free_ char *pretty = NULL;
265
266 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
267 if (r < 0)
268 return r;
269
270 printf("%*s%s\n",
271 (int) strlen(prefix),
272 i == 0 ? prefix : "",
273 pretty);
274 }
275
276 return 0;
277}
278
279static void dump_list(const char *prefix, char **l) {
280 char **i;
281
282 STRV_FOREACH(i, l) {
283 printf("%*s%s\n",
284 (int) strlen(prefix),
285 i == l ? prefix : "",
286 *i);
287 }
288}
289
9085f64a 290static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
2301cb9f 291 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
ab1525bc 292 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
293 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
294 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
295 char devid[2 + DECIMAL_STR_MAX(int)];
296 _cleanup_free_ char *t = NULL;
297 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
d57c365b
LP
298 const char *on_color_operational, *off_color_operational,
299 *on_color_setup, *off_color_setup;
9085f64a
LP
300 struct ether_addr e;
301 unsigned iftype;
302 int r, ifindex;
303 bool have_mac;
304 uint32_t mtu;
305
306 assert(rtnl);
307 assert(udev);
308 assert(name);
309
310 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
311 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
312 else {
313 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
314 if (r < 0)
315 return rtnl_log_create_error(r);
316
317 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
318 }
319
320 if (r < 0)
321 return rtnl_log_create_error(r);
322
323 r = sd_rtnl_call(rtnl, req, 0, &reply);
324 if (r < 0) {
325 log_error("Failed to query link: %s", strerror(-r));
326 return r;
327 }
328
329 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
330 if (r < 0)
331 return rtnl_log_parse_error(r);
332
333 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
334 if (r < 0)
335 return rtnl_log_parse_error(r);
336
337 r = sd_rtnl_message_link_get_type(reply, &iftype);
338 if (r < 0)
339 return rtnl_log_parse_error(r);
340
341 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
342
343 if (have_mac) {
344 const uint8_t *p;
345 bool all_zeroes = true;
346
347 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
348 if (*p != 0) {
349 all_zeroes = false;
350 break;
351 }
352
353 if (all_zeroes)
354 have_mac = false;
355 }
356
357 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
358
d6731e4c 359 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
360 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
361
362 sd_network_link_get_setup_state(ifindex, &setup_state);
363 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 364
d6731e4c
TG
365 sd_network_link_get_dns(ifindex, &dns);
366 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 367 sd_network_link_get_domains(ifindex, &domains);
9085f64a
LP
368
369 sprintf(devid, "n%i", ifindex);
370 d = udev_device_new_from_device_id(udev, devid);
371
372 link_get_type_string(iftype, d, &t);
373
374 if (d) {
375 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
376 path = udev_device_get_property_value(d, "ID_PATH");
377
378 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
379 if (!vendor)
380 vendor = udev_device_get_property_value(d, "ID_VENDOR");
381
382 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
383 if (!model)
384 model = udev_device_get_property_value(d, "ID_MODEL");
385 }
386
df3fb561 387
d57c365b 388 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a
LP
389
390 printf(" Type: %s\n"
d57c365b 391 " State: %s%s%s (%s%s%s)\n",
9085f64a 392 strna(t),
d57c365b
LP
393 on_color_operational, strna(operational_state), off_color_operational,
394 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
395
396 if (path)
397 printf(" Path: %s\n", path);
398 if (driver)
399 printf(" Driver: %s\n", driver);
400 if (vendor)
401 printf(" Vendor: %s\n", vendor);
402 if (model)
403 printf(" Model: %s\n", model);
404
405 if (have_mac)
406 printf(" HW Address: %s\n", ether_ntoa(&e));
407
408 if (mtu > 0)
409 printf(" MTU: %u\n", mtu);
410
411 dump_addresses(rtnl, " Address: ", ifindex);
412
413 if (!strv_isempty(dns))
414 dump_list(" DNS: ", dns);
2301cb9f
LP
415 if (!strv_isempty(domains))
416 dump_list(" Domains: ", domains);
9085f64a
LP
417 if (!strv_isempty(ntp))
418 dump_list(" NTP: ", ntp);
419
420 return 0;
421}
422
ee8c4568
LP
423static int link_status(char **args, unsigned n) {
424 _cleanup_udev_unref_ struct udev *udev = NULL;
425 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
426 char **name;
427 int r;
428
f7d68aa8
LP
429 r = sd_rtnl_open(&rtnl, 0);
430 if (r < 0) {
431 log_error("Failed to connect to netlink: %s", strerror(-r));
432 return r;
433 }
434
435 udev = udev_new();
436 if (!udev) {
437 log_error("Failed to connect to udev: %m");
438 return -errno;
439 }
440
9085f64a 441 if (n <= 1 && !arg_all) {
ee8c4568 442 _cleanup_free_ char *operational_state = NULL;
03cc0fd1 443 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
f7d68aa8 444 _cleanup_free_ struct local_address *addresses = NULL;
e92da1e5 445 const char *on_color_operational, *off_color_operational;
f7d68aa8 446 int i, c;
ee8c4568 447
03cc0fd1 448 sd_network_get_operational_state(&operational_state);
e92da1e5 449 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 450
e92da1e5 451 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 452
f7d68aa8
LP
453 c = local_addresses(rtnl, 0, &addresses);
454 for (i = 0; i < c; i++) {
455 _cleanup_free_ char *pretty = NULL;
456
457 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
458 if (r < 0)
459 return log_oom();
460
461 printf("%13s %s\n",
462 i > 0 ? "" : "Address:", pretty);
463 }
464
03cc0fd1
LP
465 sd_network_get_dns(&dns);
466 if (!strv_isempty(dns))
467 dump_list(" DNS: ", dns);
468
469 sd_network_get_dns(&ntp);
470 if (!strv_isempty(ntp))
471 dump_list(" NTP: ", ntp);
ee8c4568 472
ee8c4568
LP
473 return 0;
474 }
475
476 pager_open_if_enabled();
477
9085f64a 478 if (arg_all) {
ee8c4568 479 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
480 _cleanup_free_ LinkInfo *links = NULL;
481 int c, i;
ee8c4568 482
9085f64a
LP
483 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
484 if (r < 0)
485 return rtnl_log_create_error(r);
ee8c4568 486
9085f64a 487 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
488 if (r < 0)
489 return rtnl_log_create_error(r);
490
491 r = sd_rtnl_call(rtnl, req, 0, &reply);
492 if (r < 0) {
9085f64a
LP
493 log_error("Failed to enumerate links: %s", strerror(-r));
494 return r;
ee8c4568
LP
495 }
496
6d0c65ff
LP
497 c = decode_and_sort_links(reply, &links);
498 if (c < 0)
499 return rtnl_log_parse_error(c);
ee8c4568 500
6d0c65ff
LP
501 for (i = 0; i < c; i++) {
502 if (i > 0)
9085f64a 503 fputc('\n', stdout);
ee8c4568 504
6d0c65ff 505 link_status_one(rtnl, udev, links[i].name);
ee8c4568 506 }
9085f64a 507 }
ee8c4568 508
9085f64a
LP
509 STRV_FOREACH(name, args + 1) {
510 if (name != args+1)
511 fputc('\n', stdout);
ee8c4568 512
9085f64a 513 link_status_one(rtnl, udev, *name);
ee8c4568
LP
514 }
515
516 return 0;
517}
518
519static void help(void) {
520 printf("%s [OPTIONS...]\n\n"
521 "Query and control the networking subsystem.\n\n"
522 " -h --help Show this help\n"
9085f64a
LP
523 " --version Show package version\n"
524 " --no-pager Do not pipe output into a pager\n"
525 " --no-legend Do not show the headers and footers\n"
526 " -a --all Show status for all links\n\n"
ee8c4568
LP
527 "Commands:\n"
528 " list List links\n"
529 " status LINK Show link status\n"
530 , program_invocation_short_name);
531}
532
533static int parse_argv(int argc, char *argv[]) {
534
535 enum {
536 ARG_VERSION = 0x100,
537 ARG_NO_PAGER,
538 ARG_NO_LEGEND,
539 };
540
541 static const struct option options[] = {
542 { "help", no_argument, NULL, 'h' },
543 { "version", no_argument, NULL, ARG_VERSION },
544 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
545 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 546 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
547 {}
548 };
549
550 int c;
551
552 assert(argc >= 0);
553 assert(argv);
554
9085f64a 555 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
556
557 switch (c) {
558
559 case 'h':
560 help();
561 return 0;
562
563 case ARG_VERSION:
564 puts(PACKAGE_STRING);
565 puts(SYSTEMD_FEATURES);
566 return 0;
567
568 case ARG_NO_PAGER:
569 arg_no_pager = true;
570 break;
571
572 case ARG_NO_LEGEND:
573 arg_legend = false;
574 break;
575
9085f64a
LP
576 case 'a':
577 arg_all = true;
578 break;
579
ee8c4568
LP
580 case '?':
581 return -EINVAL;
582
583 default:
584 assert_not_reached("Unhandled option");
585 }
586 }
587
588 return 1;
589}
590
591static int networkctl_main(int argc, char *argv[]) {
592
593 static const struct {
594 const char* verb;
595 const enum {
596 MORE,
597 LESS,
598 EQUAL
599 } argc_cmp;
600 const int argc;
601 int (* const dispatch)(char **args, unsigned n);
602 } verbs[] = {
603 { "list", LESS, 1, list_links },
604 { "status", MORE, 1, link_status },
605 };
606
607 int left;
608 unsigned i;
609
610 assert(argc >= 0);
611 assert(argv);
612
613 left = argc - optind;
614
615 if (left <= 0)
616 /* Special rule: no arguments means "list" */
617 i = 0;
618 else {
619 if (streq(argv[optind], "help")) {
620 help();
621 return 0;
622 }
623
624 for (i = 0; i < ELEMENTSOF(verbs); i++)
625 if (streq(argv[optind], verbs[i].verb))
626 break;
627
628 if (i >= ELEMENTSOF(verbs)) {
629 log_error("Unknown operation %s", argv[optind]);
630 return -EINVAL;
631 }
632 }
633
634 switch (verbs[i].argc_cmp) {
635
636 case EQUAL:
637 if (left != verbs[i].argc) {
638 log_error("Invalid number of arguments.");
639 return -EINVAL;
640 }
641
642 break;
643
644 case MORE:
645 if (left < verbs[i].argc) {
646 log_error("Too few arguments.");
647 return -EINVAL;
648 }
649
650 break;
651
652 case LESS:
653 if (left > verbs[i].argc) {
654 log_error("Too many arguments.");
655 return -EINVAL;
656 }
657
658 break;
659
660 default:
661 assert_not_reached("Unknown comparison operator.");
662 }
663
664 return verbs[i].dispatch(argv + optind, left);
665}
666
667int main(int argc, char* argv[]) {
668 int r;
669
670 log_parse_environment();
671 log_open();
672
673 r = parse_argv(argc, argv);
674 if (r <= 0)
675 goto finish;
676
677 r = networkctl_main(argc, argv);
678
679finish:
680 pager_close();
681
682 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
683}