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