]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
Merge pull request #13217 from poettering/TODO-updates
[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 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 #include "sd-bus.h"
12 #include "sd-device.h"
13 #include "sd-hwdb.h"
14 #include "sd-lldp.h"
15 #include "sd-netlink.h"
16 #include "sd-network.h"
17
18 #include "alloc-util.h"
19 #include "arphrd-list.h"
20 #include "bus-common-errors.h"
21 #include "bus-error.h"
22 #include "bus-util.h"
23 #include "device-util.h"
24 #include "ether-addr-util.h"
25 #include "ethtool-util.h"
26 #include "fd-util.h"
27 #include "format-table.h"
28 #include "format-util.h"
29 #include "hwdb-util.h"
30 #include "local-addresses.h"
31 #include "locale-util.h"
32 #include "macro.h"
33 #include "main-func.h"
34 #include "netlink-util.h"
35 #include "pager.h"
36 #include "parse-util.h"
37 #include "pretty-print.h"
38 #include "set.h"
39 #include "socket-util.h"
40 #include "sort-util.h"
41 #include "sparse-endian.h"
42 #include "stdio-util.h"
43 #include "string-table.h"
44 #include "string-util.h"
45 #include "strv.h"
46 #include "strxcpyx.h"
47 #include "terminal-util.h"
48 #include "verbs.h"
49
50 /* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
51 #define NETDEV_KIND_MAX 64
52
53 static PagerFlags arg_pager_flags = 0;
54 static bool arg_legend = true;
55 static bool arg_all = false;
56 static bool arg_stats = false;
57
58 static char *link_get_type_string(unsigned short iftype, sd_device *d) {
59 const char *t, *devtype;
60 char *p;
61
62 if (d &&
63 sd_device_get_devtype(d, &devtype) >= 0 &&
64 !isempty(devtype))
65 return strdup(devtype);
66
67 t = arphrd_to_name(iftype);
68 if (!t)
69 return NULL;
70
71 p = strdup(t);
72 if (!p)
73 return NULL;
74
75 ascii_strlower(p);
76 return p;
77 }
78
79 static void operational_state_to_color(const char *state, const char **on, const char **off) {
80 assert(on);
81 assert(off);
82
83 if (STRPTR_IN_SET(state, "routable", "enslaved")) {
84 *on = ansi_highlight_green();
85 *off = ansi_normal();
86 } else if (streq_ptr(state, "degraded")) {
87 *on = ansi_highlight_yellow();
88 *off = ansi_normal();
89 } else
90 *on = *off = "";
91 }
92
93 static void setup_state_to_color(const char *state, const char **on, const char **off) {
94 assert(on);
95 assert(off);
96
97 if (streq_ptr(state, "configured")) {
98 *on = ansi_highlight_green();
99 *off = ansi_normal();
100 } else if (streq_ptr(state, "configuring")) {
101 *on = ansi_highlight_yellow();
102 *off = ansi_normal();
103 } else if (STRPTR_IN_SET(state, "failed", "linger")) {
104 *on = ansi_highlight_red();
105 *off = ansi_normal();
106 } else
107 *on = *off = "";
108 }
109
110 typedef struct VxLanInfo {
111 uint32_t vni;
112 uint32_t link;
113
114 int local_family;
115 int group_family;
116
117 union in_addr_union local;
118 union in_addr_union group;
119
120 uint16_t dest_port;
121
122 } VxLanInfo;
123
124 typedef struct LinkInfo {
125 char name[IFNAMSIZ+1];
126 char netdev_kind[NETDEV_KIND_MAX];
127 int ifindex;
128 unsigned short iftype;
129 struct ether_addr mac_address;
130 uint32_t mtu;
131 uint32_t min_mtu;
132 uint32_t max_mtu;
133 uint32_t tx_queues;
134 uint32_t rx_queues;
135
136 union {
137 struct rtnl_link_stats64 stats64;
138 struct rtnl_link_stats stats;
139 };
140
141 uint64_t tx_bitrate;
142 uint64_t rx_bitrate;
143
144 /* bridge info */
145 uint32_t forward_delay;
146 uint32_t hello_time;
147 uint32_t max_age;
148 uint32_t ageing_time;
149 uint32_t stp_state;
150 uint16_t priority;
151 uint8_t mcast_igmp_version;
152
153 /* vxlan info */
154 VxLanInfo vxlan_info;
155
156 /* ethtool info */
157 int autonegotiation;
158 size_t speed;
159 Duplex duplex;
160 NetDevPort port;
161
162 bool has_mac_address:1;
163 bool has_tx_queues:1;
164 bool has_rx_queues:1;
165 bool has_stats64:1;
166 bool has_stats:1;
167 bool has_bitrates:1;
168 bool has_ethtool_link_info:1;
169 } LinkInfo;
170
171 static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
172 return CMP(a->ifindex, b->ifindex);
173 }
174
175 static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
176 const char *received_kind;
177 int r;
178
179 assert(m);
180 assert(info);
181
182 r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
183 if (r < 0)
184 return r;
185
186 r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
187 if (r < 0)
188 return r;
189
190 r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
191 if (r < 0)
192 return r;
193
194 if (streq(received_kind, "bridge")) {
195 (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
196 (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
197 (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
198 (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
199 (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
200 (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
201 (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
202
203 } else if (streq(received_kind, "vxlan")) {
204 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
205
206 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
207 if (r >= 0)
208 info->vxlan_info.group_family = AF_INET;
209 else {
210 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
211 if (r >= 0)
212 info->vxlan_info.group_family = AF_INET6;
213 }
214
215 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
216 if (r >= 0)
217 info->vxlan_info.local_family = AF_INET;
218 else {
219 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
220 if (r >= 0)
221 info->vxlan_info.local_family = AF_INET6;
222 }
223
224 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
225 (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
226 }
227
228 strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
229
230 (void) sd_netlink_message_exit_container(m);
231 (void) sd_netlink_message_exit_container(m);
232
233 return 0;
234 }
235
236 static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
237 const char *name;
238 int ifindex, r;
239 uint16_t type;
240
241 assert(m);
242 assert(info);
243
244 r = sd_netlink_message_get_type(m, &type);
245 if (r < 0)
246 return r;
247
248 if (type != RTM_NEWLINK)
249 return 0;
250
251 r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
252 if (r < 0)
253 return r;
254
255 r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
256 if (r < 0)
257 return r;
258
259 if (patterns) {
260 char str[DECIMAL_STR_MAX(int)];
261
262 xsprintf(str, "%i", ifindex);
263
264 if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0))
265 return 0;
266 }
267
268 r = sd_rtnl_message_link_get_type(m, &info->iftype);
269 if (r < 0)
270 return r;
271
272 strscpy(info->name, sizeof info->name, name);
273 info->ifindex = ifindex;
274
275 info->has_mac_address =
276 sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 &&
277 memcmp(&info->mac_address, &ETHER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
278
279 (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu);
280 (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
281 (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
282
283 info->has_rx_queues =
284 sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
285 info->rx_queues > 0;
286
287 info->has_tx_queues =
288 sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
289 info->tx_queues > 0;
290
291 if (sd_netlink_message_read(m, IFLA_STATS64, sizeof info->stats64, &info->stats64) >= 0)
292 info->has_stats64 = true;
293 else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
294 info->has_stats = true;
295
296 /* fill kind info */
297 (void) decode_netdev(m, info);
298
299 return 1;
300 }
301
302 static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
303 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
304 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
305 _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
306 int r;
307
308 if (asprintf(&ifindex_str, "%i", link->ifindex) < 0)
309 return -ENOMEM;
310
311 r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path);
312 if (r < 0)
313 return r;
314
315 r = sd_bus_call_method(
316 bus,
317 "org.freedesktop.network1",
318 path,
319 "org.freedesktop.DBus.Properties",
320 "Get",
321 &error,
322 &reply,
323 "ss",
324 "org.freedesktop.network1.Link",
325 "BitRates");
326 if (r < 0) {
327 bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) ||
328 sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
329
330 return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
331 r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
332 }
333
334 r = sd_bus_message_enter_container(reply, 'v', "(tt)");
335 if (r < 0)
336 return bus_log_parse_error(r);
337
338 r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
339 if (r < 0)
340 return bus_log_parse_error(r);
341
342 r = sd_bus_message_exit_container(reply);
343 if (r < 0)
344 return bus_log_parse_error(r);
345
346 link->has_bitrates = true;
347
348 return 0;
349 }
350
351 static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
352 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
353 _cleanup_free_ LinkInfo *links = NULL;
354 _cleanup_close_ int fd = -1;
355 size_t allocated = 0, c = 0, j;
356 sd_netlink_message *i;
357 int r;
358
359 assert(rtnl);
360 assert(ret);
361
362 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
363 if (r < 0)
364 return rtnl_log_create_error(r);
365
366 r = sd_netlink_message_request_dump(req, true);
367 if (r < 0)
368 return rtnl_log_create_error(r);
369
370 r = sd_netlink_call(rtnl, req, 0, &reply);
371 if (r < 0)
372 return log_error_errno(r, "Failed to enumerate links: %m");
373
374 for (i = reply; i; i = sd_netlink_message_next(i)) {
375 if (!GREEDY_REALLOC0(links, allocated, c+1))
376 return -ENOMEM;
377
378 r = decode_link(i, links + c, patterns);
379 if (r < 0)
380 return r;
381 if (r == 0)
382 continue;
383
384 r = ethtool_get_link_info(&fd, links[c].name,
385 &links[c].autonegotiation, &links[c].speed,
386 &links[c].duplex, &links[c].port);
387 if (r >= 0)
388 links[c].has_ethtool_link_info = true;
389
390 c++;
391 }
392
393 typesafe_qsort(links, c, link_info_compare);
394
395 if (bus)
396 for (j = 0; j < c; j++)
397 (void) acquire_link_bitrates(bus, links + j);
398
399 *ret = TAKE_PTR(links);
400
401 return (int) c;
402 }
403
404 static int list_links(int argc, char *argv[], void *userdata) {
405 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
406 _cleanup_free_ LinkInfo *links = NULL;
407 _cleanup_(table_unrefp) Table *table = NULL;
408 TableCell *cell;
409 int c, i, r;
410
411 r = sd_netlink_open(&rtnl);
412 if (r < 0)
413 return log_error_errno(r, "Failed to connect to netlink: %m");
414
415 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
416 if (c < 0)
417 return c;
418
419 (void) pager_open(arg_pager_flags);
420
421 table = table_new("idx", "link", "type", "operational", "setup");
422 if (!table)
423 return log_oom();
424
425 table_set_header(table, arg_legend);
426
427 assert_se(cell = table_get_cell(table, 0, 0));
428 (void) table_set_minimum_width(table, cell, 3);
429 (void) table_set_weight(table, cell, 0);
430 (void) table_set_ellipsize_percent(table, cell, 100);
431 (void) table_set_align_percent(table, cell, 100);
432
433 assert_se(cell = table_get_cell(table, 0, 1));
434 (void) table_set_ellipsize_percent(table, cell, 100);
435
436 for (i = 0; i < c; i++) {
437 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
438 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
439 const char *on_color_operational, *off_color_operational,
440 *on_color_setup, *off_color_setup;
441 char devid[2 + DECIMAL_STR_MAX(int)];
442 _cleanup_free_ char *t = NULL;
443
444 (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
445 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
446
447 r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
448 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
449 setup_state = strdup("unmanaged");
450 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
451
452 xsprintf(devid, "n%i", links[i].ifindex);
453 (void) sd_device_new_from_device_id(&d, devid);
454
455 t = link_get_type_string(links[i].iftype, d);
456
457 r = table_add_many(table,
458 TABLE_INT, links[i].ifindex,
459 TABLE_STRING, links[i].name,
460 TABLE_STRING, strna(t),
461 TABLE_STRING, strna(operational_state),
462 TABLE_SET_COLOR, on_color_operational,
463 TABLE_STRING, strna(setup_state),
464 TABLE_SET_COLOR, on_color_setup);
465 if (r < 0)
466 return r;
467 }
468
469 r = table_print(table, NULL);
470 if (r < 0)
471 return log_error_errno(r, "Failed to print table: %m");
472
473 if (arg_legend)
474 printf("\n%i links listed.\n", c);
475
476 return 0;
477 }
478
479 /* IEEE Organizationally Unique Identifier vendor string */
480 static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
481 const char *description;
482 char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
483 int r;
484
485 assert(ret);
486
487 if (!hwdb)
488 return -EINVAL;
489
490 if (!mac)
491 return -EINVAL;
492
493 /* skip commonly misused 00:00:00 (Xerox) prefix */
494 if (memcmp(mac, "\0\0\0", 3) == 0)
495 return -EINVAL;
496
497 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
498 ETHER_ADDR_FORMAT_VAL(*mac));
499
500 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
501 if (r < 0)
502 return r;
503
504 desc = strdup(description);
505 if (!desc)
506 return -ENOMEM;
507
508 *ret = desc;
509
510 return 0;
511 }
512
513 static int get_gateway_description(
514 sd_netlink *rtnl,
515 sd_hwdb *hwdb,
516 int ifindex,
517 int family,
518 union in_addr_union *gateway,
519 char **gateway_description) {
520 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
521 sd_netlink_message *m;
522 int r;
523
524 assert(rtnl);
525 assert(ifindex >= 0);
526 assert(IN_SET(family, AF_INET, AF_INET6));
527 assert(gateway);
528 assert(gateway_description);
529
530 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
531 if (r < 0)
532 return r;
533
534 r = sd_netlink_message_request_dump(req, true);
535 if (r < 0)
536 return r;
537
538 r = sd_netlink_call(rtnl, req, 0, &reply);
539 if (r < 0)
540 return r;
541
542 for (m = reply; m; m = sd_netlink_message_next(m)) {
543 union in_addr_union gw = IN_ADDR_NULL;
544 struct ether_addr mac = ETHER_ADDR_NULL;
545 uint16_t type;
546 int ifi, fam;
547
548 r = sd_netlink_message_get_errno(m);
549 if (r < 0) {
550 log_error_errno(r, "got error: %m");
551 continue;
552 }
553
554 r = sd_netlink_message_get_type(m, &type);
555 if (r < 0) {
556 log_error_errno(r, "could not get type: %m");
557 continue;
558 }
559
560 if (type != RTM_NEWNEIGH) {
561 log_error("type is not RTM_NEWNEIGH");
562 continue;
563 }
564
565 r = sd_rtnl_message_neigh_get_family(m, &fam);
566 if (r < 0) {
567 log_error_errno(r, "could not get family: %m");
568 continue;
569 }
570
571 if (fam != family) {
572 log_error("family is not correct");
573 continue;
574 }
575
576 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
577 if (r < 0) {
578 log_error_errno(r, "could not get ifindex: %m");
579 continue;
580 }
581
582 if (ifindex > 0 && ifi != ifindex)
583 continue;
584
585 switch (fam) {
586 case AF_INET:
587 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
588 if (r < 0)
589 continue;
590
591 break;
592 case AF_INET6:
593 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
594 if (r < 0)
595 continue;
596
597 break;
598 default:
599 continue;
600 }
601
602 if (!in_addr_equal(fam, &gw, gateway))
603 continue;
604
605 r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
606 if (r < 0)
607 continue;
608
609 r = ieee_oui(hwdb, &mac, gateway_description);
610 if (r < 0)
611 continue;
612
613 return 0;
614 }
615
616 return -ENODATA;
617 }
618
619 static int dump_gateways(
620 sd_netlink *rtnl,
621 sd_hwdb *hwdb,
622 Table *table,
623 int ifindex) {
624 _cleanup_free_ struct local_address *local = NULL;
625 int r, n, i;
626
627 assert(rtnl);
628 assert(table);
629
630 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
631 if (n < 0)
632 return n;
633
634 for (i = 0; i < n; i++) {
635 _cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
636
637 r = table_add_many(table,
638 TABLE_EMPTY,
639 TABLE_STRING, i == 0 ? "Gateway:" : "");
640 if (r < 0)
641 return r;
642
643 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
644 if (r < 0)
645 return r;
646
647 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
648 if (r < 0)
649 log_debug_errno(r, "Could not get description of gateway: %m");
650
651 if (description) {
652 with_description = strjoin(gateway, " (", description, ")");
653 if (!with_description)
654 return -ENOMEM;
655 }
656
657 /* Show interface name for the entry if we show
658 * entries for all interfaces */
659 if (ifindex <= 0) {
660 char name[IF_NAMESIZE+1];
661
662 if (format_ifname(local[i].ifindex, name))
663 r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway, name);
664 else
665 r = table_add_cell_stringf(table, NULL, "%s on %%%i", with_description ?: gateway, local[i].ifindex);
666 } else
667 r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway);
668 if (r < 0)
669 return r;
670 }
671
672 return 0;
673 }
674
675 static int dump_addresses(
676 sd_netlink *rtnl,
677 Table *table,
678 int ifindex) {
679
680 _cleanup_free_ struct local_address *local = NULL;
681 int r, n, i;
682
683 assert(rtnl);
684 assert(table);
685
686 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
687 if (n < 0)
688 return n;
689
690 for (i = 0; i < n; i++) {
691 _cleanup_free_ char *pretty = NULL;
692
693 r = table_add_many(table,
694 TABLE_EMPTY,
695 TABLE_STRING, i == 0 ? "Address:" : "");
696 if (r < 0)
697 return r;
698
699 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
700 if (r < 0)
701 return r;
702
703 if (ifindex <= 0) {
704 char name[IF_NAMESIZE+1];
705
706 if (format_ifname(local[i].ifindex, name))
707 r = table_add_cell_stringf(table, NULL, "%s on %s", pretty, name);
708 else
709 r = table_add_cell_stringf(table, NULL, "%s on %%%i", pretty, local[i].ifindex);
710 } else
711 r = table_add_cell(table, NULL, TABLE_STRING, pretty);
712 if (r < 0)
713 return r;
714 }
715
716 return 0;
717 }
718
719 static int dump_address_labels(sd_netlink *rtnl) {
720 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
721 _cleanup_(table_unrefp) Table *table = NULL;
722 sd_netlink_message *m;
723 TableCell *cell;
724 int r;
725
726 assert(rtnl);
727
728 r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
729 if (r < 0)
730 return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
731
732 r = sd_netlink_message_request_dump(req, true);
733 if (r < 0)
734 return r;
735
736 r = sd_netlink_call(rtnl, req, 0, &reply);
737 if (r < 0)
738 return r;
739
740 table = table_new("label", "prefix/prefixlen");
741 if (!table)
742 return -ENOMEM;
743
744 r = table_set_sort(table, 0, SIZE_MAX);
745 if (r < 0)
746 return r;
747
748 assert_se(cell = table_get_cell(table, 0, 0));
749 (void) table_set_align_percent(table, cell, 100);
750 (void) table_set_ellipsize_percent(table, cell, 100);
751
752 assert_se(cell = table_get_cell(table, 0, 1));
753 (void) table_set_align_percent(table, cell, 100);
754
755 for (m = reply; m; m = sd_netlink_message_next(m)) {
756 _cleanup_free_ char *pretty = NULL;
757 union in_addr_union prefix = IN_ADDR_NULL;
758 uint8_t prefixlen;
759 uint32_t label;
760
761 r = sd_netlink_message_get_errno(m);
762 if (r < 0) {
763 log_error_errno(r, "got error: %m");
764 continue;
765 }
766
767 r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
768 if (r < 0 && r != -ENODATA) {
769 log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
770 continue;
771 }
772
773 r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
774 if (r < 0)
775 continue;
776
777 r = in_addr_to_string(AF_INET6, &prefix, &pretty);
778 if (r < 0)
779 continue;
780
781 r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
782 if (r < 0)
783 continue;
784
785 r = table_add_cell(table, NULL, TABLE_UINT32, &label);
786 if (r < 0)
787 return r;
788
789 r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
790 if (r < 0)
791 return r;
792 }
793
794 return table_print(table, NULL);
795 }
796
797 static int list_address_labels(int argc, char *argv[], void *userdata) {
798 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
799 int r;
800
801 r = sd_netlink_open(&rtnl);
802 if (r < 0)
803 return log_error_errno(r, "Failed to connect to netlink: %m");
804
805 dump_address_labels(rtnl);
806
807 return 0;
808 }
809
810 static int open_lldp_neighbors(int ifindex, FILE **ret) {
811 _cleanup_free_ char *p = NULL;
812 FILE *f;
813
814 if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
815 return -ENOMEM;
816
817 f = fopen(p, "re");
818 if (!f)
819 return -errno;
820
821 *ret = f;
822 return 0;
823 }
824
825 static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
826 _cleanup_free_ void *raw = NULL;
827 size_t l;
828 le64_t u;
829 int r;
830
831 assert(f);
832 assert(ret);
833
834 l = fread(&u, 1, sizeof(u), f);
835 if (l == 0 && feof(f))
836 return 0;
837 if (l != sizeof(u))
838 return -EBADMSG;
839
840 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
841 if (le64toh(u) >= 4096)
842 return -EBADMSG;
843
844 raw = new(uint8_t, le64toh(u));
845 if (!raw)
846 return -ENOMEM;
847
848 if (fread(raw, 1, le64toh(u), f) != le64toh(u))
849 return -EBADMSG;
850
851 r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
852 if (r < 0)
853 return r;
854
855 return 1;
856 }
857
858 static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
859 _cleanup_fclose_ FILE *f = NULL;
860 int r, c = 0;
861
862 assert(table);
863 assert(prefix);
864 assert(ifindex > 0);
865
866 r = open_lldp_neighbors(ifindex, &f);
867 if (r == -ENOENT)
868 return 0;
869 if (r < 0)
870 return r;
871
872 for (;;) {
873 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
874 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
875
876 r = next_lldp_neighbor(f, &n);
877 if (r < 0)
878 return r;
879 if (r == 0)
880 break;
881
882 r = table_add_many(table,
883 TABLE_EMPTY,
884 TABLE_STRING, c == 0 ? prefix : "");
885 if (r < 0)
886 return r;
887
888 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
889 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
890 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
891
892 r = table_add_cell_stringf(table, NULL,
893 "%s on port %s%s%s%s",
894 strna(system_name), strna(port_id),
895 isempty(port_description) ? "" : " (",
896 port_description,
897 isempty(port_description) ? "" : ")");
898 if (r < 0)
899 return r;
900
901 c++;
902 }
903
904 return c;
905 }
906
907 static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
908 unsigned c;
909 int r;
910
911 assert(prefix);
912
913 if (!ifindexes || ifindexes[0] <= 0)
914 return 0;
915
916 for (c = 0; ifindexes[c] > 0; c++) {
917 r = table_add_many(table,
918 TABLE_EMPTY,
919 TABLE_STRING, c == 0 ? prefix : "",
920 TABLE_IFINDEX, ifindexes[c]);
921 if (r < 0)
922 return r;
923 }
924
925 return 0;
926 }
927
928 static int dump_list(Table *table, const char *prefix, char **l) {
929 char **i;
930 int r;
931
932 if (strv_isempty(l))
933 return 0;
934
935 STRV_FOREACH(i, l) {
936 r = table_add_many(table,
937 TABLE_EMPTY,
938 TABLE_STRING, i == l ? prefix : "",
939 TABLE_STRING, *i);
940 if (r < 0)
941 return r;
942 }
943
944 return 0;
945 }
946
947 #define DUMP_STATS_ONE(name, val_name) \
948 r = table_add_many(table, \
949 TABLE_EMPTY, \
950 TABLE_STRING, name ":"); \
951 if (r < 0) \
952 return r; \
953 r = table_add_cell(table, NULL, \
954 info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
955 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
956 if (r < 0) \
957 return r;
958
959 static int dump_statistics(Table *table, const LinkInfo *info) {
960 int r;
961
962 if (!arg_stats)
963 return 0;
964
965 if (!info->has_stats64 && !info->has_stats)
966 return 0;
967
968 DUMP_STATS_ONE("Rx Packets", rx_packets);
969 DUMP_STATS_ONE("Tx Packets", tx_packets);
970 DUMP_STATS_ONE("Rx Bytes", rx_bytes);
971 DUMP_STATS_ONE("Tx Bytes", tx_bytes);
972 DUMP_STATS_ONE("Rx Errors", rx_errors);
973 DUMP_STATS_ONE("Tx Errors", tx_errors);
974 DUMP_STATS_ONE("Rx Dropped", rx_dropped);
975 DUMP_STATS_ONE("Tx Dropped", tx_dropped);
976 DUMP_STATS_ONE("Multicast Packets", multicast);
977 DUMP_STATS_ONE("Collisions", collisions);
978
979 return 0;
980 }
981
982 static int link_status_one(
983 sd_netlink *rtnl,
984 sd_hwdb *hwdb,
985 const LinkInfo *info) {
986
987 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
988 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
989 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
990 char devid[2 + DECIMAL_STR_MAX(int)];
991 _cleanup_free_ char *t = NULL, *network = NULL;
992 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
993 const char *on_color_operational, *off_color_operational,
994 *on_color_setup, *off_color_setup;
995 _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
996 _cleanup_(table_unrefp) Table *table = NULL;
997 TableCell *cell;
998 int r;
999
1000 assert(rtnl);
1001 assert(info);
1002
1003 (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
1004 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
1005
1006 r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
1007 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
1008 setup_state = strdup("unmanaged");
1009 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
1010
1011 (void) sd_network_link_get_dns(info->ifindex, &dns);
1012 (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
1013 (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
1014 (void) sd_network_link_get_ntp(info->ifindex, &ntp);
1015
1016 xsprintf(devid, "n%i", info->ifindex);
1017
1018 (void) sd_device_new_from_device_id(&d, devid);
1019
1020 if (d) {
1021 (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
1022 (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
1023 (void) sd_device_get_property_value(d, "ID_PATH", &path);
1024
1025 if (sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
1026 (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
1027
1028 if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) < 0)
1029 (void) sd_device_get_property_value(d, "ID_MODEL", &model);
1030 }
1031
1032 t = link_get_type_string(info->iftype, d);
1033
1034 (void) sd_network_link_get_network_file(info->ifindex, &network);
1035
1036 (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
1037 (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
1038
1039 table = table_new("dot", "key", "value");
1040 if (!table)
1041 return -ENOMEM;
1042
1043 assert_se(cell = table_get_cell(table, 0, 0));
1044 (void) table_set_ellipsize_percent(table, cell, 100);
1045
1046 assert_se(cell = table_get_cell(table, 0, 1));
1047 (void) table_set_ellipsize_percent(table, cell, 100);
1048
1049 table_set_header(table, false);
1050
1051 r = table_add_many(table,
1052 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1053 TABLE_SET_COLOR, on_color_operational);
1054 if (r < 0)
1055 return r;
1056 r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
1057 if (r < 0)
1058 return r;
1059 (void) table_set_align_percent(table, cell, 0);
1060
1061 r = table_add_many(table,
1062 TABLE_EMPTY,
1063 TABLE_EMPTY,
1064 TABLE_STRING, "Link File:",
1065 TABLE_SET_ALIGN_PERCENT, 100,
1066 TABLE_STRING, strna(link),
1067 TABLE_EMPTY,
1068 TABLE_STRING, "Network File:",
1069 TABLE_STRING, strna(network),
1070 TABLE_EMPTY,
1071 TABLE_STRING, "Type:",
1072 TABLE_STRING, strna(t),
1073 TABLE_EMPTY,
1074 TABLE_STRING, "State:");
1075 if (r < 0)
1076 return r;
1077 r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
1078 on_color_operational, strna(operational_state), off_color_operational,
1079 on_color_setup, strna(setup_state), off_color_setup);
1080 if (r < 0)
1081 return r;
1082
1083 if (path) {
1084 r = table_add_many(table,
1085 TABLE_EMPTY,
1086 TABLE_STRING, "Path:",
1087 TABLE_STRING, path);
1088 if (r < 0)
1089 return r;
1090 }
1091 if (driver) {
1092 r = table_add_many(table,
1093 TABLE_EMPTY,
1094 TABLE_STRING, "Driver:",
1095 TABLE_STRING, driver);
1096 if (r < 0)
1097 return r;
1098 }
1099 if (vendor) {
1100 r = table_add_many(table,
1101 TABLE_EMPTY,
1102 TABLE_STRING, "Vendor:",
1103 TABLE_STRING, vendor);
1104 if (r < 0)
1105 return r;
1106 }
1107 if (model) {
1108 r = table_add_many(table,
1109 TABLE_EMPTY,
1110 TABLE_STRING, "Model:",
1111 TABLE_STRING, model);
1112 if (r < 0)
1113 return r;
1114 }
1115
1116 if (info->has_mac_address) {
1117 _cleanup_free_ char *description = NULL;
1118 char ea[ETHER_ADDR_TO_STRING_MAX];
1119
1120 (void) ieee_oui(hwdb, &info->mac_address, &description);
1121
1122 r = table_add_many(table,
1123 TABLE_EMPTY,
1124 TABLE_STRING, "HW Address:");
1125 if (r < 0)
1126 return r;
1127 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
1128 ether_addr_to_string(&info->mac_address, ea),
1129 description ? " (" : "",
1130 strempty(description),
1131 description ? ")" : "");
1132 if (r < 0)
1133 return r;
1134 }
1135
1136 if (info->mtu > 0) {
1137 char min_str[DECIMAL_STR_MAX(uint32_t)], max_str[DECIMAL_STR_MAX(uint32_t)];
1138
1139 xsprintf(min_str, "%" PRIu32, info->min_mtu);
1140 xsprintf(max_str, "%" PRIu32, info->max_mtu);
1141
1142 r = table_add_many(table,
1143 TABLE_EMPTY,
1144 TABLE_STRING, "MTU:");
1145 if (r < 0)
1146 return r;
1147 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "%s%s%s%s%s%s%s",
1148 info->mtu,
1149 info->min_mtu > 0 || info->max_mtu > 0 ? " (" : "",
1150 info->min_mtu > 0 ? "min: " : "",
1151 info->min_mtu > 0 ? min_str : "",
1152 info->min_mtu > 0 && info->max_mtu > 0 ? ", " : "",
1153 info->max_mtu > 0 ? "max: " : "",
1154 info->max_mtu > 0 ? max_str : "",
1155 info->min_mtu > 0 || info->max_mtu > 0 ? ")" : "");
1156 if (r < 0)
1157 return r;
1158 }
1159
1160 if (streq_ptr(info->netdev_kind, "bridge")) {
1161 r = table_add_many(table,
1162 TABLE_EMPTY,
1163 TABLE_STRING, "Forward Delay:",
1164 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->forward_delay),
1165 TABLE_EMPTY,
1166 TABLE_STRING, "Hello Time:",
1167 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->hello_time),
1168 TABLE_EMPTY,
1169 TABLE_STRING, "Max Age:",
1170 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->max_age),
1171 TABLE_EMPTY,
1172 TABLE_STRING, "Ageing Time:",
1173 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->ageing_time),
1174 TABLE_EMPTY,
1175 TABLE_STRING, "Priority:",
1176 TABLE_UINT16, info->priority,
1177 TABLE_EMPTY,
1178 TABLE_STRING, "STP:",
1179 TABLE_BOOLEAN, info->stp_state > 0,
1180 TABLE_EMPTY,
1181 TABLE_STRING, "Multicast IGMP Version:",
1182 TABLE_UINT8, info->mcast_igmp_version);
1183 if (r < 0)
1184 return r;
1185
1186 } else if (streq_ptr(info->netdev_kind, "vxlan")) {
1187 if (info->vxlan_info.vni > 0) {
1188 r = table_add_many(table,
1189 TABLE_EMPTY,
1190 TABLE_STRING, "VNI:",
1191 TABLE_UINT32, info->vxlan_info.vni);
1192 if (r < 0)
1193 return r;
1194 }
1195
1196 if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
1197 r = table_add_many(table,
1198 TABLE_EMPTY,
1199 TABLE_STRING, "Group:",
1200 info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1201 &info->vxlan_info.group);
1202 if (r < 0)
1203 return r;
1204 }
1205
1206 if (IN_SET(info->vxlan_info.local_family, AF_INET, AF_INET6)) {
1207 r = table_add_many(table,
1208 TABLE_EMPTY,
1209 TABLE_STRING, "Local:",
1210 info->vxlan_info.local_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1211 &info->vxlan_info.local);
1212 if (r < 0)
1213 return r;
1214 }
1215
1216 if (info->vxlan_info.dest_port > 0) {
1217 r = table_add_many(table,
1218 TABLE_EMPTY,
1219 TABLE_STRING, "Destination Port:",
1220 TABLE_UINT16, be16toh(info->vxlan_info.dest_port));
1221 if (r < 0)
1222 return r;
1223 }
1224
1225 if (info->vxlan_info.link > 0) {
1226 r = table_add_many(table,
1227 TABLE_EMPTY,
1228 TABLE_STRING, "Underlying Device:",
1229 TABLE_IFINDEX, info->vxlan_info.link);
1230 if (r < 0)
1231 return r;
1232 }
1233 }
1234
1235 if (info->has_bitrates) {
1236 char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX];
1237
1238 r = table_add_many(table,
1239 TABLE_EMPTY,
1240 TABLE_STRING, "Bit Rate (Tx/Rx):");
1241 if (r < 0)
1242 return r;
1243 r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
1244 format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0),
1245 format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0));
1246 if (r < 0)
1247 return r;
1248 }
1249
1250 if (info->has_tx_queues || info->has_rx_queues) {
1251 r = table_add_many(table,
1252 TABLE_EMPTY,
1253 TABLE_STRING, "Queue Length (Tx/Rx):");
1254 if (r < 0)
1255 return r;
1256 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "/%" PRIu32, info->tx_queues, info->rx_queues);
1257 if (r < 0)
1258 return r;
1259 }
1260
1261 if (info->has_ethtool_link_info) {
1262 const char *duplex = duplex_to_string(info->duplex);
1263 const char *port = port_to_string(info->port);
1264
1265 if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
1266 r = table_add_many(table,
1267 TABLE_EMPTY,
1268 TABLE_STRING, "Auto negotiation:",
1269 TABLE_BOOLEAN, info->autonegotiation == AUTONEG_ENABLE);
1270 if (r < 0)
1271 return r;
1272 }
1273
1274 if (info->speed > 0) {
1275 r = table_add_many(table,
1276 TABLE_EMPTY,
1277 TABLE_STRING, "Speed:",
1278 TABLE_BPS, info->speed);
1279 if (r < 0)
1280 return r;
1281 }
1282
1283 if (duplex) {
1284 r = table_add_many(table,
1285 TABLE_EMPTY,
1286 TABLE_STRING, "Duplex:",
1287 TABLE_STRING, duplex);
1288 if (r < 0)
1289 return r;
1290 }
1291
1292 if (port) {
1293 r = table_add_many(table,
1294 TABLE_EMPTY,
1295 TABLE_STRING, "Port:",
1296 TABLE_STRING, port);
1297 if (r < 0)
1298 return r;
1299 }
1300 }
1301
1302 r = dump_addresses(rtnl, table, info->ifindex);
1303 if (r < 0)
1304 return r;
1305 r = dump_gateways(rtnl, hwdb, table, info->ifindex);
1306 if (r < 0)
1307 return r;
1308 r = dump_list(table, "DNS:", dns);
1309 if (r < 0)
1310 return r;
1311 r = dump_list(table, "Search Domains:", search_domains);
1312 if (r < 0)
1313 return r;
1314 r = dump_list(table, "Route Domains:", route_domains);
1315 if (r < 0)
1316 return r;
1317 r = dump_list(table, "NTP:", ntp);
1318 if (r < 0)
1319 return r;
1320 r = dump_ifindexes(table, "Carrier Bound To:", carrier_bound_to);
1321 if (r < 0)
1322 return r;
1323 r = dump_ifindexes(table, "Carrier Bound By:", carrier_bound_by);
1324 if (r < 0)
1325 return r;
1326
1327 (void) sd_network_link_get_timezone(info->ifindex, &tz);
1328 if (tz) {
1329 r = table_add_many(table,
1330 TABLE_EMPTY,
1331 TABLE_STRING, "Time Zone:",
1332 TABLE_STRING, tz);
1333 if (r < 0)
1334 return r;
1335 }
1336
1337 r = dump_lldp_neighbors(table, "Connected To:", info->ifindex);
1338 if (r < 0)
1339 return r;
1340
1341 r = dump_statistics(table, info);
1342 if (r < 0)
1343 return r;
1344
1345 return table_print(table, NULL);
1346 }
1347
1348 static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
1349 _cleanup_free_ char *operational_state = NULL;
1350 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
1351 const char *on_color_operational, *off_color_operational;
1352 _cleanup_(table_unrefp) Table *table = NULL;
1353 TableCell *cell;
1354 int r;
1355
1356 assert(rtnl);
1357
1358 (void) sd_network_get_operational_state(&operational_state);
1359 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
1360
1361 table = table_new("dot", "key", "value");
1362 if (!table)
1363 return -ENOMEM;
1364
1365 assert_se(cell = table_get_cell(table, 0, 0));
1366 (void) table_set_ellipsize_percent(table, cell, 100);
1367
1368 assert_se(cell = table_get_cell(table, 0, 1));
1369 (void) table_set_align_percent(table, cell, 100);
1370 (void) table_set_ellipsize_percent(table, cell, 100);
1371
1372 table_set_header(table, false);
1373
1374 r = table_add_many(table,
1375 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1376 TABLE_SET_COLOR, on_color_operational,
1377 TABLE_STRING, "State:",
1378 TABLE_STRING, strna(operational_state),
1379 TABLE_SET_COLOR, on_color_operational);
1380
1381 r = dump_addresses(rtnl, table, 0);
1382 if (r < 0)
1383 return r;
1384 r = dump_gateways(rtnl, hwdb, table, 0);
1385 if (r < 0)
1386 return r;
1387
1388 (void) sd_network_get_dns(&dns);
1389 r = dump_list(table, "DNS:", dns);
1390 if (r < 0)
1391 return r;
1392
1393 (void) sd_network_get_search_domains(&search_domains);
1394 r = dump_list(table, "Search Domains:", search_domains);
1395 if (r < 0)
1396 return r;
1397
1398 (void) sd_network_get_route_domains(&route_domains);
1399 r = dump_list(table, "Route Domains:", route_domains);
1400 if (r < 0)
1401 return r;
1402
1403 (void) sd_network_get_ntp(&ntp);
1404 r = dump_list(table, "NTP:", ntp);
1405 if (r < 0)
1406 return r;
1407
1408 return table_print(table, NULL);
1409 }
1410
1411 static int link_status(int argc, char *argv[], void *userdata) {
1412 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1413 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1414 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
1415 _cleanup_free_ LinkInfo *links = NULL;
1416 int r, c, i;
1417
1418 (void) pager_open(arg_pager_flags);
1419
1420 r = sd_bus_open_system(&bus);
1421 if (r < 0)
1422 return log_error_errno(r, "Failed to connect system bus: %m");
1423
1424 r = sd_netlink_open(&rtnl);
1425 if (r < 0)
1426 return log_error_errno(r, "Failed to connect to netlink: %m");
1427
1428 r = sd_hwdb_new(&hwdb);
1429 if (r < 0)
1430 log_debug_errno(r, "Failed to open hardware database: %m");
1431
1432 if (arg_all)
1433 c = acquire_link_info(bus, rtnl, NULL, &links);
1434 else if (argc <= 1)
1435 return system_status(rtnl, hwdb);
1436 else
1437 c = acquire_link_info(bus, rtnl, argv + 1, &links);
1438 if (c < 0)
1439 return c;
1440
1441 for (i = 0; i < c; i++) {
1442 if (i > 0)
1443 fputc('\n', stdout);
1444
1445 link_status_one(rtnl, hwdb, links + i);
1446 }
1447
1448 return 0;
1449 }
1450
1451 static char *lldp_capabilities_to_string(uint16_t x) {
1452 static const char characters[] = {
1453 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1454 };
1455 char *ret;
1456 unsigned i;
1457
1458 ret = new(char, ELEMENTSOF(characters) + 1);
1459 if (!ret)
1460 return NULL;
1461
1462 for (i = 0; i < ELEMENTSOF(characters); i++)
1463 ret[i] = (x & (1U << i)) ? characters[i] : '.';
1464
1465 ret[i] = 0;
1466 return ret;
1467 }
1468
1469 static void lldp_capabilities_legend(uint16_t x) {
1470 unsigned w, i, cols = columns();
1471 static const char* const table[] = {
1472 "o - Other",
1473 "p - Repeater",
1474 "b - Bridge",
1475 "w - WLAN Access Point",
1476 "r - Router",
1477 "t - Telephone",
1478 "d - DOCSIS cable device",
1479 "a - Station",
1480 "c - Customer VLAN",
1481 "s - Service VLAN",
1482 "m - Two-port MAC Relay (TPMR)",
1483 };
1484
1485 if (x == 0)
1486 return;
1487
1488 printf("\nCapability Flags:\n");
1489 for (w = 0, i = 0; i < ELEMENTSOF(table); i++)
1490 if (x & (1U << i) || arg_all) {
1491 bool newline;
1492
1493 newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols;
1494 if (newline)
1495 w = 0;
1496 w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]);
1497 }
1498 puts("");
1499 }
1500
1501 static int link_lldp_status(int argc, char *argv[], void *userdata) {
1502 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1503 _cleanup_free_ LinkInfo *links = NULL;
1504 _cleanup_(table_unrefp) Table *table = NULL;
1505 int i, r, c, m = 0;
1506 uint16_t all = 0;
1507 TableCell *cell;
1508
1509 r = sd_netlink_open(&rtnl);
1510 if (r < 0)
1511 return log_error_errno(r, "Failed to connect to netlink: %m");
1512
1513 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
1514 if (c < 0)
1515 return c;
1516
1517 (void) pager_open(arg_pager_flags);
1518
1519 table = table_new("link",
1520 "chassis id",
1521 "system name",
1522 "caps",
1523 "port id",
1524 "port description");
1525 if (!table)
1526 return -ENOMEM;
1527
1528 table_set_header(table, arg_legend);
1529
1530 assert_se(cell = table_get_cell(table, 0, 0));
1531 table_set_minimum_width(table, cell, 16);
1532
1533 assert_se(cell = table_get_cell(table, 0, 1));
1534 table_set_minimum_width(table, cell, 17);
1535
1536 assert_se(cell = table_get_cell(table, 0, 2));
1537 table_set_minimum_width(table, cell, 16);
1538
1539 assert_se(cell = table_get_cell(table, 0, 3));
1540 table_set_minimum_width(table, cell, 11);
1541
1542 assert_se(cell = table_get_cell(table, 0, 4));
1543 table_set_minimum_width(table, cell, 17);
1544
1545 assert_se(cell = table_get_cell(table, 0, 5));
1546 table_set_minimum_width(table, cell, 16);
1547
1548 for (i = 0; i < c; i++) {
1549 _cleanup_fclose_ FILE *f = NULL;
1550
1551 r = open_lldp_neighbors(links[i].ifindex, &f);
1552 if (r == -ENOENT)
1553 continue;
1554 if (r < 0) {
1555 log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
1556 continue;
1557 }
1558
1559 for (;;) {
1560 _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL, *capabilities = NULL;
1561 const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
1562 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1563 uint16_t cc;
1564
1565 r = next_lldp_neighbor(f, &n);
1566 if (r < 0) {
1567 log_warning_errno(r, "Failed to read neighbor data: %m");
1568 break;
1569 }
1570 if (r == 0)
1571 break;
1572
1573 (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
1574 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1575 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1576 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1577
1578 if (chassis_id) {
1579 cid = ellipsize(chassis_id, 17, 100);
1580 if (cid)
1581 chassis_id = cid;
1582 }
1583
1584 if (port_id) {
1585 pid = ellipsize(port_id, 17, 100);
1586 if (pid)
1587 port_id = pid;
1588 }
1589
1590 if (system_name) {
1591 sname = ellipsize(system_name, 16, 100);
1592 if (sname)
1593 system_name = sname;
1594 }
1595
1596 if (port_description) {
1597 pdesc = ellipsize(port_description, 16, 100);
1598 if (pdesc)
1599 port_description = pdesc;
1600 }
1601
1602 if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
1603 capabilities = lldp_capabilities_to_string(cc);
1604 all |= cc;
1605 }
1606
1607 r = table_add_many(table,
1608 TABLE_STRING, links[i].name,
1609 TABLE_STRING, strna(chassis_id),
1610 TABLE_STRING, strna(system_name),
1611 TABLE_STRING, strna(capabilities),
1612 TABLE_STRING, strna(port_id),
1613 TABLE_STRING, strna(port_description));
1614 if (r < 0)
1615 return r;
1616
1617 m++;
1618 }
1619 }
1620
1621 r = table_print(table, NULL);
1622 if (r < 0)
1623 return r;
1624
1625 if (arg_legend) {
1626 lldp_capabilities_legend(all);
1627 printf("\n%i neighbors listed.\n", m);
1628 }
1629
1630 return 0;
1631 }
1632
1633 static int link_delete_send_message(sd_netlink *rtnl, int index) {
1634 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
1635 int r;
1636
1637 assert(rtnl);
1638
1639 r = sd_rtnl_message_new_link(rtnl, &req, RTM_DELLINK, index);
1640 if (r < 0)
1641 return rtnl_log_create_error(r);
1642
1643 r = sd_netlink_call(rtnl, req, 0, NULL);
1644 if (r < 0)
1645 return r;
1646
1647 return 0;
1648 }
1649
1650 static int link_delete(int argc, char *argv[], void *userdata) {
1651 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1652 _cleanup_set_free_ Set *indexes = NULL;
1653 int index, r, i;
1654 Iterator j;
1655 void *p;
1656
1657 r = sd_netlink_open(&rtnl);
1658 if (r < 0)
1659 return log_error_errno(r, "Failed to connect to netlink: %m");
1660
1661 indexes = set_new(NULL);
1662 if (!indexes)
1663 return log_oom();
1664
1665 for (i = 1; i < argc; i++) {
1666 r = parse_ifindex_or_ifname(argv[i], &index);
1667 if (r < 0)
1668 return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
1669
1670 r = set_put(indexes, INT_TO_PTR(index));
1671 if (r < 0)
1672 return log_oom();
1673 }
1674
1675 SET_FOREACH(p, indexes, j) {
1676 r = link_delete_send_message(rtnl, PTR_TO_INT(p));
1677 if (r < 0) {
1678 char ifname[IF_NAMESIZE + 1];
1679
1680 if (format_ifname(index, ifname))
1681 return log_error_errno(r, "Failed to delete interface %s: %m", ifname);
1682 else
1683 return log_error_errno(r, "Failed to delete interface %d: %m", index);
1684 }
1685 }
1686
1687 return r;
1688 }
1689
1690 static int help(void) {
1691 _cleanup_free_ char *link = NULL;
1692 int r;
1693
1694 r = terminal_urlify_man("networkctl", "1", &link);
1695 if (r < 0)
1696 return log_oom();
1697
1698 printf("%s [OPTIONS...]\n\n"
1699 "Query and control the networking subsystem.\n\n"
1700 " -h --help Show this help\n"
1701 " --version Show package version\n"
1702 " --no-pager Do not pipe output into a pager\n"
1703 " --no-legend Do not show the headers and footers\n"
1704 " -a --all Show status for all links\n"
1705 " -s --stats Show detailed link statics\n"
1706 "\nCommands:\n"
1707 " list [PATTERN...] List links\n"
1708 " status [PATTERN...] Show link status\n"
1709 " lldp [PATTERN...] Show LLDP neighbors\n"
1710 " label Show current address label entries in the kernel\n"
1711 " delete DEVICES Delete virtual netdevs\n"
1712 "\nSee the %s for details.\n"
1713 , program_invocation_short_name
1714 , link
1715 );
1716
1717 return 0;
1718 }
1719
1720 static int parse_argv(int argc, char *argv[]) {
1721
1722 enum {
1723 ARG_VERSION = 0x100,
1724 ARG_NO_PAGER,
1725 ARG_NO_LEGEND,
1726 };
1727
1728 static const struct option options[] = {
1729 { "help", no_argument, NULL, 'h' },
1730 { "version", no_argument, NULL, ARG_VERSION },
1731 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1732 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1733 { "all", no_argument, NULL, 'a' },
1734 { "stats", no_argument, NULL, 's' },
1735 {}
1736 };
1737
1738 int c;
1739
1740 assert(argc >= 0);
1741 assert(argv);
1742
1743 while ((c = getopt_long(argc, argv, "has", options, NULL)) >= 0) {
1744
1745 switch (c) {
1746
1747 case 'h':
1748 return help();
1749
1750 case ARG_VERSION:
1751 return version();
1752
1753 case ARG_NO_PAGER:
1754 arg_pager_flags |= PAGER_DISABLE;
1755 break;
1756
1757 case ARG_NO_LEGEND:
1758 arg_legend = false;
1759 break;
1760
1761 case 'a':
1762 arg_all = true;
1763 break;
1764
1765 case 's':
1766 arg_stats = true;
1767 break;
1768
1769 case '?':
1770 return -EINVAL;
1771
1772 default:
1773 assert_not_reached("Unhandled option");
1774 }
1775 }
1776
1777 return 1;
1778 }
1779
1780 static int networkctl_main(int argc, char *argv[]) {
1781 static const Verb verbs[] = {
1782 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
1783 { "status", VERB_ANY, VERB_ANY, 0, link_status },
1784 { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
1785 { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
1786 { "delete", 2, VERB_ANY, 0, link_delete },
1787 {}
1788 };
1789
1790 return dispatch_verb(argc, argv, verbs, NULL);
1791 }
1792
1793 static void warn_networkd_missing(void) {
1794
1795 if (access("/run/systemd/netif/state", F_OK) >= 0)
1796 return;
1797
1798 fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1799 }
1800
1801 static int run(int argc, char* argv[]) {
1802 int r;
1803
1804 log_show_color(true);
1805 log_parse_environment();
1806 log_open();
1807
1808 r = parse_argv(argc, argv);
1809 if (r <= 0)
1810 return r;
1811
1812 warn_networkd_missing();
1813
1814 return networkctl_main(argc, argv);
1815 }
1816
1817 DEFINE_MAIN_FUNCTION(run);