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