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