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