]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
Merge pull request #10407 from yuwata/netlink-slot
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ee8c4568 2
ee8c4568 3#include <getopt.h>
d37b7627 4#include <linux/if_addrlabel.h>
1693a943 5#include <net/if.h>
3f6fd1ba 6#include <stdbool.h>
ee8c4568 7
914d6c09 8#include "sd-device.h"
3f6fd1ba 9#include "sd-hwdb.h"
34437b4f 10#include "sd-lldp.h"
3f6fd1ba
LP
11#include "sd-netlink.h"
12#include "sd-network.h"
ee8c4568 13
b5efdb8a 14#include "alloc-util.h"
3f6fd1ba 15#include "arphrd-list.h"
914d6c09 16#include "device-util.h"
3f6fd1ba 17#include "ether-addr-util.h"
34437b4f 18#include "fd-util.h"
81fd1dd3 19#include "hwdb-util.h"
ee8c4568 20#include "local-addresses.h"
8752c575 21#include "locale-util.h"
d37b7627 22#include "macro.h"
3f6fd1ba
LP
23#include "netlink-util.h"
24#include "pager.h"
6bedfcbb 25#include "parse-util.h"
db73295a 26#include "socket-util.h"
34437b4f 27#include "sparse-endian.h"
d054f0a4 28#include "stdio-util.h"
8b43440b 29#include "string-table.h"
8752c575 30#include "string-util.h"
3f6fd1ba 31#include "strv.h"
2388b2f4 32#include "strxcpyx.h"
288a74cc 33#include "terminal-util.h"
3f6fd1ba
LP
34#include "util.h"
35#include "verbs.h"
ee8c4568
LP
36
37static bool arg_no_pager = false;
38static bool arg_legend = true;
9085f64a 39static bool arg_all = false;
ee8c4568 40
b55822c3 41static char *link_get_type_string(unsigned short iftype, sd_device *d) {
2c740afd 42 const char *t, *devtype;
ee8c4568
LP
43 char *p;
44
2c740afd
YW
45 if (d &&
46 sd_device_get_devtype(d, &devtype) >= 0 &&
47 !isempty(devtype))
48 return strdup(devtype);
ee8c4568
LP
49
50 t = arphrd_to_name(iftype);
b55822c3
JD
51 if (!t)
52 return NULL;
ee8c4568
LP
53
54 p = strdup(t);
55 if (!p)
b55822c3 56 return NULL;
ee8c4568
LP
57
58 ascii_strlower(p);
b55822c3 59 return p;
ee8c4568
LP
60}
61
7e5a080a
LP
62static void operational_state_to_color(const char *state, const char **on, const char **off) {
63 assert(on);
64 assert(off);
65
66 if (streq_ptr(state, "routable")) {
67 *on = ansi_highlight_green();
68 *off = ansi_normal();
69 } else if (streq_ptr(state, "degraded")) {
70 *on = ansi_highlight_yellow();
71 *off = ansi_normal();
72 } else
73 *on = *off = "";
74}
75
76static void setup_state_to_color(const char *state, const char **on, const char **off) {
77 assert(on);
78 assert(off);
79
80 if (streq_ptr(state, "configured")) {
81 *on = ansi_highlight_green();
82 *off = ansi_normal();
83 } else if (streq_ptr(state, "configuring")) {
84 *on = ansi_highlight_yellow();
85 *off = ansi_normal();
1cf03a4f 86 } else if (STRPTR_IN_SET(state, "failed", "linger")) {
7e5a080a
LP
87 *on = ansi_highlight_red();
88 *off = ansi_normal();
89 } else
90 *on = *off = "";
91}
92
6d0c65ff 93typedef struct LinkInfo {
e997c4b0 94 char name[IFNAMSIZ+1];
6d0c65ff 95 int ifindex;
1c4a6088 96 unsigned short iftype;
b147503e
LP
97 struct ether_addr mac_address;
98 uint32_t mtu;
99
100 bool has_mac_address:1;
101 bool has_mtu:1;
6d0c65ff
LP
102} LinkInfo;
103
93bab288
YW
104static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
105 return CMP(a->ifindex, b->ifindex);
6d0c65ff
LP
106}
107
7e5a080a 108static int decode_link(sd_netlink_message *m, LinkInfo *info) {
e997c4b0 109 const char *name;
e997c4b0 110 uint16_t type;
b147503e 111 int r;
6d0c65ff 112
e997c4b0
LP
113 assert(m);
114 assert(info);
6d0c65ff 115
e997c4b0
LP
116 r = sd_netlink_message_get_type(m, &type);
117 if (r < 0)
118 return r;
6d0c65ff 119
e997c4b0
LP
120 if (type != RTM_NEWLINK)
121 return 0;
6d0c65ff 122
b147503e 123 r = sd_rtnl_message_link_get_ifindex(m, &info->ifindex);
e997c4b0
LP
124 if (r < 0)
125 return r;
6d0c65ff 126
e997c4b0
LP
127 r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
128 if (r < 0)
129 return r;
6d0c65ff 130
b147503e 131 r = sd_rtnl_message_link_get_type(m, &info->iftype);
e997c4b0
LP
132 if (r < 0)
133 return r;
6d0c65ff 134
2388b2f4 135 strscpy(info->name, sizeof info->name, name);
b147503e
LP
136
137 info->has_mac_address =
138 sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 &&
72e551f4 139 memcmp(&info->mac_address, &ETHER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
b147503e
LP
140
141 info->has_mtu =
142 sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) &&
143 info->mtu > 0;
e997c4b0
LP
144
145 return 1;
146}
147
b147503e 148static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) {
e997c4b0 149 _cleanup_free_ LinkInfo *links = NULL;
e997c4b0
LP
150 char **i;
151 size_t c = 0;
152 int r;
153
b147503e 154 assert(rtnl);
e997c4b0
LP
155 assert(ret);
156
157 links = new(LinkInfo, strv_length(l));
158 if (!links)
159 return log_oom();
160
e997c4b0
LP
161 STRV_FOREACH(i, l) {
162 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
163 int ifindex;
164
165 if (parse_ifindex(*i, &ifindex) >= 0)
166 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
167 else {
168 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
169 if (r < 0)
170 return rtnl_log_create_error(r);
171
172 r = sd_netlink_message_append_string(req, IFLA_IFNAME, *i);
173 }
174 if (r < 0)
175 return rtnl_log_create_error(r);
176
177 r = sd_netlink_call(rtnl, req, 0, &reply);
178 if (r < 0)
179 return log_error_errno(r, "Failed to request link: %m");
180
7e5a080a 181 r = decode_link(reply, links + c);
e997c4b0
LP
182 if (r < 0)
183 return r;
184 if (r > 0)
185 c++;
186 }
187
93bab288 188 typesafe_qsort(links, c, link_info_compare);
e997c4b0 189
1cc6c93a 190 *ret = TAKE_PTR(links);
e997c4b0
LP
191
192 return (int) c;
193}
194
b147503e 195static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) {
4afd3348 196 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
7e5a080a
LP
197 _cleanup_free_ LinkInfo *links = NULL;
198 size_t allocated = 0, c = 0;
199 sd_netlink_message *i;
7d367b45
LP
200 int r;
201
b147503e 202 assert(rtnl);
7d367b45 203 assert(ret);
ee8c4568 204
ee8c4568
LP
205 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
206 if (r < 0)
207 return rtnl_log_create_error(r);
208
1c4baffc 209 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
210 if (r < 0)
211 return rtnl_log_create_error(r);
212
1c4baffc 213 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
214 if (r < 0)
215 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 216
7e5a080a
LP
217 for (i = reply; i; i = sd_netlink_message_next(i)) {
218 if (!GREEDY_REALLOC(links, allocated, c+1))
219 return -ENOMEM;
7d367b45 220
7e5a080a
LP
221 r = decode_link(i, links + c);
222 if (r < 0)
223 return r;
224 if (r > 0)
225 c++;
226 }
227
93bab288 228 typesafe_qsort(links, c, link_info_compare);
7e5a080a 229
1cc6c93a 230 *ret = TAKE_PTR(links);
7e5a080a
LP
231
232 return (int) c;
7d367b45 233}
ee8c4568 234
7d367b45 235static int list_links(int argc, char *argv[], void *userdata) {
b147503e 236 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
7d367b45 237 _cleanup_free_ LinkInfo *links = NULL;
b147503e
LP
238 int c, i, r;
239
240 r = sd_netlink_open(&rtnl);
241 if (r < 0)
242 return log_error_errno(r, "Failed to connect to netlink: %m");
7d367b45 243
e997c4b0 244 if (argc > 1)
b147503e 245 c = acquire_link_info_strv(rtnl, argv + 1, &links);
e997c4b0 246 else
b147503e 247 c = acquire_link_info_all(rtnl, &links);
6d0c65ff 248 if (c < 0)
7d367b45
LP
249 return c;
250
ee5324aa 251 (void) pager_open(arg_no_pager, false);
7d367b45
LP
252
253 if (arg_legend)
254 printf("%3s %-16s %-18s %-11s %-10s\n",
255 "IDX",
256 "LINK",
257 "TYPE",
258 "OPERATIONAL",
259 "SETUP");
6d0c65ff
LP
260
261 for (i = 0; i < c; i++) {
ab1525bc 262 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
4afd3348 263 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
d57c365b
LP
264 const char *on_color_operational, *off_color_operational,
265 *on_color_setup, *off_color_setup;
7d6884b6 266 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 267 _cleanup_free_ char *t = NULL;
ee8c4568 268
4abd866d 269 (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
270 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
271
33d5013d
LP
272 r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
273 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
274 setup_state = strdup("unmanaged");
d57c365b 275 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
ee8c4568 276
691d4da9
LP
277 xsprintf(devid, "n%i", links[i].ifindex);
278 (void) sd_device_new_from_device_id(&d, devid);
ee8c4568 279
b55822c3 280 t = link_get_type_string(links[i].iftype, d);
ee8c4568 281
3e3db0ee 282 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
283 links[i].ifindex, links[i].name, strna(t),
284 on_color_operational, strna(operational_state), off_color_operational,
285 on_color_setup, strna(setup_state), off_color_setup);
ee8c4568
LP
286 }
287
288 if (arg_legend)
6d0c65ff 289 printf("\n%i links listed.\n", c);
ee8c4568
LP
290
291 return 0;
292}
293
c09da729 294/* IEEE Organizationally Unique Identifier vendor string */
b147503e 295static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
81fd1dd3 296 const char *description;
fbd0b64f 297 char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
81fd1dd3
TG
298 int r;
299
300 assert(ret);
c09da729 301
888943fc
LP
302 if (!hwdb)
303 return -EINVAL;
304
81fd1dd3
TG
305 if (!mac)
306 return -EINVAL;
307
c09da729
TG
308 /* skip commonly misused 00:00:00 (Xerox) prefix */
309 if (memcmp(mac, "\0\0\0", 3) == 0)
310 return -EINVAL;
311
d054f0a4
DM
312 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
313 ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 314
81fd1dd3
TG
315 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
316 if (r < 0)
317 return r;
c09da729 318
81fd1dd3
TG
319 desc = strdup(description);
320 if (!desc)
321 return -ENOMEM;
c09da729 322
81fd1dd3
TG
323 *ret = desc;
324
325 return 0;
c09da729
TG
326}
327
69fb1176 328static int get_gateway_description(
1c4baffc 329 sd_netlink *rtnl,
81fd1dd3 330 sd_hwdb *hwdb,
69fb1176
LP
331 int ifindex,
332 int family,
333 union in_addr_union *gateway,
334 char **gateway_description) {
4afd3348 335 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
1c4baffc 336 sd_netlink_message *m;
c09da729
TG
337 int r;
338
339 assert(rtnl);
340 assert(ifindex >= 0);
4c701096 341 assert(IN_SET(family, AF_INET, AF_INET6));
c09da729
TG
342 assert(gateway);
343 assert(gateway_description);
344
345 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
346 if (r < 0)
347 return r;
348
1c4baffc 349 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
350 if (r < 0)
351 return r;
352
1c4baffc 353 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
354 if (r < 0)
355 return r;
356
1c4baffc 357 for (m = reply; m; m = sd_netlink_message_next(m)) {
c09da729
TG
358 union in_addr_union gw = {};
359 struct ether_addr mac = {};
360 uint16_t type;
361 int ifi, fam;
362
1c4baffc 363 r = sd_netlink_message_get_errno(m);
c09da729
TG
364 if (r < 0) {
365 log_error_errno(r, "got error: %m");
366 continue;
367 }
368
1c4baffc 369 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
370 if (r < 0) {
371 log_error_errno(r, "could not get type: %m");
372 continue;
373 }
374
375 if (type != RTM_NEWNEIGH) {
376 log_error("type is not RTM_NEWNEIGH");
377 continue;
378 }
379
380 r = sd_rtnl_message_neigh_get_family(m, &fam);
381 if (r < 0) {
382 log_error_errno(r, "could not get family: %m");
383 continue;
384 }
385
386 if (fam != family) {
387 log_error("family is not correct");
388 continue;
389 }
390
391 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
392 if (r < 0) {
144232a8 393 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
394 continue;
395 }
396
397 if (ifindex > 0 && ifi != ifindex)
398 continue;
399
400 switch (fam) {
401 case AF_INET:
1c4baffc 402 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
403 if (r < 0)
404 continue;
405
406 break;
407 case AF_INET6:
1c4baffc 408 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
409 if (r < 0)
410 continue;
411
412 break;
413 default:
414 continue;
415 }
416
417 if (!in_addr_equal(fam, &gw, gateway))
418 continue;
419
1c4baffc 420 r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
c09da729
TG
421 if (r < 0)
422 continue;
423
424 r = ieee_oui(hwdb, &mac, gateway_description);
425 if (r < 0)
426 continue;
427
428 return 0;
429 }
430
431 return -ENODATA;
432}
433
69fb1176 434static int dump_gateways(
1c4baffc 435 sd_netlink *rtnl,
81fd1dd3 436 sd_hwdb *hwdb,
69fb1176
LP
437 const char *prefix,
438 int ifindex) {
b6a3ca6d
TG
439 _cleanup_free_ struct local_address *local = NULL;
440 int r, n, i;
c09da729 441
837f57da
LP
442 assert(rtnl);
443 assert(prefix);
444
b6a3ca6d
TG
445 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
446 if (n < 0)
447 return n;
c09da729 448
b6a3ca6d
TG
449 for (i = 0; i < n; i++) {
450 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 451
b6a3ca6d 452 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 453 if (r < 0)
b6a3ca6d 454 return r;
c09da729 455
69fb1176 456 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
c09da729 457 if (r < 0)
b6a3ca6d 458 log_debug_errno(r, "Could not get description of gateway: %m");
c09da729 459
1693a943
LP
460 printf("%*s%s",
461 (int) strlen(prefix),
462 i == 0 ? prefix : "",
463 gateway);
464
b6a3ca6d 465 if (description)
1693a943
LP
466 printf(" (%s)", description);
467
468 /* Show interface name for the entry if we show
469 * entries for all interfaces */
470 if (ifindex <= 0) {
471 char name[IF_NAMESIZE+1];
472
473 if (if_indextoname(local[i].ifindex, name)) {
474 fputs(" on ", stdout);
475 fputs(name, stdout);
476 } else
477 printf(" on %%%i", local[i].ifindex);
478 }
479
480 fputc('\n', stdout);
c09da729
TG
481 }
482
483 return 0;
484}
485
69fb1176 486static int dump_addresses(
1c4baffc 487 sd_netlink *rtnl,
69fb1176
LP
488 const char *prefix,
489 int ifindex) {
490
ee8c4568
LP
491 _cleanup_free_ struct local_address *local = NULL;
492 int r, n, i;
493
837f57da
LP
494 assert(rtnl);
495 assert(prefix);
496
1d050e1e 497 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
498 if (n < 0)
499 return n;
500
501 for (i = 0; i < n; i++) {
502 _cleanup_free_ char *pretty = NULL;
503
504 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
505 if (r < 0)
506 return r;
507
1693a943 508 printf("%*s%s",
ee8c4568
LP
509 (int) strlen(prefix),
510 i == 0 ? prefix : "",
511 pretty);
1693a943
LP
512
513 if (ifindex <= 0) {
514 char name[IF_NAMESIZE+1];
515
516 if (if_indextoname(local[i].ifindex, name)) {
517 fputs(" on ", stdout);
518 fputs(name, stdout);
519 } else
520 printf(" on %%%i", local[i].ifindex);
521 }
522
523 fputc('\n', stdout);
ee8c4568
LP
524 }
525
526 return 0;
527}
528
d37b7627
SS
529static int dump_address_labels(sd_netlink *rtnl) {
530 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
531 sd_netlink_message *m;
532 int r;
533
534 assert(rtnl);
535
536 r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
537 if (r < 0)
538 return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
539
540 r = sd_netlink_message_request_dump(req, true);
541 if (r < 0)
542 return r;
543
544 r = sd_netlink_call(rtnl, req, 0, &reply);
545 if (r < 0)
546 return r;
547
548 printf("%10s/%s %30s\n", "Prefix", "Prefixlen", "Label");
549
550 for (m = reply; m; m = sd_netlink_message_next(m)) {
551 _cleanup_free_ char *pretty = NULL;
552 union in_addr_union prefix = {};
553 uint8_t prefixlen;
554 uint32_t label;
555
556 r = sd_netlink_message_get_errno(m);
557 if (r < 0) {
558 log_error_errno(r, "got error: %m");
559 continue;
560 }
561
562 r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
563 if (r < 0 && r != -ENODATA) {
564 log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
565 continue;
566 }
567
568 r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
569 if (r < 0)
570 continue;
571
572 r = in_addr_to_string(AF_INET6, &prefix, &pretty);
573 if (r < 0)
574 continue;
575
576 r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
577 if (r < 0)
578 continue;
579
580 printf("%10s/%-5u %30u\n", pretty, prefixlen, label);
581 }
582
583 return 0;
584}
585
586static int list_address_labels(int argc, char *argv[], void *userdata) {
587 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
588 int r;
589
590 r = sd_netlink_open(&rtnl);
591 if (r < 0)
592 return log_error_errno(r, "Failed to connect to netlink: %m");
593
594 dump_address_labels(rtnl);
595
596 return 0;
597}
598
837f57da
LP
599static int open_lldp_neighbors(int ifindex, FILE **ret) {
600 _cleanup_free_ char *p = NULL;
601 FILE *f;
602
603 if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
604 return -ENOMEM;
605
606 f = fopen(p, "re");
607 if (!f)
608 return -errno;
609
610 *ret = f;
611 return 0;
612}
613
614static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
615 _cleanup_free_ void *raw = NULL;
616 size_t l;
617 le64_t u;
618 int r;
619
620 assert(f);
621 assert(ret);
622
623 l = fread(&u, 1, sizeof(u), f);
624 if (l == 0 && feof(f))
625 return 0;
626 if (l != sizeof(u))
627 return -EBADMSG;
628
d23c3e4c
FB
629 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
630 if (le64toh(u) >= 4096)
631 return -EBADMSG;
632
837f57da
LP
633 raw = new(uint8_t, le64toh(u));
634 if (!raw)
635 return -ENOMEM;
636
637 if (fread(raw, 1, le64toh(u), f) != le64toh(u))
638 return -EBADMSG;
639
640 r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
641 if (r < 0)
642 return r;
643
644 return 1;
645}
646
647static int dump_lldp_neighbors(const char *prefix, int ifindex) {
648 _cleanup_fclose_ FILE *f = NULL;
649 int r, c = 0;
650
651 assert(prefix);
652 assert(ifindex > 0);
653
654 r = open_lldp_neighbors(ifindex, &f);
655 if (r < 0)
656 return r;
657
658 for (;;) {
659 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
660 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
661
662 r = next_lldp_neighbor(f, &n);
663 if (r < 0)
664 return r;
665 if (r == 0)
666 break;
667
668 printf("%*s",
669 (int) strlen(prefix),
670 c == 0 ? prefix : "");
671
672 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
673 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
674 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
675
676 printf("%s on port %s", strna(system_name), strna(port_id));
677
678 if (!isempty(port_description))
679 printf(" (%s)", port_description);
680
681 putchar('\n');
682
683 c++;
684 }
685
686 return c;
687}
688
b295beea
LP
689static void dump_ifindexes(const char *prefix, const int *ifindexes) {
690 unsigned c;
691
692 assert(prefix);
693
694 if (!ifindexes || ifindexes[0] <= 0)
695 return;
696
697 for (c = 0; ifindexes[c] > 0; c++) {
698 char name[IF_NAMESIZE+1];
699
700 printf("%*s",
701 (int) strlen(prefix),
702 c == 0 ? prefix : "");
703
704 if (if_indextoname(ifindexes[c], name))
705 fputs(name, stdout);
706 else
707 printf("%i", ifindexes[c]);
708
709 fputc('\n', stdout);
710 }
711}
712
ee8c4568
LP
713static void dump_list(const char *prefix, char **l) {
714 char **i;
715
dce83649
LP
716 if (strv_isempty(l))
717 return;
718
ee8c4568
LP
719 STRV_FOREACH(i, l) {
720 printf("%*s%s\n",
721 (int) strlen(prefix),
722 i == l ? prefix : "",
723 *i);
724 }
725}
726
69fb1176 727static int link_status_one(
1c4baffc 728 sd_netlink *rtnl,
81fd1dd3 729 sd_hwdb *hwdb,
b147503e
LP
730 const LinkInfo *info) {
731
3df9bec5 732 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
64d6c229 733 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
4afd3348 734 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
9085f64a 735 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 736 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 737 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
738 const char *on_color_operational, *off_color_operational,
739 *on_color_setup, *off_color_setup;
b295beea 740 _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
b147503e 741 int r;
9085f64a
LP
742
743 assert(rtnl);
b147503e 744 assert(info);
9085f64a 745
b147503e 746 (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
d57c365b
LP
747 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
748
33d5013d
LP
749 r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
750 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
751 setup_state = strdup("unmanaged");
d57c365b 752 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 753
b147503e
LP
754 (void) sd_network_link_get_dns(info->ifindex, &dns);
755 (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
756 (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
757 (void) sd_network_link_get_ntp(info->ifindex, &ntp);
9085f64a 758
691d4da9 759 xsprintf(devid, "n%i", info->ifindex);
914d6c09 760
dce83649 761 (void) sd_device_new_from_device_id(&d, devid);
914d6c09 762
9085f64a 763 if (d) {
dce83649
LP
764 (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
765 (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
766 (void) sd_device_get_property_value(d, "ID_PATH", &path);
9085f64a 767
2c740afd 768 if (sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
dce83649 769 (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
9085f64a 770
2c740afd 771 if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) < 0)
dce83649 772 (void) sd_device_get_property_value(d, "ID_MODEL", &model);
9085f64a
LP
773 }
774
b55822c3 775 t = link_get_type_string(info->iftype, d);
b1acce80 776
4abd866d 777 (void) sd_network_link_get_network_file(info->ifindex, &network);
df3fb561 778
4abd866d
LP
779 (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
780 (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
0d4ad91d 781
323b7dc9 782 printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name);
9085f64a 783
0d4ad91d
AR
784 printf(" Link File: %s\n"
785 " Network File: %s\n"
786 " Type: %s\n"
787 " State: %s%s%s (%s%s%s)\n",
af5effc4 788 strna(link),
373d9f17 789 strna(network),
9085f64a 790 strna(t),
d57c365b
LP
791 on_color_operational, strna(operational_state), off_color_operational,
792 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
793
794 if (path)
0d4ad91d 795 printf(" Path: %s\n", path);
9085f64a 796 if (driver)
0d4ad91d 797 printf(" Driver: %s\n", driver);
9085f64a 798 if (vendor)
0d4ad91d 799 printf(" Vendor: %s\n", vendor);
9085f64a 800 if (model)
0d4ad91d 801 printf(" Model: %s\n", model);
9085f64a 802
b147503e 803 if (info->has_mac_address) {
888943fc 804 _cleanup_free_ char *description = NULL;
db73295a 805 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc 806
4abd866d 807 (void) ieee_oui(hwdb, &info->mac_address, &description);
888943fc
LP
808
809 if (description)
b147503e 810 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description);
888943fc 811 else
b147503e 812 printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea));
db73295a 813 }
9085f64a 814
b147503e 815 if (info->has_mtu)
4e964aa0 816 printf(" MTU: %" PRIu32 "\n", info->mtu);
9085f64a 817
4abd866d
LP
818 (void) dump_addresses(rtnl, " Address: ", info->ifindex);
819 (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex);
9085f64a 820
dce83649
LP
821 dump_list(" DNS: ", dns);
822 dump_list(" Search Domains: ", search_domains);
823 dump_list(" Route Domains: ", route_domains);
8eb9058d 824
dce83649 825 dump_list(" NTP: ", ntp);
0d4ad91d 826
b295beea
LP
827 dump_ifindexes("Carrier Bound To: ", carrier_bound_to);
828 dump_ifindexes("Carrier Bound By: ", carrier_bound_by);
9085f64a 829
b147503e 830 (void) sd_network_link_get_timezone(info->ifindex, &tz);
64d6c229 831 if (tz)
f5207c22 832 printf(" Time Zone: %s\n", tz);
8eb9058d 833
837f57da
LP
834 (void) dump_lldp_neighbors(" Connected To: ", info->ifindex);
835
9085f64a
LP
836 return 0;
837}
838
0070333f
LP
839static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
840 _cleanup_free_ char *operational_state = NULL;
841 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
842 const char *on_color_operational, *off_color_operational;
843
844 assert(rtnl);
845
4abd866d 846 (void) sd_network_get_operational_state(&operational_state);
0070333f
LP
847 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
848
849 printf("%s%s%s State: %s%s%s\n",
323b7dc9 850 on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational,
0070333f
LP
851 on_color_operational, strna(operational_state), off_color_operational);
852
4abd866d
LP
853 (void) dump_addresses(rtnl, " Address: ", 0);
854 (void) dump_gateways(rtnl, hwdb, " Gateway: ", 0);
0070333f 855
4abd866d 856 (void) sd_network_get_dns(&dns);
0070333f
LP
857 dump_list(" DNS: ", dns);
858
4abd866d 859 (void) sd_network_get_search_domains(&search_domains);
0070333f
LP
860 dump_list("Search Domains: ", search_domains);
861
4abd866d 862 (void) sd_network_get_route_domains(&route_domains);
0070333f
LP
863 dump_list(" Route Domains: ", route_domains);
864
4abd866d 865 (void) sd_network_get_ntp(&ntp);
0070333f
LP
866 dump_list(" NTP: ", ntp);
867
868 return 0;
869}
870
266b5389 871static int link_status(int argc, char *argv[], void *userdata) {
4afd3348 872 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b147503e
LP
873 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
874 _cleanup_free_ LinkInfo *links = NULL;
875 int r, c, i;
ee8c4568 876
ee5324aa 877 (void) pager_open(arg_no_pager, false);
0070333f 878
1c4baffc 879 r = sd_netlink_open(&rtnl);
f647962d
MS
880 if (r < 0)
881 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 882
81fd1dd3
TG
883 r = sd_hwdb_new(&hwdb);
884 if (r < 0)
885 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 886
b147503e
LP
887 if (arg_all)
888 c = acquire_link_info_all(rtnl, &links);
889 else if (argc <= 1)
0070333f 890 return system_status(rtnl, hwdb);
b147503e
LP
891 else
892 c = acquire_link_info_strv(rtnl, argv + 1, &links);
893 if (c < 0)
894 return c;
ee8c4568 895
b147503e
LP
896 for (i = 0; i < c; i++) {
897 if (i > 0)
898 fputc('\n', stdout);
ee8c4568 899
b147503e 900 link_status_one(rtnl, hwdb, links + i);
ee8c4568
LP
901 }
902
903 return 0;
904}
905
34437b4f
LP
906static char *lldp_capabilities_to_string(uint16_t x) {
907 static const char characters[] = {
908 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
909 };
910 char *ret;
911 unsigned i;
49699bac 912
34437b4f
LP
913 ret = new(char, ELEMENTSOF(characters) + 1);
914 if (!ret)
49699bac
SS
915 return NULL;
916
34437b4f
LP
917 for (i = 0; i < ELEMENTSOF(characters); i++)
918 ret[i] = (x & (1U << i)) ? characters[i] : '.';
49699bac 919
34437b4f
LP
920 ret[i] = 0;
921 return ret;
49699bac
SS
922}
923
4c3160f1
ZJS
924static void lldp_capabilities_legend(uint16_t x) {
925 unsigned w, i, cols = columns();
404d53a9 926 static const char* const table[] = {
4c3160f1
ZJS
927 "o - Other",
928 "p - Repeater",
929 "b - Bridge",
930 "w - WLAN Access Point",
931 "r - Router",
932 "t - Telephone",
933 "d - DOCSIS cable device",
934 "a - Station",
935 "c - Customer VLAN",
936 "s - Service VLAN",
937 "m - Two-port MAC Relay (TPMR)",
938 };
939
940 if (x == 0)
941 return;
942
943 printf("\nCapability Flags:\n");
944 for (w = 0, i = 0; i < ELEMENTSOF(table); i++)
945 if (x & (1U << i) || arg_all) {
946 bool newline;
947
948 newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols;
949 if (newline)
950 w = 0;
951 w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]);
952 }
953 puts("");
954}
955
49699bac 956static int link_lldp_status(int argc, char *argv[], void *userdata) {
b147503e 957 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
49699bac 958 _cleanup_free_ LinkInfo *links = NULL;
b147503e 959 int i, r, c, m = 0;
4c3160f1 960 uint16_t all = 0;
b147503e
LP
961
962 r = sd_netlink_open(&rtnl);
963 if (r < 0)
964 return log_error_errno(r, "Failed to connect to netlink: %m");
49699bac 965
e997c4b0 966 if (argc > 1)
b147503e 967 c = acquire_link_info_strv(rtnl, argv + 1, &links);
e997c4b0 968 else
b147503e 969 c = acquire_link_info_all(rtnl, &links);
49699bac 970 if (c < 0)
7d367b45
LP
971 return c;
972
ee5324aa 973 (void) pager_open(arg_no_pager, false);
49699bac 974
19727828 975 if (arg_legend)
34437b4f
LP
976 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
977 "LINK",
978 "CHASSIS ID",
979 "SYSTEM NAME",
980 "CAPS",
981 "PORT ID",
982 "PORT DESCRIPTION");
49699bac 983
b147503e 984 for (i = 0; i < c; i++) {
34437b4f 985 _cleanup_fclose_ FILE *f = NULL;
49699bac 986
837f57da
LP
987 r = open_lldp_neighbors(links[i].ifindex, &f);
988 if (r == -ENOENT)
989 continue;
990 if (r < 0) {
991 log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
34437b4f
LP
992 continue;
993 }
49699bac 994
34437b4f 995 for (;;) {
d08191a2 996 _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL;
34437b4f
LP
997 const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL;
998 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
34437b4f 999 uint16_t cc;
49699bac 1000
837f57da 1001 r = next_lldp_neighbor(f, &n);
34437b4f 1002 if (r < 0) {
837f57da 1003 log_warning_errno(r, "Failed to read neighbor data: %m");
34437b4f 1004 break;
49699bac 1005 }
837f57da
LP
1006 if (r == 0)
1007 break;
34437b4f
LP
1008
1009 (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
1010 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1011 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1012 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1013
d08191a2
LP
1014 if (chassis_id) {
1015 cid = ellipsize(chassis_id, 17, 100);
1016 if (cid)
1017 chassis_id = cid;
1018 }
1019
1020 if (port_id) {
1021 pid = ellipsize(port_id, 17, 100);
1022 if (pid)
1023 port_id = pid;
1024 }
1025
1026 if (system_name) {
1027 sname = ellipsize(system_name, 16, 100);
1028 if (sname)
1029 system_name = sname;
1030 }
1031
1032 if (port_description) {
1033 pdesc = ellipsize(port_description, 16, 100);
1034 if (pdesc)
1035 port_description = pdesc;
1036 }
1037
4c3160f1 1038 if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
34437b4f 1039 capabilities = lldp_capabilities_to_string(cc);
4c3160f1
ZJS
1040 all |= cc;
1041 }
34437b4f
LP
1042
1043 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
1044 links[i].name,
1045 strna(chassis_id),
1046 strna(system_name),
1047 strna(capabilities),
1048 strna(port_id),
1049 strna(port_description));
b147503e
LP
1050
1051 m++;
49699bac
SS
1052 }
1053 }
1054
4c3160f1
ZJS
1055 if (arg_legend) {
1056 lldp_capabilities_legend(all);
1057 printf("\n%i neighbors listed.\n", m);
1058 }
49699bac
SS
1059
1060 return 0;
1061}
1062
37ec0fdd
LP
1063static int help(void) {
1064 _cleanup_free_ char *link = NULL;
1065 int r;
1066
1067 r = terminal_urlify_man("networkctl", "1", &link);
1068 if (r < 0)
1069 return log_oom();
1070
ee8c4568
LP
1071 printf("%s [OPTIONS...]\n\n"
1072 "Query and control the networking subsystem.\n\n"
1073 " -h --help Show this help\n"
9085f64a
LP
1074 " --version Show package version\n"
1075 " --no-pager Do not pipe output into a pager\n"
1076 " --no-legend Do not show the headers and footers\n"
1077 " -a --all Show status for all links\n\n"
ee8c4568 1078 "Commands:\n"
e997c4b0 1079 " list [LINK...] List links\n"
d9000fd3 1080 " status [LINK...] Show link status\n"
837f57da 1081 " lldp [LINK...] Show LLDP neighbors\n"
d37b7627 1082 " label Show current address label entries in the kernel\n"
37ec0fdd
LP
1083 "\nSee the %s for details.\n"
1084 , program_invocation_short_name
1085 , link
1086 );
1087
1088 return 0;
ee8c4568
LP
1089}
1090
1091static int parse_argv(int argc, char *argv[]) {
1092
1093 enum {
1094 ARG_VERSION = 0x100,
1095 ARG_NO_PAGER,
1096 ARG_NO_LEGEND,
1097 };
1098
1099 static const struct option options[] = {
1100 { "help", no_argument, NULL, 'h' },
1101 { "version", no_argument, NULL, ARG_VERSION },
1102 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1103 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1104 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1105 {}
1106 };
1107
1108 int c;
1109
1110 assert(argc >= 0);
1111 assert(argv);
1112
9085f64a 1113 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1114
1115 switch (c) {
1116
1117 case 'h':
37ec0fdd 1118 return help();
ee8c4568
LP
1119
1120 case ARG_VERSION:
3f6fd1ba 1121 return version();
ee8c4568
LP
1122
1123 case ARG_NO_PAGER:
1124 arg_no_pager = true;
1125 break;
1126
1127 case ARG_NO_LEGEND:
1128 arg_legend = false;
1129 break;
1130
9085f64a
LP
1131 case 'a':
1132 arg_all = true;
1133 break;
1134
ee8c4568
LP
1135 case '?':
1136 return -EINVAL;
1137
1138 default:
1139 assert_not_reached("Unhandled option");
1140 }
1141 }
1142
1143 return 1;
1144}
1145
1146static int networkctl_main(int argc, char *argv[]) {
15c3626e
YW
1147 static const Verb verbs[] = {
1148 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
1149 { "status", VERB_ANY, VERB_ANY, 0, link_status },
1150 { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
1151 { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
266b5389 1152 {}
ee8c4568
LP
1153 };
1154
266b5389 1155 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1156}
1157
58fb3678
LP
1158static void warn_networkd_missing(void) {
1159
1160 if (access("/run/systemd/netif/state", F_OK) >= 0)
1161 return;
1162
1163 fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1164}
1165
ee8c4568
LP
1166int main(int argc, char* argv[]) {
1167 int r;
1168
1169 log_parse_environment();
1170 log_open();
1171
1172 r = parse_argv(argc, argv);
1173 if (r <= 0)
1174 goto finish;
1175
58fb3678
LP
1176 warn_networkd_missing();
1177
ee8c4568
LP
1178 r = networkctl_main(argc, argv);
1179
1180finish:
1181 pager_close();
1182
1183 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1184}