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