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