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