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