]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
sd-rtnl: route - allow GETROUTE with AF_UNSPEC
[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"
db73295a 36#include "socket-util.h"
ee8c4568
LP
37
38static bool arg_no_pager = false;
39static bool arg_legend = true;
9085f64a 40static bool arg_all = false;
ee8c4568
LP
41
42static void pager_open_if_enabled(void) {
43
44 if (arg_no_pager)
45 return;
46
47 pager_open(false);
48}
49
50static 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
6d0c65ff
LP
92typedef struct LinkInfo {
93 const char *name;
94 int ifindex;
95 unsigned iftype;
96} LinkInfo;
97
98static 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
104static 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
a6a4f528 144 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
145
146 *ret = links;
147 links = NULL;
148
149 return (int) c;
150}
151
d57c365b
LP
152static 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
166static 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
ee8c4568
LP
183static 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;
6d0c65ff
LP
187 _cleanup_free_ LinkInfo *links = NULL;
188 int r, c, i;
ee8c4568
LP
189
190 pager_open_if_enabled();
191
192 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
193 if (r < 0)
194 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568
LP
195
196 udev = udev_new();
4a62c710
MS
197 if (!udev)
198 return log_error_errno(errno, "Failed to connect to udev: %m");
ee8c4568
LP
199
200 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
201 if (r < 0)
202 return rtnl_log_create_error(r);
203
204 r = sd_rtnl_message_request_dump(req, true);
205 if (r < 0)
206 return rtnl_log_create_error(r);
207
208 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
209 if (r < 0)
210 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568
LP
211
212 if (arg_legend)
3e3db0ee 213 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 214
6d0c65ff
LP
215 c = decode_and_sort_links(reply, &links);
216 if (c < 0)
217 return rtnl_log_parse_error(c);
218
219 for (i = 0; i < c; i++) {
ab1525bc 220 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
ee8c4568 221 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
d57c365b
LP
222 const char *on_color_operational, *off_color_operational,
223 *on_color_setup, *off_color_setup;
df3fb561 224 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 225 _cleanup_free_ char *t = NULL;
ee8c4568 226
d6731e4c 227 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
228 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
229
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);
ee8c4568 232
6d0c65ff 233 sprintf(devid, "n%i", links[i].ifindex);
ee8c4568
LP
234 d = udev_device_new_from_device_id(udev, devid);
235
6d0c65ff 236 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 237
3e3db0ee 238 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
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);
ee8c4568
LP
242 }
243
244 if (arg_legend)
6d0c65ff 245 printf("\n%i links listed.\n", c);
ee8c4568
LP
246
247 return 0;
248}
249
250static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
251 _cleanup_free_ struct local_address *local = NULL;
252 int r, n, i;
253
254 n = local_addresses(rtnl, ifindex, &local);
255 if (n < 0)
256 return n;
257
258 for (i = 0; i < n; i++) {
259 _cleanup_free_ char *pretty = NULL;
260
261 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
262 if (r < 0)
263 return r;
264
265 printf("%*s%s\n",
266 (int) strlen(prefix),
267 i == 0 ? prefix : "",
268 pretty);
269 }
270
271 return 0;
272}
273
274static void dump_list(const char *prefix, char **l) {
275 char **i;
276
277 STRV_FOREACH(i, l) {
278 printf("%*s%s\n",
279 (int) strlen(prefix),
280 i == l ? prefix : "",
281 *i);
282 }
283}
284
9085f64a 285static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
2301cb9f 286 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
ab1525bc 287 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
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)];
373d9f17 291 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 292 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
293 const char *on_color_operational, *off_color_operational,
294 *on_color_setup, *off_color_setup;
9085f64a
LP
295 struct ether_addr e;
296 unsigned iftype;
297 int r, ifindex;
298 bool have_mac;
299 uint32_t mtu;
300
301 assert(rtnl);
302 assert(udev);
303 assert(name);
304
305 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
306 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
307 else {
308 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
309 if (r < 0)
310 return rtnl_log_create_error(r);
311
312 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
313 }
314
315 if (r < 0)
316 return rtnl_log_create_error(r);
317
318 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
319 if (r < 0)
320 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
321
322 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
323 if (r < 0)
324 return rtnl_log_parse_error(r);
325
326 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
327 if (r < 0)
328 return rtnl_log_parse_error(r);
329
330 r = sd_rtnl_message_link_get_type(reply, &iftype);
331 if (r < 0)
332 return rtnl_log_parse_error(r);
333
334 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
335
336 if (have_mac) {
337 const uint8_t *p;
338 bool all_zeroes = true;
339
340 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
341 if (*p != 0) {
342 all_zeroes = false;
343 break;
344 }
345
346 if (all_zeroes)
347 have_mac = false;
348 }
349
350 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
351
d6731e4c 352 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
353 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
354
355 sd_network_link_get_setup_state(ifindex, &setup_state);
356 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 357
d6731e4c
TG
358 sd_network_link_get_dns(ifindex, &dns);
359 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 360 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
361 r = sd_network_link_get_wildcard_domain(ifindex);
362 if (r > 0) {
363 char *wildcard;
364
365 wildcard = strdup("*");
1405434b
LP
366 if (!wildcard)
367 return log_oom();
368
369 if (strv_consume(&domains, wildcard) < 0)
370 return log_oom();
67272d15 371 }
9085f64a
LP
372
373 sprintf(devid, "n%i", ifindex);
374 d = udev_device_new_from_device_id(udev, devid);
375
376 link_get_type_string(iftype, d, &t);
377
378 if (d) {
af5effc4 379 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
9085f64a
LP
380 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
381 path = udev_device_get_property_value(d, "ID_PATH");
382
383 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
384 if (!vendor)
385 vendor = udev_device_get_property_value(d, "ID_VENDOR");
386
387 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
388 if (!model)
389 model = udev_device_get_property_value(d, "ID_MODEL");
390 }
391
373d9f17 392 sd_network_link_get_network_file(ifindex, &network);
df3fb561 393
d57c365b 394 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 395
af5effc4
TG
396 printf(" Link File: %s\n"
397 "Network File: %s\n"
373d9f17 398 " Type: %s\n"
d57c365b 399 " State: %s%s%s (%s%s%s)\n",
af5effc4 400 strna(link),
373d9f17 401 strna(network),
9085f64a 402 strna(t),
d57c365b
LP
403 on_color_operational, strna(operational_state), off_color_operational,
404 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
405
406 if (path)
407 printf(" Path: %s\n", path);
408 if (driver)
409 printf(" Driver: %s\n", driver);
410 if (vendor)
411 printf(" Vendor: %s\n", vendor);
412 if (model)
413 printf(" Model: %s\n", model);
414
db73295a
LP
415 if (have_mac) {
416 char ea[ETHER_ADDR_TO_STRING_MAX];
417 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
418 }
9085f64a
LP
419
420 if (mtu > 0)
421 printf(" MTU: %u\n", mtu);
422
423 dump_addresses(rtnl, " Address: ", ifindex);
424
425 if (!strv_isempty(dns))
426 dump_list(" DNS: ", dns);
2301cb9f 427 if (!strv_isempty(domains))
c627729f 428 dump_list(" Domain: ", domains);
9085f64a
LP
429 if (!strv_isempty(ntp))
430 dump_list(" NTP: ", ntp);
431
432 return 0;
433}
434
ee8c4568
LP
435static int link_status(char **args, unsigned n) {
436 _cleanup_udev_unref_ struct udev *udev = NULL;
437 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
438 char **name;
439 int r;
440
f7d68aa8 441 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
442 if (r < 0)
443 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8
LP
444
445 udev = udev_new();
4a62c710
MS
446 if (!udev)
447 return log_error_errno(errno, "Failed to connect to udev: %m");
f7d68aa8 448
9085f64a 449 if (n <= 1 && !arg_all) {
ee8c4568 450 _cleanup_free_ char *operational_state = NULL;
c627729f 451 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
f7d68aa8 452 _cleanup_free_ struct local_address *addresses = NULL;
e92da1e5 453 const char *on_color_operational, *off_color_operational;
f7d68aa8 454 int i, c;
ee8c4568 455
03cc0fd1 456 sd_network_get_operational_state(&operational_state);
e92da1e5 457 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 458
e92da1e5 459 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 460
f7d68aa8
LP
461 c = local_addresses(rtnl, 0, &addresses);
462 for (i = 0; i < c; i++) {
463 _cleanup_free_ char *pretty = NULL;
464
465 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
466 if (r < 0)
467 return log_oom();
468
469 printf("%13s %s\n",
470 i > 0 ? "" : "Address:", pretty);
471 }
472
03cc0fd1
LP
473 sd_network_get_dns(&dns);
474 if (!strv_isempty(dns))
475 dump_list(" DNS: ", dns);
476
c627729f
LP
477 sd_network_get_domains(&domains);
478 if (!strv_isempty(domains))
479 dump_list(" Domain: ", domains);
480
ddb7f7fc 481 sd_network_get_ntp(&ntp);
03cc0fd1
LP
482 if (!strv_isempty(ntp))
483 dump_list(" NTP: ", ntp);
ee8c4568 484
ee8c4568
LP
485 return 0;
486 }
487
488 pager_open_if_enabled();
489
9085f64a 490 if (arg_all) {
ee8c4568 491 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
492 _cleanup_free_ LinkInfo *links = NULL;
493 int c, i;
ee8c4568 494
9085f64a
LP
495 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
496 if (r < 0)
497 return rtnl_log_create_error(r);
ee8c4568 498
9085f64a 499 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
500 if (r < 0)
501 return rtnl_log_create_error(r);
502
503 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
504 if (r < 0)
505 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 506
6d0c65ff
LP
507 c = decode_and_sort_links(reply, &links);
508 if (c < 0)
509 return rtnl_log_parse_error(c);
ee8c4568 510
6d0c65ff
LP
511 for (i = 0; i < c; i++) {
512 if (i > 0)
9085f64a 513 fputc('\n', stdout);
ee8c4568 514
6d0c65ff 515 link_status_one(rtnl, udev, links[i].name);
ee8c4568 516 }
9085f64a 517 }
ee8c4568 518
9085f64a
LP
519 STRV_FOREACH(name, args + 1) {
520 if (name != args+1)
521 fputc('\n', stdout);
ee8c4568 522
9085f64a 523 link_status_one(rtnl, udev, *name);
ee8c4568
LP
524 }
525
526 return 0;
527}
528
529static 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"
9085f64a
LP
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"
ee8c4568
LP
537 "Commands:\n"
538 " list List links\n"
539 " status LINK Show link status\n"
540 , program_invocation_short_name);
541}
542
543static 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 },
9085f64a 556 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
557 {}
558 };
559
560 int c;
561
562 assert(argc >= 0);
563 assert(argv);
564
9085f64a 565 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
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
9085f64a
LP
586 case 'a':
587 arg_all = true;
588 break;
589
ee8c4568
LP
590 case '?':
591 return -EINVAL;
592
593 default:
594 assert_not_reached("Unhandled option");
595 }
596 }
597
598 return 1;
599}
600
601static 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
677int 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
689finish:
690 pager_close();
691
692 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
693}