]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
networkd: Add bridge port path cost
[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"
d8500c53 37#include "ether-addr-util.h"
ee8c4568
LP
38
39static bool arg_no_pager = false;
40static bool arg_legend = true;
9085f64a 41static bool arg_all = false;
ee8c4568
LP
42
43static void pager_open_if_enabled(void) {
44
45 if (arg_no_pager)
46 return;
47
48 pager_open(false);
49}
50
51static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
52 const char *t;
53 char *p;
54
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
59 * them */
60
61 devtype = udev_device_get_devtype(d);
62 if (streq_ptr(devtype, "wlan"))
63 id = "wlan";
64 else if (streq_ptr(devtype, "wwan"))
65 id = "wwan";
66
67 if (id) {
68 p = strdup(id);
69 if (!p)
70 return -ENOMEM;
71
72 *ret = p;
73 return 1;
74 }
75 }
76
77 t = arphrd_to_name(iftype);
78 if (!t) {
79 *ret = NULL;
80 return 0;
81 }
82
83 p = strdup(t);
84 if (!p)
85 return -ENOMEM;
86
87 ascii_strlower(p);
88 *ret = p;
89
90 return 0;
91}
92
6d0c65ff
LP
93typedef struct LinkInfo {
94 const char *name;
95 int ifindex;
96 unsigned iftype;
97} LinkInfo;
98
99static int link_info_compare(const void *a, const void *b) {
100 const LinkInfo *x = a, *y = b;
101
102 return x->ifindex - y->ifindex;
103}
104
105static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
106 _cleanup_free_ LinkInfo *links = NULL;
107 size_t size = 0, c = 0;
108 sd_rtnl_message *i;
109 int r;
110
111 for (i = m; i; i = sd_rtnl_message_next(i)) {
112 const char *name;
113 unsigned iftype;
114 uint16_t type;
115 int ifindex;
116
117 r = sd_rtnl_message_get_type(i, &type);
118 if (r < 0)
119 return r;
120
121 if (type != RTM_NEWLINK)
122 continue;
123
124 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
125 if (r < 0)
126 return r;
127
128 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
129 if (r < 0)
130 return r;
131
132 r = sd_rtnl_message_link_get_type(i, &iftype);
133 if (r < 0)
134 return r;
135
136 if (!GREEDY_REALLOC(links, size, c+1))
137 return -ENOMEM;
138
139 links[c].name = name;
140 links[c].ifindex = ifindex;
141 links[c].iftype = iftype;
142 c++;
143 }
144
a6a4f528 145 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
146
147 *ret = links;
148 links = NULL;
149
150 return (int) c;
151}
152
d57c365b
LP
153static void operational_state_to_color(const char *state, const char **on, const char **off) {
154 assert(on);
155 assert(off);
156
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();
163 } else
164 *on = *off = "";
165}
166
167static void setup_state_to_color(const char *state, const char **on, const char **off) {
168 assert(on);
169 assert(off);
170
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();
180 } else
181 *on = *off = "";
182}
183
ee8c4568
LP
184static 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;
6d0c65ff
LP
188 _cleanup_free_ LinkInfo *links = NULL;
189 int r, c, i;
ee8c4568
LP
190
191 pager_open_if_enabled();
192
193 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
194 if (r < 0)
195 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568
LP
196
197 udev = udev_new();
4a62c710
MS
198 if (!udev)
199 return log_error_errno(errno, "Failed to connect to udev: %m");
ee8c4568
LP
200
201 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
202 if (r < 0)
203 return rtnl_log_create_error(r);
204
205 r = sd_rtnl_message_request_dump(req, true);
206 if (r < 0)
207 return rtnl_log_create_error(r);
208
209 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
210 if (r < 0)
211 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568
LP
212
213 if (arg_legend)
3e3db0ee 214 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 215
6d0c65ff
LP
216 c = decode_and_sort_links(reply, &links);
217 if (c < 0)
218 return rtnl_log_parse_error(c);
219
220 for (i = 0; i < c; i++) {
ab1525bc 221 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
ee8c4568 222 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
d57c365b
LP
223 const char *on_color_operational, *off_color_operational,
224 *on_color_setup, *off_color_setup;
df3fb561 225 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 226 _cleanup_free_ char *t = NULL;
ee8c4568 227
d6731e4c 228 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
229 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
230
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);
ee8c4568 233
6d0c65ff 234 sprintf(devid, "n%i", links[i].ifindex);
ee8c4568
LP
235 d = udev_device_new_from_device_id(udev, devid);
236
6d0c65ff 237 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 238
3e3db0ee 239 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
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);
ee8c4568
LP
243 }
244
245 if (arg_legend)
6d0c65ff 246 printf("\n%i links listed.\n", c);
ee8c4568
LP
247
248 return 0;
249}
250
c09da729
TG
251/* IEEE Organizationally Unique Identifier vendor string */
252static int ieee_oui(struct udev_hwdb *hwdb, struct ether_addr *mac, char **ret) {
253 struct udev_list_entry *entry;
254 char *description;
d8500c53 255 char str[strlen("OUI:XXYYXXYYXXYY") + 1];
c09da729
TG
256
257 /* skip commonly misused 00:00:00 (Xerox) prefix */
258 if (memcmp(mac, "\0\0\0", 3) == 0)
259 return -EINVAL;
260
d8500c53 261 snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
c09da729
TG
262
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));
266 if (!description)
267 return -ENOMEM;
268
269 *ret = description;
270 return 0;
271 }
272
273 return -ENODATA;
274}
275
276static 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;
279 sd_rtnl_message *m;
280 int r;
281
282 assert(rtnl);
283 assert(ifindex >= 0);
284 assert(family == AF_INET || family == AF_INET6);
285 assert(gateway);
286 assert(gateway_description);
287
288 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
289 if (r < 0)
290 return r;
291
292 r = sd_rtnl_message_request_dump(req, true);
293 if (r < 0)
294 return r;
295
296 r = sd_rtnl_call(rtnl, req, 0, &reply);
297 if (r < 0)
298 return r;
299
300 for (m = reply; m; m = sd_rtnl_message_next(m)) {
301 union in_addr_union gw = {};
302 struct ether_addr mac = {};
303 uint16_t type;
304 int ifi, fam;
305
306 r = sd_rtnl_message_get_errno(m);
307 if (r < 0) {
308 log_error_errno(r, "got error: %m");
309 continue;
310 }
311
312 r = sd_rtnl_message_get_type(m, &type);
313 if (r < 0) {
314 log_error_errno(r, "could not get type: %m");
315 continue;
316 }
317
318 if (type != RTM_NEWNEIGH) {
319 log_error("type is not RTM_NEWNEIGH");
320 continue;
321 }
322
323 r = sd_rtnl_message_neigh_get_family(m, &fam);
324 if (r < 0) {
325 log_error_errno(r, "could not get family: %m");
326 continue;
327 }
328
329 if (fam != family) {
330 log_error("family is not correct");
331 continue;
332 }
333
334 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
335 if (r < 0) {
144232a8 336 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
337 continue;
338 }
339
340 if (ifindex > 0 && ifi != ifindex)
341 continue;
342
343 switch (fam) {
344 case AF_INET:
345 r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
346 if (r < 0)
347 continue;
348
349 break;
350 case AF_INET6:
351 r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
352 if (r < 0)
353 continue;
354
355 break;
356 default:
357 continue;
358 }
359
360 if (!in_addr_equal(fam, &gw, gateway))
361 continue;
362
363 r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
364 if (r < 0)
365 continue;
366
367 r = ieee_oui(hwdb, &mac, gateway_description);
368 if (r < 0)
369 continue;
370
371 return 0;
372 }
373
374 return -ENODATA;
375}
376
377static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
378 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
379 sd_rtnl_message *m;
380 bool first = true;
381 int r;
382
383 assert(rtnl);
384 assert(ifindex >= 0);
385
386 r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, AF_UNSPEC, RTPROT_UNSPEC);
387 if (r < 0)
388 return r;
389
390 r = sd_rtnl_message_request_dump(req, true);
391 if (r < 0)
392 return r;
393
394 r = sd_rtnl_call(rtnl, req, 0, &reply);
395 if (r < 0)
396 return r;
397
398 for (m = reply; m; m = sd_rtnl_message_next(m)) {
399 _cleanup_free_ char *gateway = NULL, *gateway_description = NULL;
400 union in_addr_union gw = {};
401 uint16_t type;
402 uint32_t ifi;
403 int family;
404
405 r = sd_rtnl_message_get_errno(m);
406 if (r < 0) {
407 log_error_errno(r, "got error: %m");
408 continue;
409 }
410
411 r = sd_rtnl_message_get_type(m, &type);
412 if (r < 0) {
413 log_error_errno(r, "could not get type: %m");
414 continue;
415 }
416
417 if (type != RTM_NEWROUTE) {
418 log_error("type is not RTM_NEWROUTE");
419 continue;
420 }
421
422 r = sd_rtnl_message_route_get_family(m, &family);
423 if (r < 0) {
424 log_error_errno(r, "could not get family: %m");
425 continue;
426 }
427
428 r = sd_rtnl_message_read_u32(m, RTA_OIF, &ifi);
429 if (r < 0) {
144232a8 430 log_error_errno(r, "could not get RTA_OIF: %m");
c09da729
TG
431 continue;
432 }
433
434 if (ifindex > 0 && ifi != (unsigned) ifindex)
435 continue;
436
437 switch (family) {
438 case AF_INET:
439 r = sd_rtnl_message_read_in_addr(m, RTA_GATEWAY, &gw.in);
440 if (r < 0)
441 continue;
442
443 r = sd_rtnl_message_read_in_addr(m, RTA_DST, NULL);
444 if (r >= 0)
445 continue;
446
447 r = sd_rtnl_message_read_in_addr(m, RTA_SRC, NULL);
448 if (r >= 0)
449 continue;
450
451 break;
452 case AF_INET6:
453 r = sd_rtnl_message_read_in6_addr(m, RTA_GATEWAY, &gw.in6);
454 if (r < 0)
455 continue;
456
457 r = sd_rtnl_message_read_in6_addr(m, RTA_DST, NULL);
458 if (r >= 0)
459 continue;
460
461 r = sd_rtnl_message_read_in6_addr(m, RTA_SRC, NULL);
462 if (r >= 0)
463 continue;
464
465 break;
466 default:
467 continue;
468 }
469
470 r = in_addr_to_string(family, &gw, &gateway);
471 if (r < 0)
472 continue;
473
474 r = get_gateway_description(rtnl, hwdb, ifi, family, &gw, &gateway_description);
475 if (r < 0)
476 log_debug("could not get description of gateway: %s", strerror(-r));
477
478 if (gateway_description)
479 printf("%*s%s (%s)\n",
480 (int) strlen(prefix),
481 first ? prefix : "",
482 gateway, gateway_description);
483 else
484 printf("%*s%s\n",
485 (int) strlen(prefix),
486 first ? prefix : "",
487 gateway);
488
489 first = false;
490 }
491
492 return 0;
493}
494
ee8c4568
LP
495static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
496 _cleanup_free_ struct local_address *local = NULL;
497 int r, n, i;
498
1d050e1e 499 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
500 if (n < 0)
501 return n;
502
503 for (i = 0; i < n; i++) {
504 _cleanup_free_ char *pretty = NULL;
505
506 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
507 if (r < 0)
508 return r;
509
510 printf("%*s%s\n",
511 (int) strlen(prefix),
512 i == 0 ? prefix : "",
513 pretty);
514 }
515
516 return 0;
517}
518
519static void dump_list(const char *prefix, char **l) {
520 char **i;
521
522 STRV_FOREACH(i, l) {
523 printf("%*s%s\n",
524 (int) strlen(prefix),
525 i == l ? prefix : "",
526 *i);
527 }
528}
529
9085f64a 530static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
2301cb9f 531 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
6c03d27d 532 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
533 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
534 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
c09da729 535 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
9085f64a 536 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 537 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 538 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
539 const char *on_color_operational, *off_color_operational,
540 *on_color_setup, *off_color_setup;
9085f64a
LP
541 struct ether_addr e;
542 unsigned iftype;
543 int r, ifindex;
544 bool have_mac;
545 uint32_t mtu;
546
547 assert(rtnl);
548 assert(udev);
549 assert(name);
550
551 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
552 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
553 else {
554 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
555 if (r < 0)
556 return rtnl_log_create_error(r);
557
558 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
559 }
560
561 if (r < 0)
562 return rtnl_log_create_error(r);
563
564 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
565 if (r < 0)
566 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
567
568 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
569 if (r < 0)
570 return rtnl_log_parse_error(r);
571
572 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
573 if (r < 0)
574 return rtnl_log_parse_error(r);
575
576 r = sd_rtnl_message_link_get_type(reply, &iftype);
577 if (r < 0)
578 return rtnl_log_parse_error(r);
579
580 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
581
582 if (have_mac) {
583 const uint8_t *p;
584 bool all_zeroes = true;
585
586 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
587 if (*p != 0) {
588 all_zeroes = false;
589 break;
590 }
591
592 if (all_zeroes)
593 have_mac = false;
594 }
595
596 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
597
d6731e4c 598 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
599 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
600
601 sd_network_link_get_setup_state(ifindex, &setup_state);
602 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 603
d6731e4c
TG
604 sd_network_link_get_dns(ifindex, &dns);
605 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 606 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
607 r = sd_network_link_get_wildcard_domain(ifindex);
608 if (r > 0) {
609 char *wildcard;
610
611 wildcard = strdup("*");
1405434b
LP
612 if (!wildcard)
613 return log_oom();
614
615 if (strv_consume(&domains, wildcard) < 0)
616 return log_oom();
67272d15 617 }
9085f64a
LP
618
619 sprintf(devid, "n%i", ifindex);
620 d = udev_device_new_from_device_id(udev, devid);
621
622 link_get_type_string(iftype, d, &t);
623
624 if (d) {
af5effc4 625 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
9085f64a
LP
626 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
627 path = udev_device_get_property_value(d, "ID_PATH");
628
629 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
630 if (!vendor)
631 vendor = udev_device_get_property_value(d, "ID_VENDOR");
632
633 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
634 if (!model)
635 model = udev_device_get_property_value(d, "ID_MODEL");
636 }
637
373d9f17 638 sd_network_link_get_network_file(ifindex, &network);
df3fb561 639
d57c365b 640 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 641
af5effc4
TG
642 printf(" Link File: %s\n"
643 "Network File: %s\n"
373d9f17 644 " Type: %s\n"
d57c365b 645 " State: %s%s%s (%s%s%s)\n",
af5effc4 646 strna(link),
373d9f17 647 strna(network),
9085f64a 648 strna(t),
d57c365b
LP
649 on_color_operational, strna(operational_state), off_color_operational,
650 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
651
652 if (path)
653 printf(" Path: %s\n", path);
654 if (driver)
655 printf(" Driver: %s\n", driver);
656 if (vendor)
657 printf(" Vendor: %s\n", vendor);
658 if (model)
659 printf(" Model: %s\n", model);
660
db73295a
LP
661 if (have_mac) {
662 char ea[ETHER_ADDR_TO_STRING_MAX];
663 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
664 }
9085f64a
LP
665
666 if (mtu > 0)
667 printf(" MTU: %u\n", mtu);
668
c09da729
TG
669 hwdb = udev_hwdb_new(udev);
670
671 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
672
9085f64a
LP
673 dump_addresses(rtnl, " Address: ", ifindex);
674
675 if (!strv_isempty(dns))
676 dump_list(" DNS: ", dns);
2301cb9f 677 if (!strv_isempty(domains))
c627729f 678 dump_list(" Domain: ", domains);
9085f64a
LP
679 if (!strv_isempty(ntp))
680 dump_list(" NTP: ", ntp);
681
682 return 0;
683}
684
ee8c4568
LP
685static int link_status(char **args, unsigned n) {
686 _cleanup_udev_unref_ struct udev *udev = NULL;
687 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
688 char **name;
689 int r;
690
f7d68aa8 691 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
692 if (r < 0)
693 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8
LP
694
695 udev = udev_new();
4a62c710
MS
696 if (!udev)
697 return log_error_errno(errno, "Failed to connect to udev: %m");
f7d68aa8 698
9085f64a 699 if (n <= 1 && !arg_all) {
ee8c4568 700 _cleanup_free_ char *operational_state = NULL;
c627729f 701 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
f7d68aa8 702 _cleanup_free_ struct local_address *addresses = NULL;
e92da1e5 703 const char *on_color_operational, *off_color_operational;
f7d68aa8 704 int i, c;
ee8c4568 705
03cc0fd1 706 sd_network_get_operational_state(&operational_state);
e92da1e5 707 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 708
e92da1e5 709 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 710
1d050e1e 711 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
f7d68aa8
LP
712 for (i = 0; i < c; i++) {
713 _cleanup_free_ char *pretty = NULL;
714
715 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
716 if (r < 0)
717 return log_oom();
718
719 printf("%13s %s\n",
720 i > 0 ? "" : "Address:", pretty);
721 }
722
03cc0fd1
LP
723 sd_network_get_dns(&dns);
724 if (!strv_isempty(dns))
725 dump_list(" DNS: ", dns);
726
c627729f
LP
727 sd_network_get_domains(&domains);
728 if (!strv_isempty(domains))
729 dump_list(" Domain: ", domains);
730
ddb7f7fc 731 sd_network_get_ntp(&ntp);
03cc0fd1
LP
732 if (!strv_isempty(ntp))
733 dump_list(" NTP: ", ntp);
ee8c4568 734
ee8c4568
LP
735 return 0;
736 }
737
738 pager_open_if_enabled();
739
9085f64a 740 if (arg_all) {
ee8c4568 741 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
742 _cleanup_free_ LinkInfo *links = NULL;
743 int c, i;
ee8c4568 744
9085f64a
LP
745 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
746 if (r < 0)
747 return rtnl_log_create_error(r);
ee8c4568 748
9085f64a 749 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
750 if (r < 0)
751 return rtnl_log_create_error(r);
752
753 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
754 if (r < 0)
755 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 756
6d0c65ff
LP
757 c = decode_and_sort_links(reply, &links);
758 if (c < 0)
759 return rtnl_log_parse_error(c);
ee8c4568 760
6d0c65ff
LP
761 for (i = 0; i < c; i++) {
762 if (i > 0)
9085f64a 763 fputc('\n', stdout);
ee8c4568 764
6d0c65ff 765 link_status_one(rtnl, udev, links[i].name);
ee8c4568 766 }
9085f64a 767 }
ee8c4568 768
9085f64a
LP
769 STRV_FOREACH(name, args + 1) {
770 if (name != args+1)
771 fputc('\n', stdout);
ee8c4568 772
9085f64a 773 link_status_one(rtnl, udev, *name);
ee8c4568
LP
774 }
775
776 return 0;
777}
778
779static void help(void) {
780 printf("%s [OPTIONS...]\n\n"
781 "Query and control the networking subsystem.\n\n"
782 " -h --help Show this help\n"
9085f64a
LP
783 " --version Show package version\n"
784 " --no-pager Do not pipe output into a pager\n"
785 " --no-legend Do not show the headers and footers\n"
786 " -a --all Show status for all links\n\n"
ee8c4568
LP
787 "Commands:\n"
788 " list List links\n"
789 " status LINK Show link status\n"
790 , program_invocation_short_name);
791}
792
793static int parse_argv(int argc, char *argv[]) {
794
795 enum {
796 ARG_VERSION = 0x100,
797 ARG_NO_PAGER,
798 ARG_NO_LEGEND,
799 };
800
801 static const struct option options[] = {
802 { "help", no_argument, NULL, 'h' },
803 { "version", no_argument, NULL, ARG_VERSION },
804 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
805 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 806 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
807 {}
808 };
809
810 int c;
811
812 assert(argc >= 0);
813 assert(argv);
814
9085f64a 815 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
816
817 switch (c) {
818
819 case 'h':
820 help();
821 return 0;
822
823 case ARG_VERSION:
824 puts(PACKAGE_STRING);
825 puts(SYSTEMD_FEATURES);
826 return 0;
827
828 case ARG_NO_PAGER:
829 arg_no_pager = true;
830 break;
831
832 case ARG_NO_LEGEND:
833 arg_legend = false;
834 break;
835
9085f64a
LP
836 case 'a':
837 arg_all = true;
838 break;
839
ee8c4568
LP
840 case '?':
841 return -EINVAL;
842
843 default:
844 assert_not_reached("Unhandled option");
845 }
846 }
847
848 return 1;
849}
850
851static int networkctl_main(int argc, char *argv[]) {
852
853 static const struct {
854 const char* verb;
855 const enum {
856 MORE,
857 LESS,
858 EQUAL
859 } argc_cmp;
860 const int argc;
861 int (* const dispatch)(char **args, unsigned n);
862 } verbs[] = {
863 { "list", LESS, 1, list_links },
864 { "status", MORE, 1, link_status },
865 };
866
867 int left;
868 unsigned i;
869
870 assert(argc >= 0);
871 assert(argv);
872
873 left = argc - optind;
874
875 if (left <= 0)
876 /* Special rule: no arguments means "list" */
877 i = 0;
878 else {
879 if (streq(argv[optind], "help")) {
880 help();
881 return 0;
882 }
883
884 for (i = 0; i < ELEMENTSOF(verbs); i++)
885 if (streq(argv[optind], verbs[i].verb))
886 break;
887
888 if (i >= ELEMENTSOF(verbs)) {
889 log_error("Unknown operation %s", argv[optind]);
890 return -EINVAL;
891 }
892 }
893
894 switch (verbs[i].argc_cmp) {
895
896 case EQUAL:
897 if (left != verbs[i].argc) {
898 log_error("Invalid number of arguments.");
899 return -EINVAL;
900 }
901
902 break;
903
904 case MORE:
905 if (left < verbs[i].argc) {
906 log_error("Too few arguments.");
907 return -EINVAL;
908 }
909
910 break;
911
912 case LESS:
913 if (left > verbs[i].argc) {
914 log_error("Too many arguments.");
915 return -EINVAL;
916 }
917
918 break;
919
920 default:
921 assert_not_reached("Unknown comparison operator.");
922 }
923
924 return verbs[i].dispatch(argv + optind, left);
925}
926
927int main(int argc, char* argv[]) {
928 int r;
929
930 log_parse_environment();
931 log_open();
932
933 r = parse_argv(argc, argv);
934 if (r <= 0)
935 goto finish;
936
937 r = networkctl_main(argc, argv);
938
939finish:
940 pager_close();
941
942 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
943}