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