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