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