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