]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
Merge pull request #14990 from keszybz/nss-homed-fix
[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"
8d07de25 24#include "escape.h"
3f6fd1ba 25#include "ether-addr-util.h"
c967d2c7 26#include "ethtool-util.h"
34437b4f 27#include "fd-util.h"
ff7c88a2 28#include "format-table.h"
518a66ec 29#include "format-util.h"
0ef84b80 30#include "glob-util.h"
81fd1dd3 31#include "hwdb-util.h"
ee8c4568 32#include "local-addresses.h"
8752c575 33#include "locale-util.h"
10c71c36 34#include "logs-show.h"
d37b7627 35#include "macro.h"
4e2ca442 36#include "main-func.h"
3f6fd1ba
LP
37#include "netlink-util.h"
38#include "pager.h"
6bedfcbb 39#include "parse-util.h"
294bf0c3 40#include "pretty-print.h"
9cd8c766 41#include "set.h"
d308bb99 42#include "socket-netlink.h"
db73295a 43#include "socket-util.h"
760877e9 44#include "sort-util.h"
34437b4f 45#include "sparse-endian.h"
d054f0a4 46#include "stdio-util.h"
8b43440b 47#include "string-table.h"
8752c575 48#include "string-util.h"
3f6fd1ba 49#include "strv.h"
2388b2f4 50#include "strxcpyx.h"
288a74cc 51#include "terminal-util.h"
10c71c36 52#include "unit-def.h"
3f6fd1ba 53#include "verbs.h"
8d07de25 54#include "wifi-util.h"
ee8c4568 55
c82d1bf2
SS
56/* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
57#define NETDEV_KIND_MAX 64
58
8d07de25
ZJS
59/* use 128 kB for receive socket kernel queue, we shouldn't need more here */
60#define RCVBUF_SIZE (128*1024)
61
0221d68a 62static PagerFlags arg_pager_flags = 0;
ee8c4568 63static bool arg_legend = true;
9085f64a 64static bool arg_all = false;
a459b24f 65static bool arg_stats = false;
10c71c36
YW
66static bool arg_full = false;
67static unsigned arg_lines = 10;
ee8c4568 68
b55822c3 69static char *link_get_type_string(unsigned short iftype, sd_device *d) {
2c740afd 70 const char *t, *devtype;
ee8c4568
LP
71 char *p;
72
2c740afd
YW
73 if (d &&
74 sd_device_get_devtype(d, &devtype) >= 0 &&
75 !isempty(devtype))
76 return strdup(devtype);
ee8c4568
LP
77
78 t = arphrd_to_name(iftype);
b55822c3
JD
79 if (!t)
80 return NULL;
ee8c4568
LP
81
82 p = strdup(t);
83 if (!p)
b55822c3 84 return NULL;
ee8c4568
LP
85
86 ascii_strlower(p);
b55822c3 87 return p;
ee8c4568
LP
88}
89
ceb366df 90static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
7e5a080a
LP
91 assert(on);
92 assert(off);
93
ceb366df
ZJS
94 if (STRPTR_IN_SET(state, "routable", "enslaved") ||
95 (streq_ptr(name, "lo") && streq_ptr(state, "carrier"))) {
7e5a080a
LP
96 *on = ansi_highlight_green();
97 *off = ansi_normal();
98 } else if (streq_ptr(state, "degraded")) {
99 *on = ansi_highlight_yellow();
100 *off = ansi_normal();
101 } else
102 *on = *off = "";
103}
104
105static void setup_state_to_color(const char *state, const char **on, const char **off) {
106 assert(on);
107 assert(off);
108
109 if (streq_ptr(state, "configured")) {
110 *on = ansi_highlight_green();
111 *off = ansi_normal();
112 } else if (streq_ptr(state, "configuring")) {
113 *on = ansi_highlight_yellow();
114 *off = ansi_normal();
1cf03a4f 115 } else if (STRPTR_IN_SET(state, "failed", "linger")) {
7e5a080a
LP
116 *on = ansi_highlight_red();
117 *off = ansi_normal();
118 } else
119 *on = *off = "";
120}
121
b24281aa
SS
122typedef struct VxLanInfo {
123 uint32_t vni;
124 uint32_t link;
125
126 int local_family;
127 int group_family;
128
129 union in_addr_union local;
130 union in_addr_union group;
131
132 uint16_t dest_port;
133
134} VxLanInfo;
135
6d0c65ff 136typedef struct LinkInfo {
e997c4b0 137 char name[IFNAMSIZ+1];
c82d1bf2 138 char netdev_kind[NETDEV_KIND_MAX];
172353b1 139 sd_device *sd_device;
6d0c65ff 140 int ifindex;
1c4a6088 141 unsigned short iftype;
b147503e 142 struct ether_addr mac_address;
caa8538a 143 struct ether_addr permanent_mac_address;
b147503e 144 uint32_t mtu;
2c73f59b
SS
145 uint32_t min_mtu;
146 uint32_t max_mtu;
0307afc6
SS
147 uint32_t tx_queues;
148 uint32_t rx_queues;
511070ee 149 char **alternative_names;
b147503e 150
a459b24f
YW
151 union {
152 struct rtnl_link_stats64 stats64;
153 struct rtnl_link_stats stats;
154 };
155
42a63431
YW
156 uint64_t tx_bitrate;
157 uint64_t rx_bitrate;
335dd8ba 158
c82d1bf2
SS
159 /* bridge info */
160 uint32_t forward_delay;
161 uint32_t hello_time;
162 uint32_t max_age;
163 uint32_t ageing_time;
164 uint32_t stp_state;
165 uint16_t priority;
166 uint8_t mcast_igmp_version;
167
b24281aa
SS
168 /* vxlan info */
169 VxLanInfo vxlan_info;
170
c967d2c7
YW
171 /* ethtool info */
172 int autonegotiation;
50299121 173 uint64_t speed;
c967d2c7
YW
174 Duplex duplex;
175 NetDevPort port;
176
8d07de25 177 /* wlan info */
78404d22 178 enum nl80211_iftype wlan_iftype;
8d07de25
ZJS
179 char *ssid;
180 struct ether_addr bssid;
181
b147503e 182 bool has_mac_address:1;
caa8538a 183 bool has_permanent_mac_address:1;
0307afc6
SS
184 bool has_tx_queues:1;
185 bool has_rx_queues:1;
a459b24f
YW
186 bool has_stats64:1;
187 bool has_stats:1;
335dd8ba 188 bool has_bitrates:1;
c967d2c7 189 bool has_ethtool_link_info:1;
8d07de25 190 bool has_wlan_link_info:1;
172353b1
ZJS
191
192 bool needs_freeing:1;
6d0c65ff
LP
193} LinkInfo;
194
93bab288
YW
195static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
196 return CMP(a->ifindex, b->ifindex);
6d0c65ff
LP
197}
198
172353b1
ZJS
199static const LinkInfo* link_info_array_free(LinkInfo *array) {
200 for (unsigned i = 0; array && array[i].needs_freeing; i++) {
201 sd_device_unref(array[i].sd_device);
8d07de25 202 free(array[i].ssid);
511070ee 203 strv_free(array[i].alternative_names);
172353b1
ZJS
204 }
205
206 return mfree(array);
207}
208DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free);
209
c82d1bf2
SS
210static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
211 const char *received_kind;
212 int r;
213
214 assert(m);
215 assert(info);
216
217 r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
218 if (r < 0)
219 return r;
220
221 r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
222 if (r < 0)
223 return r;
224
225 r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
226 if (r < 0)
227 return r;
228
229 if (streq(received_kind, "bridge")) {
230 (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
231 (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
232 (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
233 (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
234 (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
235 (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
236 (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
b24281aa
SS
237
238 } else if (streq(received_kind, "vxlan")) {
239 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
240
241 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
242 if (r >= 0)
243 info->vxlan_info.group_family = AF_INET;
244 else {
245 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
246 if (r >= 0)
247 info->vxlan_info.group_family = AF_INET6;
248 }
249
250 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
251 if (r >= 0)
252 info->vxlan_info.local_family = AF_INET;
253 else {
254 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
255 if (r >= 0)
256 info->vxlan_info.local_family = AF_INET6;
257 }
258
259 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
260 (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
c82d1bf2
SS
261 }
262
263 strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
264
265 (void) sd_netlink_message_exit_container(m);
266 (void) sd_netlink_message_exit_container(m);
267
268 return 0;
269}
270
0ef84b80 271static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) {
f7581ed6 272 _cleanup_strv_free_ char **altnames = NULL;
e997c4b0 273 const char *name;
a6962904 274 int ifindex, r;
c82d1bf2 275 uint16_t type;
6d0c65ff 276
e997c4b0
LP
277 assert(m);
278 assert(info);
6d0c65ff 279
e997c4b0
LP
280 r = sd_netlink_message_get_type(m, &type);
281 if (r < 0)
282 return r;
6d0c65ff 283
e997c4b0
LP
284 if (type != RTM_NEWLINK)
285 return 0;
6d0c65ff 286
a6962904 287 r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
e997c4b0
LP
288 if (r < 0)
289 return r;
6d0c65ff 290
e997c4b0
LP
291 r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
292 if (r < 0)
293 return r;
6d0c65ff 294
f7581ed6
YW
295 r = sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
296 if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENODATA))
297 return r;
298
a6962904
YW
299 if (patterns) {
300 char str[DECIMAL_STR_MAX(int)];
0ef84b80
ZJS
301 size_t pos;
302
303 assert(matched_patterns);
a6962904
YW
304
305 xsprintf(str, "%i", ifindex);
0ef84b80
ZJS
306 if (!strv_fnmatch_full(patterns, str, 0, &pos) &&
307 !strv_fnmatch_full(patterns, name, 0, &pos)) {
f7581ed6
YW
308 bool match = false;
309 char **p;
310
311 STRV_FOREACH(p, altnames)
0ef84b80 312 if (strv_fnmatch_full(patterns, *p, 0, &pos)) {
f7581ed6
YW
313 match = true;
314 break;
315 }
316 if (!match)
317 return 0;
318 }
0ef84b80
ZJS
319
320 matched_patterns[pos] = true;
a6962904
YW
321 }
322
b147503e 323 r = sd_rtnl_message_link_get_type(m, &info->iftype);
e997c4b0
LP
324 if (r < 0)
325 return r;
6d0c65ff 326
2388b2f4 327 strscpy(info->name, sizeof info->name, name);
a6962904 328 info->ifindex = ifindex;
f7581ed6 329 info->alternative_names = TAKE_PTR(altnames);
b147503e
LP
330
331 info->has_mac_address =
332 sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 &&
72e551f4 333 memcmp(&info->mac_address, &ETHER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
b147503e 334
caa8538a 335 info->has_permanent_mac_address =
6666c4fa 336 ethtool_get_permanent_macaddr(NULL, info->name, &info->permanent_mac_address) >= 0 &&
caa8538a
YW
337 memcmp(&info->permanent_mac_address, &ETHER_ADDR_NULL, sizeof(struct ether_addr)) != 0 &&
338 memcmp(&info->permanent_mac_address, &info->mac_address, sizeof(struct ether_addr)) != 0;
339
c06ff86e
YW
340 (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu);
341 (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
342 (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
2c73f59b 343
0307afc6
SS
344 info->has_rx_queues =
345 sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
346 info->rx_queues > 0;
347
348 info->has_tx_queues =
349 sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
350 info->tx_queues > 0;
351
a459b24f
YW
352 if (sd_netlink_message_read(m, IFLA_STATS64, sizeof info->stats64, &info->stats64) >= 0)
353 info->has_stats64 = true;
354 else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
355 info->has_stats = true;
356
c82d1bf2
SS
357 /* fill kind info */
358 (void) decode_netdev(m, info);
359
e997c4b0
LP
360 return 1;
361}
362
335dd8ba
YW
363static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
364 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
365 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
366 _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
367 int r;
368
369 if (asprintf(&ifindex_str, "%i", link->ifindex) < 0)
370 return -ENOMEM;
371
372 r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path);
373 if (r < 0)
374 return r;
375
376 r = sd_bus_call_method(
377 bus,
378 "org.freedesktop.network1",
379 path,
380 "org.freedesktop.DBus.Properties",
381 "Get",
382 &error,
383 &reply,
384 "ss",
385 "org.freedesktop.network1.Link",
386 "BitRates");
387 if (r < 0) {
4023637a
ZJS
388 bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) ||
389 sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
8210a61a
ZJS
390
391 return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
392 r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
335dd8ba
YW
393 }
394
42a63431 395 r = sd_bus_message_enter_container(reply, 'v', "(tt)");
335dd8ba
YW
396 if (r < 0)
397 return bus_log_parse_error(r);
398
42a63431 399 r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
335dd8ba
YW
400 if (r < 0)
401 return bus_log_parse_error(r);
402
403 r = sd_bus_message_exit_container(reply);
404 if (r < 0)
405 return bus_log_parse_error(r);
406
e813de54 407 link->has_bitrates = link->tx_bitrate != UINT64_MAX && link->rx_bitrate != UINT64_MAX;
335dd8ba
YW
408
409 return 0;
410}
411
516e9c80
ZJS
412static void acquire_ether_link_info(int *fd, LinkInfo *link) {
413 if (ethtool_get_link_info(fd, link->name,
414 &link->autonegotiation,
415 &link->speed,
416 &link->duplex,
417 &link->port) >= 0)
418 link->has_ethtool_link_info = true;
419}
420
8d07de25
ZJS
421static void acquire_wlan_link_info(LinkInfo *link) {
422 _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
423 const char *type = NULL;
78404d22 424 int r, k = 0;
8d07de25
ZJS
425
426 if (link->sd_device)
427 (void) sd_device_get_devtype(link->sd_device, &type);
428 if (!streq_ptr(type, "wlan"))
429 return;
430
431 r = sd_genl_socket_open(&genl);
432 if (r < 0) {
433 log_debug_errno(r, "Failed to open generic netlink socket: %m");
434 return;
435 }
436
437 (void) sd_netlink_inc_rcvbuf(genl, RCVBUF_SIZE);
438
78404d22 439 r = wifi_get_interface(genl, link->ifindex, &link->wlan_iftype, &link->ssid);
8d07de25
ZJS
440 if (r < 0)
441 log_debug_errno(r, "%s: failed to query ssid: %m", link->name);
442
33ebda2e 443 if (link->wlan_iftype == NL80211_IFTYPE_STATION) {
78404d22
YW
444 k = wifi_get_station(genl, link->ifindex, &link->bssid);
445 if (k < 0)
446 log_debug_errno(k, "%s: failed to query bssid: %m", link->name);
447 }
8d07de25
ZJS
448
449 link->has_wlan_link_info = r > 0 || k > 0;
450}
451
335dd8ba 452static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
4afd3348 453 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
172353b1 454 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
c967d2c7 455 _cleanup_close_ int fd = -1;
335dd8ba 456 size_t allocated = 0, c = 0, j;
7e5a080a 457 sd_netlink_message *i;
7d367b45
LP
458 int r;
459
b147503e 460 assert(rtnl);
7d367b45 461 assert(ret);
ee8c4568 462
ee8c4568
LP
463 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
464 if (r < 0)
465 return rtnl_log_create_error(r);
466
1c4baffc 467 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
468 if (r < 0)
469 return rtnl_log_create_error(r);
470
1c4baffc 471 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
472 if (r < 0)
473 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 474
0ef84b80
ZJS
475 _cleanup_free_ bool *matched_patterns = NULL;
476 if (patterns) {
477 matched_patterns = new0(bool, strv_length(patterns));
478 if (!matched_patterns)
479 return log_oom();
480 }
481
7e5a080a 482 for (i = reply; i; i = sd_netlink_message_next(i)) {
172353b1 483 if (!GREEDY_REALLOC0(links, allocated, c + 2)) /* We keep one trailing one as marker */
7e5a080a 484 return -ENOMEM;
7d367b45 485
0ef84b80 486 r = decode_link(i, links + c, patterns, matched_patterns);
7e5a080a
LP
487 if (r < 0)
488 return r;
c967d2c7
YW
489 if (r == 0)
490 continue;
491
172353b1
ZJS
492 links[c].needs_freeing = true;
493
494 char devid[2 + DECIMAL_STR_MAX(int)];
495 xsprintf(devid, "n%i", links[c].ifindex);
496 (void) sd_device_new_from_device_id(&links[c].sd_device, devid);
497
516e9c80 498 acquire_ether_link_info(&fd, &links[c]);
8d07de25 499 acquire_wlan_link_info(&links[c]);
c967d2c7
YW
500
501 c++;
7e5a080a
LP
502 }
503
0ef84b80
ZJS
504 /* Look if we matched all our arguments that are not globs. It
505 * is OK for a glob to match nothing, but not for an exact argument. */
506 for (size_t pos = 0; pos < strv_length(patterns); pos++) {
507 if (matched_patterns[pos])
508 continue;
509
510 if (string_is_glob(patterns[pos]))
511 log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
512 patterns[pos]);
513 else
514 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
515 "Interface \"%s\" not found.", patterns[pos]);
516 }
517
93bab288 518 typesafe_qsort(links, c, link_info_compare);
7e5a080a 519
335dd8ba
YW
520 if (bus)
521 for (j = 0; j < c; j++)
522 (void) acquire_link_bitrates(bus, links + j);
523
1cc6c93a 524 *ret = TAKE_PTR(links);
7e5a080a 525
0ef84b80
ZJS
526 if (patterns && c == 0)
527 log_warning("No interfaces matched.");
528
7e5a080a 529 return (int) c;
7d367b45 530}
ee8c4568 531
7d367b45 532static int list_links(int argc, char *argv[], void *userdata) {
b147503e 533 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
172353b1 534 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
ff7c88a2
YW
535 _cleanup_(table_unrefp) Table *table = NULL;
536 TableCell *cell;
b147503e
LP
537 int c, i, r;
538
539 r = sd_netlink_open(&rtnl);
540 if (r < 0)
541 return log_error_errno(r, "Failed to connect to netlink: %m");
7d367b45 542
335dd8ba 543 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
6d0c65ff 544 if (c < 0)
7d367b45
LP
545 return c;
546
0221d68a 547 (void) pager_open(arg_pager_flags);
7d367b45 548
4252171a 549 table = table_new("idx", "link", "type", "operational", "setup");
ff7c88a2
YW
550 if (!table)
551 return log_oom();
552
a42d9490
YW
553 if (arg_full)
554 table_set_width(table, 0);
555
ff7c88a2
YW
556 table_set_header(table, arg_legend);
557
558 assert_se(cell = table_get_cell(table, 0, 0));
559 (void) table_set_minimum_width(table, cell, 3);
560 (void) table_set_weight(table, cell, 0);
81914d9f 561 (void) table_set_ellipsize_percent(table, cell, 100);
ff7c88a2
YW
562 (void) table_set_align_percent(table, cell, 100);
563
564 assert_se(cell = table_get_cell(table, 0, 1));
81914d9f 565 (void) table_set_ellipsize_percent(table, cell, 100);
6d0c65ff
LP
566
567 for (i = 0; i < c; i++) {
ab1525bc 568 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
d57c365b
LP
569 const char *on_color_operational, *off_color_operational,
570 *on_color_setup, *off_color_setup;
ee8c4568 571 _cleanup_free_ char *t = NULL;
ee8c4568 572
4abd866d 573 (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
ceb366df 574 operational_state_to_color(links[i].name, operational_state, &on_color_operational, &off_color_operational);
d57c365b 575
33d5013d
LP
576 r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
577 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
578 setup_state = strdup("unmanaged");
d57c365b 579 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
ee8c4568 580
172353b1 581 t = link_get_type_string(links[i].iftype, links[i].sd_device);
ee8c4568 582
ff7c88a2 583 r = table_add_many(table,
8d0e0af2 584 TABLE_INT, links[i].ifindex,
ff7c88a2 585 TABLE_STRING, links[i].name,
8d0e0af2
YW
586 TABLE_STRING, strna(t),
587 TABLE_STRING, strna(operational_state),
588 TABLE_SET_COLOR, on_color_operational,
589 TABLE_STRING, strna(setup_state),
590 TABLE_SET_COLOR, on_color_setup);
ff7c88a2 591 if (r < 0)
bd17fa8c 592 return table_log_add_error(r);
ee8c4568
LP
593 }
594
ff7c88a2
YW
595 r = table_print(table, NULL);
596 if (r < 0)
597 return log_error_errno(r, "Failed to print table: %m");
598
ee8c4568 599 if (arg_legend)
6d0c65ff 600 printf("\n%i links listed.\n", c);
ee8c4568
LP
601
602 return 0;
603}
604
c09da729 605/* IEEE Organizationally Unique Identifier vendor string */
b147503e 606static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
81fd1dd3 607 const char *description;
fbd0b64f 608 char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
81fd1dd3
TG
609 int r;
610
611 assert(ret);
c09da729 612
888943fc
LP
613 if (!hwdb)
614 return -EINVAL;
615
81fd1dd3
TG
616 if (!mac)
617 return -EINVAL;
618
c09da729
TG
619 /* skip commonly misused 00:00:00 (Xerox) prefix */
620 if (memcmp(mac, "\0\0\0", 3) == 0)
621 return -EINVAL;
622
d054f0a4
DM
623 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
624 ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 625
81fd1dd3
TG
626 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
627 if (r < 0)
628 return r;
c09da729 629
81fd1dd3
TG
630 desc = strdup(description);
631 if (!desc)
632 return -ENOMEM;
c09da729 633
81fd1dd3
TG
634 *ret = desc;
635
636 return 0;
c09da729
TG
637}
638
69fb1176 639static int get_gateway_description(
1c4baffc 640 sd_netlink *rtnl,
81fd1dd3 641 sd_hwdb *hwdb,
69fb1176
LP
642 int ifindex,
643 int family,
644 union in_addr_union *gateway,
645 char **gateway_description) {
4afd3348 646 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
1c4baffc 647 sd_netlink_message *m;
c09da729
TG
648 int r;
649
650 assert(rtnl);
651 assert(ifindex >= 0);
4c701096 652 assert(IN_SET(family, AF_INET, AF_INET6));
c09da729
TG
653 assert(gateway);
654 assert(gateway_description);
655
656 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
657 if (r < 0)
658 return r;
659
1c4baffc 660 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
661 if (r < 0)
662 return r;
663
1c4baffc 664 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
665 if (r < 0)
666 return r;
667
1c4baffc 668 for (m = reply; m; m = sd_netlink_message_next(m)) {
67a46833
YW
669 union in_addr_union gw = IN_ADDR_NULL;
670 struct ether_addr mac = ETHER_ADDR_NULL;
c09da729
TG
671 uint16_t type;
672 int ifi, fam;
673
1c4baffc 674 r = sd_netlink_message_get_errno(m);
c09da729
TG
675 if (r < 0) {
676 log_error_errno(r, "got error: %m");
677 continue;
678 }
679
1c4baffc 680 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
681 if (r < 0) {
682 log_error_errno(r, "could not get type: %m");
683 continue;
684 }
685
686 if (type != RTM_NEWNEIGH) {
687 log_error("type is not RTM_NEWNEIGH");
688 continue;
689 }
690
691 r = sd_rtnl_message_neigh_get_family(m, &fam);
692 if (r < 0) {
693 log_error_errno(r, "could not get family: %m");
694 continue;
695 }
696
697 if (fam != family) {
698 log_error("family is not correct");
699 continue;
700 }
701
702 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
703 if (r < 0) {
144232a8 704 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
705 continue;
706 }
707
708 if (ifindex > 0 && ifi != ifindex)
709 continue;
710
711 switch (fam) {
712 case AF_INET:
1c4baffc 713 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
714 if (r < 0)
715 continue;
716
717 break;
718 case AF_INET6:
1c4baffc 719 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
720 if (r < 0)
721 continue;
722
723 break;
724 default:
725 continue;
726 }
727
728 if (!in_addr_equal(fam, &gw, gateway))
729 continue;
730
49808e0e 731 r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
c09da729
TG
732 if (r < 0)
733 continue;
734
735 r = ieee_oui(hwdb, &mac, gateway_description);
736 if (r < 0)
737 continue;
738
739 return 0;
740 }
741
742 return -ENODATA;
743}
744
536cdd07
YW
745static int dump_list(Table *table, const char *prefix, char * const *l) {
746 int r;
747
748 if (strv_isempty(l))
749 return 0;
750
751 r = table_add_many(table,
752 TABLE_EMPTY,
753 TABLE_STRING, prefix,
754 TABLE_STRV, l);
755 if (r < 0)
756 return table_log_add_error(r);
757
758 return 0;
759}
760
69fb1176 761static int dump_gateways(
1c4baffc 762 sd_netlink *rtnl,
81fd1dd3 763 sd_hwdb *hwdb,
98d5bef3 764 Table *table,
69fb1176 765 int ifindex) {
b6a3ca6d 766 _cleanup_free_ struct local_address *local = NULL;
536cdd07 767 _cleanup_strv_free_ char **buf = NULL;
b6a3ca6d 768 int r, n, i;
c09da729 769
837f57da 770 assert(rtnl);
98d5bef3 771 assert(table);
837f57da 772
b6a3ca6d 773 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
536cdd07 774 if (n <= 0)
b6a3ca6d 775 return n;
c09da729 776
b6a3ca6d 777 for (i = 0; i < n; i++) {
98d5bef3 778 _cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
536cdd07 779 char name[IF_NAMESIZE+1];
c09da729 780
b6a3ca6d 781 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 782 if (r < 0)
b6a3ca6d 783 return r;
c09da729 784
69fb1176 785 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
c09da729 786 if (r < 0)
536cdd07 787 log_debug_errno(r, "Could not get description of gateway, ignoring: %m");
c09da729 788
98d5bef3
YW
789 if (description) {
790 with_description = strjoin(gateway, " (", description, ")");
791 if (!with_description)
536cdd07 792 return log_oom();
98d5bef3 793 }
1693a943 794
536cdd07
YW
795 /* Show interface name for the entry if we show entries for all interfaces */
796 r = strv_extendf(&buf, "%s%s%s",
797 with_description ?: gateway,
798 ifindex <= 0 ? " on " : "",
799 ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
98d5bef3 800 if (r < 0)
536cdd07 801 return log_oom();
c09da729
TG
802 }
803
536cdd07 804 return dump_list(table, "Gateway:", buf);
c09da729
TG
805}
806
69fb1176 807static int dump_addresses(
1c4baffc 808 sd_netlink *rtnl,
98d5bef3 809 Table *table,
69fb1176
LP
810 int ifindex) {
811
ee8c4568 812 _cleanup_free_ struct local_address *local = NULL;
cdf01b36 813 _cleanup_free_ char *dhcp4_address = NULL;
536cdd07 814 _cleanup_strv_free_ char **buf = NULL;
ee8c4568
LP
815 int r, n, i;
816
837f57da 817 assert(rtnl);
98d5bef3 818 assert(table);
837f57da 819
1d050e1e 820 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
536cdd07 821 if (n <= 0)
ee8c4568
LP
822 return n;
823
cdf01b36
SS
824 (void) sd_network_link_get_dhcp4_address(ifindex, &dhcp4_address);
825
ee8c4568
LP
826 for (i = 0; i < n; i++) {
827 _cleanup_free_ char *pretty = NULL;
536cdd07 828 char name[IF_NAMESIZE+1];
1693a943 829
98d5bef3
YW
830 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
831 if (r < 0)
832 return r;
1693a943 833
cdf01b36
SS
834 if (dhcp4_address && streq(pretty, dhcp4_address)) {
835 _cleanup_free_ char *p = NULL;
836
837 p = pretty;
838 pretty = strjoin(pretty , " (DHCP4)");
839 if (!pretty)
840 return log_oom();
841 }
842
536cdd07
YW
843 r = strv_extendf(&buf, "%s%s%s",
844 pretty,
845 ifindex <= 0 ? " on " : "",
846 ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
98d5bef3 847 if (r < 0)
536cdd07 848 return log_oom();
ee8c4568
LP
849 }
850
536cdd07 851 return dump_list(table, "Address:", buf);
ee8c4568
LP
852}
853
d37b7627
SS
854static int dump_address_labels(sd_netlink *rtnl) {
855 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
0232beed 856 _cleanup_(table_unrefp) Table *table = NULL;
d37b7627 857 sd_netlink_message *m;
0232beed 858 TableCell *cell;
d37b7627
SS
859 int r;
860
861 assert(rtnl);
862
863 r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
864 if (r < 0)
865 return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
866
867 r = sd_netlink_message_request_dump(req, true);
868 if (r < 0)
869 return r;
870
871 r = sd_netlink_call(rtnl, req, 0, &reply);
872 if (r < 0)
873 return r;
874
4252171a 875 table = table_new("label", "prefix/prefixlen");
0232beed 876 if (!table)
bd17fa8c 877 return log_oom();
0232beed 878
a42d9490
YW
879 if (arg_full)
880 table_set_width(table, 0);
881
ad5555b4 882 r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
0232beed
YW
883 if (r < 0)
884 return r;
885
886 assert_se(cell = table_get_cell(table, 0, 0));
887 (void) table_set_align_percent(table, cell, 100);
81914d9f 888 (void) table_set_ellipsize_percent(table, cell, 100);
0232beed
YW
889
890 assert_se(cell = table_get_cell(table, 0, 1));
891 (void) table_set_align_percent(table, cell, 100);
d37b7627
SS
892
893 for (m = reply; m; m = sd_netlink_message_next(m)) {
894 _cleanup_free_ char *pretty = NULL;
67a46833 895 union in_addr_union prefix = IN_ADDR_NULL;
d37b7627
SS
896 uint8_t prefixlen;
897 uint32_t label;
898
899 r = sd_netlink_message_get_errno(m);
900 if (r < 0) {
901 log_error_errno(r, "got error: %m");
902 continue;
903 }
904
905 r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
906 if (r < 0 && r != -ENODATA) {
907 log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
908 continue;
909 }
910
911 r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
912 if (r < 0)
913 continue;
914
915 r = in_addr_to_string(AF_INET6, &prefix, &pretty);
916 if (r < 0)
917 continue;
918
919 r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
920 if (r < 0)
921 continue;
922
81914d9f 923 r = table_add_cell(table, NULL, TABLE_UINT32, &label);
0232beed 924 if (r < 0)
bd17fa8c 925 return table_log_add_error(r);
0232beed 926
8d0e0af2 927 r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
0232beed 928 if (r < 0)
bd17fa8c 929 return table_log_add_error(r);
d37b7627
SS
930 }
931
bd17fa8c
YW
932 r = table_print(table, NULL);
933 if (r < 0)
934 return log_error_errno(r, "Failed to print table: %m");
935
936 return 0;
d37b7627
SS
937}
938
939static int list_address_labels(int argc, char *argv[], void *userdata) {
940 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
941 int r;
942
943 r = sd_netlink_open(&rtnl);
944 if (r < 0)
945 return log_error_errno(r, "Failed to connect to netlink: %m");
946
947 dump_address_labels(rtnl);
948
949 return 0;
950}
951
837f57da
LP
952static int open_lldp_neighbors(int ifindex, FILE **ret) {
953 _cleanup_free_ char *p = NULL;
954 FILE *f;
955
956 if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
957 return -ENOMEM;
958
959 f = fopen(p, "re");
960 if (!f)
961 return -errno;
962
963 *ret = f;
964 return 0;
965}
966
967static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
968 _cleanup_free_ void *raw = NULL;
969 size_t l;
970 le64_t u;
971 int r;
972
973 assert(f);
974 assert(ret);
975
976 l = fread(&u, 1, sizeof(u), f);
977 if (l == 0 && feof(f))
978 return 0;
979 if (l != sizeof(u))
980 return -EBADMSG;
981
d23c3e4c
FB
982 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
983 if (le64toh(u) >= 4096)
984 return -EBADMSG;
985
837f57da
LP
986 raw = new(uint8_t, le64toh(u));
987 if (!raw)
988 return -ENOMEM;
989
990 if (fread(raw, 1, le64toh(u), f) != le64toh(u))
991 return -EBADMSG;
992
993 r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
994 if (r < 0)
995 return r;
996
997 return 1;
998}
999
98d5bef3 1000static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
536cdd07 1001 _cleanup_strv_free_ char **buf = NULL;
837f57da 1002 _cleanup_fclose_ FILE *f = NULL;
536cdd07 1003 int r;
837f57da 1004
98d5bef3 1005 assert(table);
837f57da
LP
1006 assert(prefix);
1007 assert(ifindex > 0);
1008
1009 r = open_lldp_neighbors(ifindex, &f);
98d5bef3
YW
1010 if (r == -ENOENT)
1011 return 0;
837f57da
LP
1012 if (r < 0)
1013 return r;
1014
1015 for (;;) {
1016 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
1017 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1018
1019 r = next_lldp_neighbor(f, &n);
1020 if (r < 0)
1021 return r;
1022 if (r == 0)
1023 break;
1024
837f57da
LP
1025 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1026 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1027 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1028
536cdd07
YW
1029 r = strv_extendf(&buf, "%s on port %s%s%s%s",
1030 strna(system_name),
1031 strna(port_id),
1032 isempty(port_description) ? "" : " (",
1033 strempty(port_description),
1034 isempty(port_description) ? "" : ")");
98d5bef3 1035 if (r < 0)
536cdd07 1036 return log_oom();
837f57da
LP
1037 }
1038
536cdd07 1039 return dump_list(table, prefix, buf);
837f57da
LP
1040}
1041
98d5bef3 1042static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
b295beea 1043 unsigned c;
98d5bef3 1044 int r;
b295beea
LP
1045
1046 assert(prefix);
1047
1048 if (!ifindexes || ifindexes[0] <= 0)
98d5bef3 1049 return 0;
b295beea
LP
1050
1051 for (c = 0; ifindexes[c] > 0; c++) {
8d0e0af2
YW
1052 r = table_add_many(table,
1053 TABLE_EMPTY,
1054 TABLE_STRING, c == 0 ? prefix : "",
1055 TABLE_IFINDEX, ifindexes[c]);
98d5bef3 1056 if (r < 0)
bd17fa8c 1057 return table_log_add_error(r);
b295beea 1058 }
98d5bef3
YW
1059
1060 return 0;
b295beea
LP
1061}
1062
a459b24f 1063#define DUMP_STATS_ONE(name, val_name) \
8d0e0af2
YW
1064 r = table_add_many(table, \
1065 TABLE_EMPTY, \
1066 TABLE_STRING, name ":"); \
a459b24f 1067 if (r < 0) \
bd17fa8c 1068 return table_log_add_error(r); \
8d0e0af2
YW
1069 r = table_add_cell(table, NULL, \
1070 info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
a459b24f
YW
1071 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
1072 if (r < 0) \
bd17fa8c 1073 return table_log_add_error(r);
a459b24f
YW
1074
1075static int dump_statistics(Table *table, const LinkInfo *info) {
1076 int r;
1077
1078 if (!arg_stats)
1079 return 0;
1080
1081 if (!info->has_stats64 && !info->has_stats)
1082 return 0;
1083
1084 DUMP_STATS_ONE("Rx Packets", rx_packets);
1085 DUMP_STATS_ONE("Tx Packets", tx_packets);
1086 DUMP_STATS_ONE("Rx Bytes", rx_bytes);
1087 DUMP_STATS_ONE("Tx Bytes", tx_bytes);
1088 DUMP_STATS_ONE("Rx Errors", rx_errors);
1089 DUMP_STATS_ONE("Tx Errors", tx_errors);
1090 DUMP_STATS_ONE("Rx Dropped", rx_dropped);
1091 DUMP_STATS_ONE("Tx Dropped", tx_dropped);
1092 DUMP_STATS_ONE("Multicast Packets", multicast);
1093 DUMP_STATS_ONE("Collisions", collisions);
1094
1095 return 0;
1096}
1097
10c71c36
YW
1098static OutputFlags get_output_flags(void) {
1099 return
1100 arg_all * OUTPUT_SHOW_ALL |
1101 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1102 colors_enabled() * OUTPUT_COLOR;
1103}
1104
1105static int show_logs(const LinkInfo *info) {
1106 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1107 int r;
1108
1109 if (arg_lines == 0)
1110 return 0;
1111
1112 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
1113 if (r < 0)
1114 return log_error_errno(r, "Failed to open journal: %m");
1115
1116 r = add_match_this_boot(j, NULL);
1117 if (r < 0)
1118 return log_error_errno(r, "Failed to add boot matches: %m");
1119
1120 if (info) {
1121 char m1[STRLEN("_KERNEL_DEVICE=n") + DECIMAL_STR_MAX(int)];
1122 const char *m2, *m3;
1123
1124 /* kernel */
1125 xsprintf(m1, "_KERNEL_DEVICE=n%i", info->ifindex);
1126 /* networkd */
1127 m2 = strjoina("INTERFACE=", info->name);
1128 /* udevd */
1129 m3 = strjoina("DEVICE=", info->name);
1130
1131 (void)(
1132 (r = sd_journal_add_match(j, m1, 0)) ||
1133 (r = sd_journal_add_disjunction(j)) ||
1134 (r = sd_journal_add_match(j, m2, 0)) ||
1135 (r = sd_journal_add_disjunction(j)) ||
1136 (r = sd_journal_add_match(j, m3, 0))
1137 );
1138 if (r < 0)
1139 return log_error_errno(r, "Failed to add link matches: %m");
1140 } else {
1141 r = add_matches_for_unit(j, "systemd-networkd.service");
1142 if (r < 0)
1143 return log_error_errno(r, "Failed to add unit matches: %m");
1144
1145 r = add_matches_for_unit(j, "systemd-networkd-wait-online.service");
1146 if (r < 0)
1147 return log_error_errno(r, "Failed to add unit matches: %m");
1148 }
1149
1150 return show_journal(
1151 stdout,
1152 j,
1153 OUTPUT_SHORT,
1154 0,
1155 0,
1156 arg_lines,
1157 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
1158 NULL);
1159}
1160
69fb1176 1161static int link_status_one(
1c4baffc 1162 sd_netlink *rtnl,
81fd1dd3 1163 sd_hwdb *hwdb,
b147503e
LP
1164 const LinkInfo *info) {
1165
3df9bec5 1166 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
64d6c229 1167 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
373d9f17 1168 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 1169 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b 1170 const char *on_color_operational, *off_color_operational,
98d5bef3 1171 *on_color_setup, *off_color_setup;
b295beea 1172 _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
98d5bef3
YW
1173 _cleanup_(table_unrefp) Table *table = NULL;
1174 TableCell *cell;
b147503e 1175 int r;
9085f64a
LP
1176
1177 assert(rtnl);
b147503e 1178 assert(info);
9085f64a 1179
b147503e 1180 (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
ceb366df 1181 operational_state_to_color(info->name, operational_state, &on_color_operational, &off_color_operational);
d57c365b 1182
33d5013d
LP
1183 r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
1184 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
1185 setup_state = strdup("unmanaged");
d57c365b 1186 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 1187
b147503e
LP
1188 (void) sd_network_link_get_dns(info->ifindex, &dns);
1189 (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
1190 (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
1191 (void) sd_network_link_get_ntp(info->ifindex, &ntp);
9085f64a 1192
172353b1
ZJS
1193 if (info->sd_device) {
1194 (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link);
1195 (void) sd_device_get_property_value(info->sd_device, "ID_NET_DRIVER", &driver);
1196 (void) sd_device_get_property_value(info->sd_device, "ID_PATH", &path);
9085f64a 1197
172353b1
ZJS
1198 if (sd_device_get_property_value(info->sd_device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
1199 (void) sd_device_get_property_value(info->sd_device, "ID_VENDOR", &vendor);
9085f64a 1200
172353b1
ZJS
1201 if (sd_device_get_property_value(info->sd_device, "ID_MODEL_FROM_DATABASE", &model) < 0)
1202 (void) sd_device_get_property_value(info->sd_device, "ID_MODEL", &model);
9085f64a
LP
1203 }
1204
172353b1 1205 t = link_get_type_string(info->iftype, info->sd_device);
b1acce80 1206
4abd866d 1207 (void) sd_network_link_get_network_file(info->ifindex, &network);
df3fb561 1208
4abd866d
LP
1209 (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
1210 (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
0d4ad91d 1211
4252171a 1212 table = table_new("dot", "key", "value");
98d5bef3 1213 if (!table)
bd17fa8c 1214 return log_oom();
98d5bef3 1215
a42d9490
YW
1216 if (arg_full)
1217 table_set_width(table, 0);
1218
81914d9f
YW
1219 assert_se(cell = table_get_cell(table, 0, 0));
1220 (void) table_set_ellipsize_percent(table, cell, 100);
1221
1222 assert_se(cell = table_get_cell(table, 0, 1));
1223 (void) table_set_ellipsize_percent(table, cell, 100);
1224
98d5bef3
YW
1225 table_set_header(table, false);
1226
8d0e0af2
YW
1227 r = table_add_many(table,
1228 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1229 TABLE_SET_COLOR, on_color_operational);
98d5bef3 1230 if (r < 0)
bd17fa8c 1231 return table_log_add_error(r);
81914d9f 1232 r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
98d5bef3 1233 if (r < 0)
bd17fa8c 1234 return table_log_add_error(r);
81914d9f 1235 (void) table_set_align_percent(table, cell, 0);
98d5bef3 1236
8d0e0af2
YW
1237 r = table_add_many(table,
1238 TABLE_EMPTY,
1239 TABLE_EMPTY,
1240 TABLE_STRING, "Link File:",
1241 TABLE_SET_ALIGN_PERCENT, 100,
1242 TABLE_STRING, strna(link),
1243 TABLE_EMPTY,
1244 TABLE_STRING, "Network File:",
1245 TABLE_STRING, strna(network),
1246 TABLE_EMPTY,
1247 TABLE_STRING, "Type:",
1248 TABLE_STRING, strna(t),
1249 TABLE_EMPTY,
1250 TABLE_STRING, "State:");
98d5bef3 1251 if (r < 0)
bd17fa8c 1252 return table_log_add_error(r);
98d5bef3 1253 r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
64e7ebde
YW
1254 on_color_operational, strna(operational_state), off_color_operational,
1255 on_color_setup, strna(setup_state), off_color_setup);
98d5bef3 1256 if (r < 0)
bd17fa8c 1257 return table_log_add_error(r);
98d5bef3 1258
536cdd07
YW
1259 r = dump_list(table, "Alternative Names:", info->alternative_names);
1260 if (r < 0)
1261 return r;
511070ee 1262
98d5bef3 1263 if (path) {
8d0e0af2
YW
1264 r = table_add_many(table,
1265 TABLE_EMPTY,
1266 TABLE_STRING, "Path:",
1267 TABLE_STRING, path);
98d5bef3 1268 if (r < 0)
bd17fa8c 1269 return table_log_add_error(r);
98d5bef3
YW
1270 }
1271 if (driver) {
8d0e0af2
YW
1272 r = table_add_many(table,
1273 TABLE_EMPTY,
1274 TABLE_STRING, "Driver:",
1275 TABLE_STRING, driver);
98d5bef3 1276 if (r < 0)
bd17fa8c 1277 return table_log_add_error(r);
98d5bef3
YW
1278 }
1279 if (vendor) {
8d0e0af2
YW
1280 r = table_add_many(table,
1281 TABLE_EMPTY,
1282 TABLE_STRING, "Vendor:",
1283 TABLE_STRING, vendor);
98d5bef3 1284 if (r < 0)
bd17fa8c 1285 return table_log_add_error(r);
98d5bef3
YW
1286 }
1287 if (model) {
8d0e0af2
YW
1288 r = table_add_many(table,
1289 TABLE_EMPTY,
1290 TABLE_STRING, "Model:",
1291 TABLE_STRING, model);
98d5bef3 1292 if (r < 0)
bd17fa8c 1293 return table_log_add_error(r);
98d5bef3 1294 }
9085f64a 1295
b147503e 1296 if (info->has_mac_address) {
888943fc 1297 _cleanup_free_ char *description = NULL;
db73295a 1298 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc 1299
4abd866d 1300 (void) ieee_oui(hwdb, &info->mac_address, &description);
888943fc 1301
8d0e0af2
YW
1302 r = table_add_many(table,
1303 TABLE_EMPTY,
1304 TABLE_STRING, "HW Address:");
98d5bef3 1305 if (r < 0)
bd17fa8c 1306 return table_log_add_error(r);
98d5bef3 1307 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
64e7ebde
YW
1308 ether_addr_to_string(&info->mac_address, ea),
1309 description ? " (" : "",
1310 strempty(description),
1311 description ? ")" : "");
98d5bef3 1312 if (r < 0)
bd17fa8c 1313 return table_log_add_error(r);
db73295a 1314 }
9085f64a 1315
caa8538a
YW
1316 if (info->has_permanent_mac_address) {
1317 _cleanup_free_ char *description = NULL;
1318 char ea[ETHER_ADDR_TO_STRING_MAX];
1319
1320 (void) ieee_oui(hwdb, &info->permanent_mac_address, &description);
1321
1322 r = table_add_many(table,
1323 TABLE_EMPTY,
1324 TABLE_STRING, "HW Permanent Address:");
1325 if (r < 0)
bd17fa8c 1326 return table_log_add_error(r);
caa8538a
YW
1327 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
1328 ether_addr_to_string(&info->permanent_mac_address, ea),
1329 description ? " (" : "",
1330 strempty(description),
1331 description ? ")" : "");
1332 if (r < 0)
bd17fa8c 1333 return table_log_add_error(r);
caa8538a
YW
1334 }
1335
c06ff86e
YW
1336 if (info->mtu > 0) {
1337 char min_str[DECIMAL_STR_MAX(uint32_t)], max_str[DECIMAL_STR_MAX(uint32_t)];
1338
1339 xsprintf(min_str, "%" PRIu32, info->min_mtu);
1340 xsprintf(max_str, "%" PRIu32, info->max_mtu);
1341
8d0e0af2
YW
1342 r = table_add_many(table,
1343 TABLE_EMPTY,
1344 TABLE_STRING, "MTU:");
98d5bef3 1345 if (r < 0)
bd17fa8c 1346 return table_log_add_error(r);
c06ff86e
YW
1347 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "%s%s%s%s%s%s%s",
1348 info->mtu,
1349 info->min_mtu > 0 || info->max_mtu > 0 ? " (" : "",
90e29fe1 1350 info->min_mtu > 0 ? "min: " : "",
c06ff86e
YW
1351 info->min_mtu > 0 ? min_str : "",
1352 info->min_mtu > 0 && info->max_mtu > 0 ? ", " : "",
90e29fe1 1353 info->max_mtu > 0 ? "max: " : "",
c06ff86e
YW
1354 info->max_mtu > 0 ? max_str : "",
1355 info->min_mtu > 0 || info->max_mtu > 0 ? ")" : "");
98d5bef3 1356 if (r < 0)
bd17fa8c 1357 return table_log_add_error(r);
98d5bef3 1358 }
8eb9058d 1359
c82d1bf2
SS
1360 if (streq_ptr(info->netdev_kind, "bridge")) {
1361 r = table_add_many(table,
1362 TABLE_EMPTY,
1363 TABLE_STRING, "Forward Delay:",
1364 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->forward_delay),
1365 TABLE_EMPTY,
1366 TABLE_STRING, "Hello Time:",
1367 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->hello_time),
1368 TABLE_EMPTY,
1369 TABLE_STRING, "Max Age:",
1370 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->max_age),
1371 TABLE_EMPTY,
1372 TABLE_STRING, "Ageing Time:",
1373 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->ageing_time),
1374 TABLE_EMPTY,
1375 TABLE_STRING, "Priority:",
1376 TABLE_UINT16, info->priority,
1377 TABLE_EMPTY,
1378 TABLE_STRING, "STP:",
1379 TABLE_BOOLEAN, info->stp_state > 0,
1380 TABLE_EMPTY,
1381 TABLE_STRING, "Multicast IGMP Version:",
1382 TABLE_UINT8, info->mcast_igmp_version);
1383 if (r < 0)
bd17fa8c 1384 return table_log_add_error(r);
b24281aa
SS
1385
1386 } else if (streq_ptr(info->netdev_kind, "vxlan")) {
1387 if (info->vxlan_info.vni > 0) {
1388 r = table_add_many(table,
1389 TABLE_EMPTY,
1390 TABLE_STRING, "VNI:",
1391 TABLE_UINT32, info->vxlan_info.vni);
1392 if (r < 0)
bd17fa8c 1393 return table_log_add_error(r);
b24281aa
SS
1394 }
1395
1396 if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
1397 r = table_add_many(table,
1398 TABLE_EMPTY,
1399 TABLE_STRING, "Group:",
1400 info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1401 &info->vxlan_info.group);
1402 if (r < 0)
bd17fa8c 1403 return table_log_add_error(r);
b24281aa
SS
1404 }
1405
1406 if (IN_SET(info->vxlan_info.local_family, AF_INET, AF_INET6)) {
1407 r = table_add_many(table,
1408 TABLE_EMPTY,
1409 TABLE_STRING, "Local:",
1410 info->vxlan_info.local_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1411 &info->vxlan_info.local);
1412 if (r < 0)
bd17fa8c 1413 return table_log_add_error(r);
b24281aa
SS
1414 }
1415
1416 if (info->vxlan_info.dest_port > 0) {
1417 r = table_add_many(table,
1418 TABLE_EMPTY,
1419 TABLE_STRING, "Destination Port:",
1420 TABLE_UINT16, be16toh(info->vxlan_info.dest_port));
1421 if (r < 0)
bd17fa8c 1422 return table_log_add_error(r);
b24281aa
SS
1423 }
1424
1425 if (info->vxlan_info.link > 0) {
1426 r = table_add_many(table,
1427 TABLE_EMPTY,
1428 TABLE_STRING, "Underlying Device:",
1429 TABLE_IFINDEX, info->vxlan_info.link);
1430 if (r < 0)
bd17fa8c 1431 return table_log_add_error(r);
b24281aa 1432 }
c82d1bf2
SS
1433 }
1434
8d07de25
ZJS
1435 if (info->has_wlan_link_info) {
1436 _cleanup_free_ char *esc = NULL;
1437 char buf[ETHER_ADDR_TO_STRING_MAX];
1438
1439 r = table_add_many(table,
1440 TABLE_EMPTY,
1441 TABLE_STRING, "WiFi access point:");
1442 if (r < 0)
bd17fa8c 1443 return table_log_add_error(r);
8d07de25
ZJS
1444
1445 if (info->ssid)
1446 esc = cescape(info->ssid);
1447
1448 r = table_add_cell_stringf(table, NULL, "%s (%s)",
1449 strnull(esc),
1450 ether_addr_to_string(&info->bssid, buf));
1451 if (r < 0)
bd17fa8c 1452 return table_log_add_error(r);
8d07de25
ZJS
1453 }
1454
335dd8ba 1455 if (info->has_bitrates) {
42a63431 1456 char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX];
335dd8ba 1457
8d0e0af2
YW
1458 r = table_add_many(table,
1459 TABLE_EMPTY,
1460 TABLE_STRING, "Bit Rate (Tx/Rx):");
335dd8ba 1461 if (r < 0)
bd17fa8c 1462 return table_log_add_error(r);
42a63431
YW
1463 r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
1464 format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0),
1465 format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0));
335dd8ba 1466 if (r < 0)
bd17fa8c 1467 return table_log_add_error(r);
335dd8ba
YW
1468 }
1469
98d5bef3 1470 if (info->has_tx_queues || info->has_rx_queues) {
8d0e0af2
YW
1471 r = table_add_many(table,
1472 TABLE_EMPTY,
1473 TABLE_STRING, "Queue Length (Tx/Rx):");
98d5bef3 1474 if (r < 0)
bd17fa8c 1475 return table_log_add_error(r);
98d5bef3
YW
1476 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "/%" PRIu32, info->tx_queues, info->rx_queues);
1477 if (r < 0)
bd17fa8c 1478 return table_log_add_error(r);
98d5bef3 1479 }
0d4ad91d 1480
c967d2c7
YW
1481 if (info->has_ethtool_link_info) {
1482 const char *duplex = duplex_to_string(info->duplex);
1483 const char *port = port_to_string(info->port);
1484
1485 if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
8d0e0af2
YW
1486 r = table_add_many(table,
1487 TABLE_EMPTY,
1488 TABLE_STRING, "Auto negotiation:",
1489 TABLE_BOOLEAN, info->autonegotiation == AUTONEG_ENABLE);
c967d2c7 1490 if (r < 0)
bd17fa8c 1491 return table_log_add_error(r);
c967d2c7
YW
1492 }
1493
1494 if (info->speed > 0) {
8d0e0af2
YW
1495 r = table_add_many(table,
1496 TABLE_EMPTY,
1497 TABLE_STRING, "Speed:",
50299121 1498 TABLE_BPS, info->speed);
c967d2c7 1499 if (r < 0)
bd17fa8c 1500 return table_log_add_error(r);
c967d2c7
YW
1501 }
1502
1503 if (duplex) {
8d0e0af2
YW
1504 r = table_add_many(table,
1505 TABLE_EMPTY,
1506 TABLE_STRING, "Duplex:",
1507 TABLE_STRING, duplex);
c967d2c7 1508 if (r < 0)
bd17fa8c 1509 return table_log_add_error(r);
c967d2c7
YW
1510 }
1511
1512 if (port) {
8d0e0af2
YW
1513 r = table_add_many(table,
1514 TABLE_EMPTY,
1515 TABLE_STRING, "Port:",
1516 TABLE_STRING, port);
c967d2c7 1517 if (r < 0)
bd17fa8c 1518 return table_log_add_error(r);
c967d2c7
YW
1519 }
1520 }
1521
98d5bef3
YW
1522 r = dump_addresses(rtnl, table, info->ifindex);
1523 if (r < 0)
1524 return r;
1525 r = dump_gateways(rtnl, hwdb, table, info->ifindex);
1526 if (r < 0)
1527 return r;
1528 r = dump_list(table, "DNS:", dns);
1529 if (r < 0)
1530 return r;
1531 r = dump_list(table, "Search Domains:", search_domains);
1532 if (r < 0)
1533 return r;
1534 r = dump_list(table, "Route Domains:", route_domains);
1535 if (r < 0)
1536 return r;
1537 r = dump_list(table, "NTP:", ntp);
1538 if (r < 0)
1539 return r;
1540 r = dump_ifindexes(table, "Carrier Bound To:", carrier_bound_to);
1541 if (r < 0)
1542 return r;
1543 r = dump_ifindexes(table, "Carrier Bound By:", carrier_bound_by);
1544 if (r < 0)
1545 return r;
9085f64a 1546
b147503e 1547 (void) sd_network_link_get_timezone(info->ifindex, &tz);
98d5bef3 1548 if (tz) {
8d0e0af2
YW
1549 r = table_add_many(table,
1550 TABLE_EMPTY,
1551 TABLE_STRING, "Time Zone:",
1552 TABLE_STRING, tz);
98d5bef3 1553 if (r < 0)
bd17fa8c 1554 return table_log_add_error(r);
98d5bef3 1555 }
8eb9058d 1556
98d5bef3
YW
1557 r = dump_lldp_neighbors(table, "Connected To:", info->ifindex);
1558 if (r < 0)
1559 return r;
837f57da 1560
a459b24f
YW
1561 r = dump_statistics(table, info);
1562 if (r < 0)
1563 return r;
1564
10c71c36
YW
1565 r = table_print(table, NULL);
1566 if (r < 0)
bd17fa8c 1567 return log_error_errno(r, "Failed to print table: %m");
10c71c36
YW
1568
1569 return show_logs(info);
9085f64a
LP
1570}
1571
0070333f
LP
1572static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
1573 _cleanup_free_ char *operational_state = NULL;
1574 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
1575 const char *on_color_operational, *off_color_operational;
98d5bef3
YW
1576 _cleanup_(table_unrefp) Table *table = NULL;
1577 TableCell *cell;
1578 int r;
0070333f
LP
1579
1580 assert(rtnl);
1581
4abd866d 1582 (void) sd_network_get_operational_state(&operational_state);
ceb366df 1583 operational_state_to_color(NULL, operational_state, &on_color_operational, &off_color_operational);
0070333f 1584
4252171a 1585 table = table_new("dot", "key", "value");
98d5bef3 1586 if (!table)
bd17fa8c 1587 return log_oom();
98d5bef3 1588
a42d9490
YW
1589 if (arg_full)
1590 table_set_width(table, 0);
1591
81914d9f
YW
1592 assert_se(cell = table_get_cell(table, 0, 0));
1593 (void) table_set_ellipsize_percent(table, cell, 100);
1594
1595 assert_se(cell = table_get_cell(table, 0, 1));
1596 (void) table_set_align_percent(table, cell, 100);
1597 (void) table_set_ellipsize_percent(table, cell, 100);
1598
98d5bef3
YW
1599 table_set_header(table, false);
1600
8d0e0af2
YW
1601 r = table_add_many(table,
1602 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1603 TABLE_SET_COLOR, on_color_operational,
1604 TABLE_STRING, "State:",
1605 TABLE_STRING, strna(operational_state),
1606 TABLE_SET_COLOR, on_color_operational);
bd17fa8c
YW
1607 if (r < 0)
1608 return table_log_add_error(r);
0070333f 1609
98d5bef3
YW
1610 r = dump_addresses(rtnl, table, 0);
1611 if (r < 0)
1612 return r;
1613 r = dump_gateways(rtnl, hwdb, table, 0);
1614 if (r < 0)
1615 return r;
0070333f 1616
4abd866d 1617 (void) sd_network_get_dns(&dns);
98d5bef3
YW
1618 r = dump_list(table, "DNS:", dns);
1619 if (r < 0)
1620 return r;
0070333f 1621
4abd866d 1622 (void) sd_network_get_search_domains(&search_domains);
98d5bef3
YW
1623 r = dump_list(table, "Search Domains:", search_domains);
1624 if (r < 0)
1625 return r;
0070333f 1626
4abd866d 1627 (void) sd_network_get_route_domains(&route_domains);
98d5bef3
YW
1628 r = dump_list(table, "Route Domains:", route_domains);
1629 if (r < 0)
1630 return r;
0070333f 1631
4abd866d 1632 (void) sd_network_get_ntp(&ntp);
98d5bef3
YW
1633 r = dump_list(table, "NTP:", ntp);
1634 if (r < 0)
1635 return r;
0070333f 1636
10c71c36
YW
1637 r = table_print(table, NULL);
1638 if (r < 0)
bd17fa8c 1639 return log_error_errno(r, "Failed to print table: %m");
10c71c36
YW
1640
1641 return show_logs(NULL);
0070333f
LP
1642}
1643
266b5389 1644static int link_status(int argc, char *argv[], void *userdata) {
335dd8ba 1645 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 1646 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b147503e 1647 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
172353b1 1648 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
b147503e 1649 int r, c, i;
ee8c4568 1650
0221d68a 1651 (void) pager_open(arg_pager_flags);
0070333f 1652
335dd8ba
YW
1653 r = sd_bus_open_system(&bus);
1654 if (r < 0)
1655 return log_error_errno(r, "Failed to connect system bus: %m");
1656
1c4baffc 1657 r = sd_netlink_open(&rtnl);
f647962d
MS
1658 if (r < 0)
1659 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 1660
81fd1dd3
TG
1661 r = sd_hwdb_new(&hwdb);
1662 if (r < 0)
1663 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 1664
b147503e 1665 if (arg_all)
335dd8ba 1666 c = acquire_link_info(bus, rtnl, NULL, &links);
b147503e 1667 else if (argc <= 1)
0070333f 1668 return system_status(rtnl, hwdb);
b147503e 1669 else
335dd8ba 1670 c = acquire_link_info(bus, rtnl, argv + 1, &links);
b147503e
LP
1671 if (c < 0)
1672 return c;
ee8c4568 1673
b147503e
LP
1674 for (i = 0; i < c; i++) {
1675 if (i > 0)
1676 fputc('\n', stdout);
ee8c4568 1677
b147503e 1678 link_status_one(rtnl, hwdb, links + i);
ee8c4568
LP
1679 }
1680
1681 return 0;
1682}
1683
34437b4f
LP
1684static char *lldp_capabilities_to_string(uint16_t x) {
1685 static const char characters[] = {
1686 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1687 };
1688 char *ret;
1689 unsigned i;
49699bac 1690
34437b4f
LP
1691 ret = new(char, ELEMENTSOF(characters) + 1);
1692 if (!ret)
49699bac
SS
1693 return NULL;
1694
34437b4f
LP
1695 for (i = 0; i < ELEMENTSOF(characters); i++)
1696 ret[i] = (x & (1U << i)) ? characters[i] : '.';
49699bac 1697
34437b4f
LP
1698 ret[i] = 0;
1699 return ret;
49699bac
SS
1700}
1701
4c3160f1
ZJS
1702static void lldp_capabilities_legend(uint16_t x) {
1703 unsigned w, i, cols = columns();
404d53a9 1704 static const char* const table[] = {
4c3160f1
ZJS
1705 "o - Other",
1706 "p - Repeater",
1707 "b - Bridge",
1708 "w - WLAN Access Point",
1709 "r - Router",
1710 "t - Telephone",
1711 "d - DOCSIS cable device",
1712 "a - Station",
1713 "c - Customer VLAN",
1714 "s - Service VLAN",
1715 "m - Two-port MAC Relay (TPMR)",
1716 };
1717
1718 if (x == 0)
1719 return;
1720
1721 printf("\nCapability Flags:\n");
1722 for (w = 0, i = 0; i < ELEMENTSOF(table); i++)
1723 if (x & (1U << i) || arg_all) {
1724 bool newline;
1725
1726 newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols;
1727 if (newline)
1728 w = 0;
1729 w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]);
1730 }
1731 puts("");
1732}
1733
49699bac 1734static int link_lldp_status(int argc, char *argv[], void *userdata) {
b147503e 1735 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
172353b1 1736 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
658e9106 1737 _cleanup_(table_unrefp) Table *table = NULL;
b147503e 1738 int i, r, c, m = 0;
4c3160f1 1739 uint16_t all = 0;
658e9106 1740 TableCell *cell;
b147503e
LP
1741
1742 r = sd_netlink_open(&rtnl);
1743 if (r < 0)
1744 return log_error_errno(r, "Failed to connect to netlink: %m");
49699bac 1745
335dd8ba 1746 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
49699bac 1747 if (c < 0)
7d367b45
LP
1748 return c;
1749
0221d68a 1750 (void) pager_open(arg_pager_flags);
49699bac 1751
4252171a
ZJS
1752 table = table_new("link",
1753 "chassis id",
1754 "system name",
1755 "caps",
1756 "port id",
1757 "port description");
658e9106 1758 if (!table)
bd17fa8c 1759 return log_oom();
658e9106 1760
a42d9490
YW
1761 if (arg_full)
1762 table_set_width(table, 0);
1763
658e9106
YW
1764 table_set_header(table, arg_legend);
1765
1766 assert_se(cell = table_get_cell(table, 0, 0));
1767 table_set_minimum_width(table, cell, 16);
1768
1769 assert_se(cell = table_get_cell(table, 0, 1));
1770 table_set_minimum_width(table, cell, 17);
1771
1772 assert_se(cell = table_get_cell(table, 0, 2));
1773 table_set_minimum_width(table, cell, 16);
1774
1775 assert_se(cell = table_get_cell(table, 0, 3));
1776 table_set_minimum_width(table, cell, 11);
1777
1778 assert_se(cell = table_get_cell(table, 0, 4));
1779 table_set_minimum_width(table, cell, 17);
1780
1781 assert_se(cell = table_get_cell(table, 0, 5));
1782 table_set_minimum_width(table, cell, 16);
49699bac 1783
b147503e 1784 for (i = 0; i < c; i++) {
34437b4f 1785 _cleanup_fclose_ FILE *f = NULL;
49699bac 1786
837f57da
LP
1787 r = open_lldp_neighbors(links[i].ifindex, &f);
1788 if (r == -ENOENT)
1789 continue;
1790 if (r < 0) {
1791 log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
34437b4f
LP
1792 continue;
1793 }
49699bac 1794
34437b4f 1795 for (;;) {
e2835111
YW
1796 _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL, *capabilities = NULL;
1797 const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
34437b4f 1798 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
34437b4f 1799 uint16_t cc;
49699bac 1800
837f57da 1801 r = next_lldp_neighbor(f, &n);
34437b4f 1802 if (r < 0) {
837f57da 1803 log_warning_errno(r, "Failed to read neighbor data: %m");
34437b4f 1804 break;
49699bac 1805 }
837f57da
LP
1806 if (r == 0)
1807 break;
34437b4f
LP
1808
1809 (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
1810 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1811 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1812 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1813
d08191a2
LP
1814 if (chassis_id) {
1815 cid = ellipsize(chassis_id, 17, 100);
1816 if (cid)
1817 chassis_id = cid;
1818 }
1819
1820 if (port_id) {
1821 pid = ellipsize(port_id, 17, 100);
1822 if (pid)
1823 port_id = pid;
1824 }
1825
1826 if (system_name) {
1827 sname = ellipsize(system_name, 16, 100);
1828 if (sname)
1829 system_name = sname;
1830 }
1831
1832 if (port_description) {
1833 pdesc = ellipsize(port_description, 16, 100);
1834 if (pdesc)
1835 port_description = pdesc;
1836 }
1837
4c3160f1 1838 if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
34437b4f 1839 capabilities = lldp_capabilities_to_string(cc);
4c3160f1
ZJS
1840 all |= cc;
1841 }
34437b4f 1842
658e9106
YW
1843 r = table_add_many(table,
1844 TABLE_STRING, links[i].name,
1845 TABLE_STRING, strna(chassis_id),
1846 TABLE_STRING, strna(system_name),
1847 TABLE_STRING, strna(capabilities),
1848 TABLE_STRING, strna(port_id),
1849 TABLE_STRING, strna(port_description));
1850 if (r < 0)
bd17fa8c 1851 return table_log_add_error(r);
b147503e
LP
1852
1853 m++;
49699bac
SS
1854 }
1855 }
1856
658e9106
YW
1857 r = table_print(table, NULL);
1858 if (r < 0)
bd17fa8c 1859 return log_error_errno(r, "Failed to print table: %m");
658e9106 1860
4c3160f1
ZJS
1861 if (arg_legend) {
1862 lldp_capabilities_legend(all);
1863 printf("\n%i neighbors listed.\n", m);
1864 }
49699bac
SS
1865
1866 return 0;
1867}
1868
9cd8c766
SS
1869static int link_delete_send_message(sd_netlink *rtnl, int index) {
1870 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
1871 int r;
1872
1873 assert(rtnl);
1874
1875 r = sd_rtnl_message_new_link(rtnl, &req, RTM_DELLINK, index);
1876 if (r < 0)
1877 return rtnl_log_create_error(r);
1878
1879 r = sd_netlink_call(rtnl, req, 0, NULL);
1880 if (r < 0)
1881 return r;
1882
1883 return 0;
1884}
1885
1886static int link_delete(int argc, char *argv[], void *userdata) {
1887 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1888 _cleanup_set_free_ Set *indexes = NULL;
9cd8c766
SS
1889 int index, r, i;
1890 Iterator j;
38b9af61 1891 void *p;
9cd8c766
SS
1892
1893 r = sd_netlink_open(&rtnl);
1894 if (r < 0)
1895 return log_error_errno(r, "Failed to connect to netlink: %m");
1896
1897 indexes = set_new(NULL);
1898 if (!indexes)
1899 return log_oom();
1900
1901 for (i = 1; i < argc; i++) {
d308bb99 1902 index = resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
1903 if (index < 0)
1904 return index;
9cd8c766
SS
1905
1906 r = set_put(indexes, INT_TO_PTR(index));
1907 if (r < 0)
1908 return log_oom();
1909 }
1910
38b9af61 1911 SET_FOREACH(p, indexes, j) {
d56d6cb8
YW
1912 index = PTR_TO_INT(p);
1913 r = link_delete_send_message(rtnl, index);
9cd8c766 1914 if (r < 0) {
518a66ec
YW
1915 char ifname[IF_NAMESIZE + 1];
1916
e4857ee2
YW
1917 return log_error_errno(r, "Failed to delete interface %s: %m",
1918 format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
9cd8c766
SS
1919 }
1920 }
1921
1922 return r;
1923}
1924
308e7dfd
YW
1925static int link_renew_one(sd_bus *bus, int index, const char *name) {
1926 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1927 int r;
1928
1929 r = sd_bus_call_method(
1930 bus,
1931 "org.freedesktop.network1",
1932 "/org/freedesktop/network1",
1933 "org.freedesktop.network1.Manager",
1934 "RenewLink",
1935 &error,
1936 NULL,
1937 "i", index);
1938 if (r < 0)
1939 return log_error_errno(r, "Failed to renew dynamic configuration of interface %s: %s",
1940 name, bus_error_message(&error, r));
1941
1942 return 0;
1943}
1944
1945static int link_renew(int argc, char *argv[], void *userdata) {
1946 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f7581ed6 1947 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
308e7dfd
YW
1948 int index, i, k = 0, r;
1949
1950 r = sd_bus_open_system(&bus);
1951 if (r < 0)
1952 return log_error_errno(r, "Failed to connect system bus: %m");
1953
1954 for (i = 1; i < argc; i++) {
d308bb99 1955 index = resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
1956 if (index < 0)
1957 return index;
308e7dfd
YW
1958
1959 r = link_renew_one(bus, index, argv[i]);
1960 if (r < 0 && k >= 0)
1961 k = r;
1962 }
1963
1964 return k;
1965}
1966
a227674c
YW
1967static int verb_reload(int argc, char *argv[], void *userdata) {
1968 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1969 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1970 int r;
1971
1972 r = sd_bus_open_system(&bus);
1973 if (r < 0)
1974 return log_error_errno(r, "Failed to connect system bus: %m");
1975
1976 r = sd_bus_call_method(
1977 bus,
1978 "org.freedesktop.network1",
1979 "/org/freedesktop/network1",
1980 "org.freedesktop.network1.Manager",
1981 "Reload",
1982 &error, NULL, NULL);
1983 if (r < 0)
1984 return log_error_errno(r, "Failed to reload network settings: %m");
1985
1986 return 0;
1987}
1988
8dc85c5e
YW
1989static int verb_reconfigure(int argc, char *argv[], void *userdata) {
1990 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1991 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f7581ed6 1992 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
8dc85c5e
YW
1993 _cleanup_set_free_ Set *indexes = NULL;
1994 int index, i, r;
1995 Iterator j;
1996 void *p;
1997
1998 r = sd_bus_open_system(&bus);
1999 if (r < 0)
2000 return log_error_errno(r, "Failed to connect system bus: %m");
2001
2002 indexes = set_new(NULL);
2003 if (!indexes)
2004 return log_oom();
2005
2006 for (i = 1; i < argc; i++) {
d308bb99 2007 index = resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2008 if (index < 0)
2009 return index;
8dc85c5e
YW
2010
2011 r = set_put(indexes, INT_TO_PTR(index));
2012 if (r < 0)
2013 return log_oom();
2014 }
2015
2016 SET_FOREACH(p, indexes, j) {
2017 index = PTR_TO_INT(p);
2018 r = sd_bus_call_method(
2019 bus,
2020 "org.freedesktop.network1",
2021 "/org/freedesktop/network1",
2022 "org.freedesktop.network1.Manager",
2023 "ReconfigureLink",
2024 &error, NULL, "i", index);
2025 if (r < 0) {
2026 char ifname[IF_NAMESIZE + 1];
2027
117caf37
ZJS
2028 return log_error_errno(r, "Failed to reconfigure network interface %s: %m",
2029 format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
8dc85c5e
YW
2030 }
2031 }
2032
2033 return 0;
2034}
2035
37ec0fdd
LP
2036static int help(void) {
2037 _cleanup_free_ char *link = NULL;
2038 int r;
2039
2040 r = terminal_urlify_man("networkctl", "1", &link);
2041 if (r < 0)
2042 return log_oom();
2043
353b2baa
LP
2044 printf("%s [OPTIONS...] COMMAND\n\n"
2045 "%sQuery and control the networking subsystem.%s\n"
a459b24f 2046 "\nCommands:\n"
8dc85c5e
YW
2047 " list [PATTERN...] List links\n"
2048 " status [PATTERN...] Show link status\n"
2049 " lldp [PATTERN...] Show LLDP neighbors\n"
2050 " label Show current address label entries in the kernel\n"
2051 " delete DEVICES... Delete virtual netdevs\n"
2052 " renew DEVICES... Renew dynamic configurations\n"
2053 " reconfigure DEVICES... Reconfigure interfaces\n"
2054 " reload Reload .network and .netdev files\n"
353b2baa 2055 "\nOptions:\n"
8dc85c5e
YW
2056 " -h --help Show this help\n"
2057 " --version Show package version\n"
2058 " --no-pager Do not pipe output into a pager\n"
2059 " --no-legend Do not show the headers and footers\n"
2060 " -a --all Show status for all links\n"
2061 " -s --stats Show detailed link statics\n"
10c71c36
YW
2062 " -l --full Do not ellipsize output\n"
2063 " -n --lines=INTEGER Number of journal entries to show\n"
37ec0fdd
LP
2064 "\nSee the %s for details.\n"
2065 , program_invocation_short_name
353b2baa 2066 , ansi_highlight()
ce2529b4 2067 , ansi_normal()
37ec0fdd
LP
2068 , link
2069 );
2070
2071 return 0;
ee8c4568
LP
2072}
2073
2074static int parse_argv(int argc, char *argv[]) {
2075
2076 enum {
2077 ARG_VERSION = 0x100,
2078 ARG_NO_PAGER,
2079 ARG_NO_LEGEND,
2080 };
2081
2082 static const struct option options[] = {
2083 { "help", no_argument, NULL, 'h' },
2084 { "version", no_argument, NULL, ARG_VERSION },
2085 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2086 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 2087 { "all", no_argument, NULL, 'a' },
a459b24f 2088 { "stats", no_argument, NULL, 's' },
10c71c36
YW
2089 { "full", no_argument, NULL, 'l' },
2090 { "lines", required_argument, NULL, 'n' },
ee8c4568
LP
2091 {}
2092 };
2093
2094 int c;
2095
2096 assert(argc >= 0);
2097 assert(argv);
2098
10c71c36 2099 while ((c = getopt_long(argc, argv, "hasln:", options, NULL)) >= 0) {
ee8c4568
LP
2100
2101 switch (c) {
2102
2103 case 'h':
37ec0fdd 2104 return help();
ee8c4568
LP
2105
2106 case ARG_VERSION:
3f6fd1ba 2107 return version();
ee8c4568
LP
2108
2109 case ARG_NO_PAGER:
0221d68a 2110 arg_pager_flags |= PAGER_DISABLE;
ee8c4568
LP
2111 break;
2112
2113 case ARG_NO_LEGEND:
2114 arg_legend = false;
2115 break;
2116
9085f64a
LP
2117 case 'a':
2118 arg_all = true;
2119 break;
2120
a459b24f
YW
2121 case 's':
2122 arg_stats = true;
2123 break;
2124
10c71c36
YW
2125 case 'l':
2126 arg_full = true;
2127 break;
2128
2129 case 'n':
2130 if (safe_atou(optarg, &arg_lines) < 0)
2131 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2132 "Failed to parse lines '%s'", optarg);
2133 break;
2134
ee8c4568
LP
2135 case '?':
2136 return -EINVAL;
2137
2138 default:
2139 assert_not_reached("Unhandled option");
2140 }
2141 }
2142
2143 return 1;
2144}
2145
2146static int networkctl_main(int argc, char *argv[]) {
15c3626e 2147 static const Verb verbs[] = {
8dc85c5e
YW
2148 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
2149 { "status", VERB_ANY, VERB_ANY, 0, link_status },
2150 { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
2151 { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
2152 { "delete", 2, VERB_ANY, 0, link_delete },
2153 { "renew", 2, VERB_ANY, 0, link_renew },
2154 { "reconfigure", 2, VERB_ANY, 0, verb_reconfigure },
2155 { "reload", 1, 1, 0, verb_reload },
266b5389 2156 {}
ee8c4568
LP
2157 };
2158
266b5389 2159 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
2160}
2161
58fb3678
LP
2162static void warn_networkd_missing(void) {
2163
2164 if (access("/run/systemd/netif/state", F_OK) >= 0)
2165 return;
2166
2167 fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
2168}
2169
4e2ca442 2170static int run(int argc, char* argv[]) {
ee8c4568
LP
2171 int r;
2172
1a043959 2173 log_show_color(true);
ee8c4568
LP
2174 log_parse_environment();
2175 log_open();
2176
2177 r = parse_argv(argc, argv);
2178 if (r <= 0)
4e2ca442 2179 return r;
ee8c4568 2180
58fb3678
LP
2181 warn_networkd_missing();
2182
4e2ca442 2183 return networkctl_main(argc, argv);
ee8c4568 2184}
4e2ca442
ZJS
2185
2186DEFINE_MAIN_FUNCTION(run);