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