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