]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
man: Add more details about desktop file processing
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
ee8c4568 2
3d0c8750 3#include <arpa/inet.h>
ee8c4568 4#include <getopt.h>
d37b7627 5#include <linux/if_addrlabel.h>
1693a943 6#include <net/if.h>
3f6fd1ba 7#include <stdbool.h>
ca78ad1d
ZJS
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <unistd.h>
12ef8fb6 11#include <linux/if_bridge.h>
dca35224 12#include <linux/if_tunnel.h>
ee8c4568 13
335dd8ba 14#include "sd-bus.h"
914d6c09 15#include "sd-device.h"
d9ce1c24 16#include "sd-dhcp-client.h"
3f6fd1ba 17#include "sd-hwdb.h"
3a2ee855 18#include "sd-lldp-rx.h"
3f6fd1ba
LP
19#include "sd-netlink.h"
20#include "sd-network.h"
ee8c4568 21
b5efdb8a 22#include "alloc-util.h"
b55818fd 23#include "bond-util.h"
9b71e4ab 24#include "bridge-util.h"
335dd8ba
YW
25#include "bus-common-errors.h"
26#include "bus-error.h"
9b71e4ab 27#include "bus-locator.h"
914d6c09 28#include "device-util.h"
8d07de25 29#include "escape.h"
3f6fd1ba 30#include "ether-addr-util.h"
c967d2c7 31#include "ethtool-util.h"
34437b4f 32#include "fd-util.h"
ff7c88a2 33#include "format-table.h"
518a66ec 34#include "format-util.h"
e7b38d7d 35#include "geneve-util.h"
0ef84b80 36#include "glob-util.h"
81fd1dd3 37#include "hwdb-util.h"
851ef1ed 38#include "ipvlan-util.h"
ee8c4568 39#include "local-addresses.h"
8752c575 40#include "locale-util.h"
10c71c36 41#include "logs-show.h"
d37b7627 42#include "macro.h"
cf217a09 43#include "macvlan-util.h"
4e2ca442 44#include "main-func.h"
b5cc5591 45#include "netif-util.h"
3f6fd1ba 46#include "netlink-util.h"
ef62949a 47#include "network-internal.h"
1929ed0e 48#include "network-util.h"
3f6fd1ba 49#include "pager.h"
bfc2b05e 50#include "parse-argument.h"
6bedfcbb 51#include "parse-util.h"
294bf0c3 52#include "pretty-print.h"
9cd8c766 53#include "set.h"
d308bb99 54#include "socket-netlink.h"
db73295a 55#include "socket-util.h"
760877e9 56#include "sort-util.h"
34437b4f 57#include "sparse-endian.h"
d054f0a4 58#include "stdio-util.h"
8b43440b 59#include "string-table.h"
8752c575 60#include "string-util.h"
3f6fd1ba 61#include "strv.h"
2388b2f4 62#include "strxcpyx.h"
288a74cc 63#include "terminal-util.h"
10c71c36 64#include "unit-def.h"
3f6fd1ba 65#include "verbs.h"
8d07de25 66#include "wifi-util.h"
ee8c4568 67
c82d1bf2
SS
68/* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
69#define NETDEV_KIND_MAX 64
70
8d07de25
ZJS
71/* use 128 kB for receive socket kernel queue, we shouldn't need more here */
72#define RCVBUF_SIZE (128*1024)
73
0221d68a 74static PagerFlags arg_pager_flags = 0;
ee8c4568 75static bool arg_legend = true;
9085f64a 76static bool arg_all = false;
a459b24f 77static bool arg_stats = false;
10c71c36
YW
78static bool arg_full = false;
79static unsigned arg_lines = 10;
bfc2b05e
YW
80static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
81
82static int get_description(JsonVariant **ret) {
83 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
84 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
85 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
86 const char *text = NULL;
87 int r;
88
89 r = sd_bus_open_system(&bus);
90 if (r < 0)
91 return log_error_errno(r, "Failed to connect system bus: %m");
92
93 r = bus_call_method(bus, bus_network_mgr, "Describe", &error, &reply, NULL);
94 if (r < 0)
95 return log_error_errno(r, "Failed to get description: %s", bus_error_message(&error, r));
96
97 r = sd_bus_message_read(reply, "s", &text);
98 if (r < 0)
99 return bus_log_parse_error(r);
100
101 r = json_parse(text, 0, ret, NULL, NULL);
102 if (r < 0)
103 return log_error_errno(r, "Failed to parse JSON: %m");
104
105 return 0;
106}
107
108static int dump_manager_description(void) {
109 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
110 int r;
111
112 r = get_description(&v);
113 if (r < 0)
114 return r;
115
116 json_variant_dump(v, arg_json_format_flags, NULL, NULL);
117 return 0;
118}
119
120static int dump_link_description(char **patterns) {
121 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
122 _cleanup_free_ bool *matched_patterns = NULL;
123 JsonVariant *i;
124 size_t c = 0;
125 int r;
126
127 r = get_description(&v);
128 if (r < 0)
129 return r;
130
131 matched_patterns = new0(bool, strv_length(patterns));
132 if (!matched_patterns)
133 return log_oom();
134
135 JSON_VARIANT_ARRAY_FOREACH(i, json_variant_by_key(v, "Interfaces")) {
718ca772 136 char ifindex_str[DECIMAL_STR_MAX(int64_t)];
bfc2b05e 137 const char *name;
718ca772 138 int64_t index;
bfc2b05e
YW
139 size_t pos;
140
141 name = json_variant_string(json_variant_by_key(i, "Name"));
142 index = json_variant_integer(json_variant_by_key(i, "Index"));
143 xsprintf(ifindex_str, "%ji", index);
144
145 if (!strv_fnmatch_full(patterns, ifindex_str, 0, &pos) &&
146 !strv_fnmatch_full(patterns, name, 0, &pos)) {
147 bool match = false;
148 JsonVariant *a;
149
150 JSON_VARIANT_ARRAY_FOREACH(a, json_variant_by_key(i, "AlternativeNames"))
151 if (strv_fnmatch_full(patterns, json_variant_string(a), 0, &pos)) {
152 match = true;
153 break;
154 }
155
156 if (!match)
157 continue;
158 }
159
160 matched_patterns[pos] = true;
161 json_variant_dump(i, arg_json_format_flags, NULL, NULL);
162 c++;
163 }
164
165 /* Look if we matched all our arguments that are not globs. It is OK for a glob to match
166 * nothing, but not for an exact argument. */
167 for (size_t pos = 0; pos < strv_length(patterns); pos++) {
168 if (matched_patterns[pos])
169 continue;
170
171 if (string_is_glob(patterns[pos]))
172 log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
173 patterns[pos]);
174 else
175 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
176 "Interface \"%s\" not found.", patterns[pos]);
177 }
178
179 if (c == 0)
180 log_warning("No interfaces matched.");
181
182 return 0;
183}
ee8c4568 184
ceb366df 185static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
ceb366df
ZJS
186 if (STRPTR_IN_SET(state, "routable", "enslaved") ||
187 (streq_ptr(name, "lo") && streq_ptr(state, "carrier"))) {
37da8bca
YW
188 if (on)
189 *on = ansi_highlight_green();
190 if (off)
191 *off = ansi_normal();
7e5a080a 192 } else if (streq_ptr(state, "degraded")) {
37da8bca
YW
193 if (on)
194 *on = ansi_highlight_yellow();
195 if (off)
196 *off = ansi_normal();
197 } else {
198 if (on)
199 *on = "";
200 if (off)
201 *off = "";
202 }
7e5a080a
LP
203}
204
205static void setup_state_to_color(const char *state, const char **on, const char **off) {
7e5a080a 206 if (streq_ptr(state, "configured")) {
37da8bca
YW
207 if (on)
208 *on = ansi_highlight_green();
209 if (off)
210 *off = ansi_normal();
7e5a080a 211 } else if (streq_ptr(state, "configuring")) {
37da8bca
YW
212 if (on)
213 *on = ansi_highlight_yellow();
214 if (off)
215 *off = ansi_normal();
1cf03a4f 216 } else if (STRPTR_IN_SET(state, "failed", "linger")) {
37da8bca
YW
217 if (on)
218 *on = ansi_highlight_red();
219 if (off)
220 *off = ansi_normal();
221 } else {
222 if (on)
223 *on = "";
224 if (off)
225 *off = "";
226 }
7e5a080a
LP
227}
228
4a481ec4
229static void online_state_to_color(const char *state, const char **on, const char **off) {
230 if (streq_ptr(state, "online")) {
231 if (on)
232 *on = ansi_highlight_green();
233 if (off)
234 *off = ansi_normal();
235 } else if (streq_ptr(state, "partial")) {
236 if (on)
237 *on = ansi_highlight_yellow();
238 if (off)
239 *off = ansi_normal();
240 } else {
241 if (on)
242 *on = "";
243 if (off)
244 *off = "";
245 }
246}
247
b24281aa
SS
248typedef struct VxLanInfo {
249 uint32_t vni;
250 uint32_t link;
251
252 int local_family;
253 int group_family;
254
255 union in_addr_union local;
256 union in_addr_union group;
257
258 uint16_t dest_port;
259
22ae6c7d
SS
260 uint8_t proxy;
261 uint8_t learning;
22ae6c7d
SS
262 uint8_t rsc;
263 uint8_t l2miss;
264 uint8_t l3miss;
265 uint8_t tos;
266 uint8_t ttl;
b24281aa
SS
267} VxLanInfo;
268
6d0c65ff 269typedef struct LinkInfo {
e997c4b0 270 char name[IFNAMSIZ+1];
c82d1bf2 271 char netdev_kind[NETDEV_KIND_MAX];
172353b1 272 sd_device *sd_device;
6d0c65ff 273 int ifindex;
1c4a6088 274 unsigned short iftype;
ca2b7cd8 275 struct hw_addr_data hw_address;
ed9fa69f 276 struct hw_addr_data permanent_hw_address;
6cfef1b3 277 uint32_t master;
b147503e 278 uint32_t mtu;
2c73f59b
SS
279 uint32_t min_mtu;
280 uint32_t max_mtu;
0307afc6
SS
281 uint32_t tx_queues;
282 uint32_t rx_queues;
d69b62de 283 uint8_t addr_gen_mode;
e810df37 284 char *qdisc;
511070ee 285 char **alternative_names;
b147503e 286
a459b24f
YW
287 union {
288 struct rtnl_link_stats64 stats64;
289 struct rtnl_link_stats stats;
290 };
291
42a63431
YW
292 uint64_t tx_bitrate;
293 uint64_t rx_bitrate;
335dd8ba 294
c82d1bf2
SS
295 /* bridge info */
296 uint32_t forward_delay;
297 uint32_t hello_time;
298 uint32_t max_age;
299 uint32_t ageing_time;
300 uint32_t stp_state;
12ef8fb6 301 uint32_t cost;
c82d1bf2
SS
302 uint16_t priority;
303 uint8_t mcast_igmp_version;
12ef8fb6 304 uint8_t port_state;
c82d1bf2 305
b24281aa
SS
306 /* vxlan info */
307 VxLanInfo vxlan_info;
308
2b2a1ae6
SS
309 /* vlan info */
310 uint16_t vlan_id;
311
dca35224 312 /* tunnel info */
4e1a1991
SS
313 uint8_t ttl;
314 uint8_t tos;
e7b38d7d
SS
315 uint8_t inherit;
316 uint8_t df;
317 uint8_t csum;
318 uint8_t csum6_tx;
319 uint8_t csum6_rx;
4e1a1991
SS
320 uint16_t tunnel_port;
321 uint32_t vni;
e7b38d7d 322 uint32_t label;
dca35224
SS
323 union in_addr_union local;
324 union in_addr_union remote;
325
b1d6fe70
SS
326 /* bonding info */
327 uint8_t mode;
328 uint32_t miimon;
329 uint32_t updelay;
330 uint32_t downdelay;
331
cf217a09
SS
332 /* macvlan and macvtap info */
333 uint32_t macvlan_mode;
334
851ef1ed
SS
335 /* ipvlan info */
336 uint16_t ipvlan_mode;
337 uint16_t ipvlan_flags;
338
c967d2c7
YW
339 /* ethtool info */
340 int autonegotiation;
50299121 341 uint64_t speed;
c967d2c7
YW
342 Duplex duplex;
343 NetDevPort port;
344
8d07de25 345 /* wlan info */
78404d22 346 enum nl80211_iftype wlan_iftype;
8d07de25
ZJS
347 char *ssid;
348 struct ether_addr bssid;
349
57ae8cd8 350 bool has_hw_address:1;
ed9fa69f 351 bool has_permanent_hw_address:1;
0307afc6
SS
352 bool has_tx_queues:1;
353 bool has_rx_queues:1;
a459b24f
YW
354 bool has_stats64:1;
355 bool has_stats:1;
335dd8ba 356 bool has_bitrates:1;
c967d2c7 357 bool has_ethtool_link_info:1;
8d07de25 358 bool has_wlan_link_info:1;
4e1a1991 359 bool has_tunnel_ipv4:1;
d69b62de 360 bool has_ipv6_address_generation_mode:1;
172353b1
ZJS
361
362 bool needs_freeing:1;
6d0c65ff
LP
363} LinkInfo;
364
93bab288
YW
365static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
366 return CMP(a->ifindex, b->ifindex);
6d0c65ff
LP
367}
368
75db809a 369static LinkInfo* link_info_array_free(LinkInfo *array) {
172353b1
ZJS
370 for (unsigned i = 0; array && array[i].needs_freeing; i++) {
371 sd_device_unref(array[i].sd_device);
8d07de25 372 free(array[i].ssid);
e810df37 373 free(array[i].qdisc);
511070ee 374 strv_free(array[i].alternative_names);
172353b1
ZJS
375 }
376
377 return mfree(array);
378}
379DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free);
380
c82d1bf2
SS
381static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
382 const char *received_kind;
383 int r;
384
385 assert(m);
386 assert(info);
387
388 r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
389 if (r < 0)
390 return r;
391
392 r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
393 if (r < 0)
394 return r;
395
396 r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
397 if (r < 0)
398 return r;
399
400 if (streq(received_kind, "bridge")) {
401 (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
402 (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
403 (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
404 (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
405 (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
12ef8fb6 406 (void) sd_netlink_message_read_u32(m, IFLA_BRPORT_COST, &info->cost);
c82d1bf2
SS
407 (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
408 (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
12ef8fb6 409 (void) sd_netlink_message_read_u8(m, IFLA_BRPORT_STATE, &info->port_state);
b1d6fe70
SS
410 } if (streq(received_kind, "bond")) {
411 (void) sd_netlink_message_read_u8(m, IFLA_BOND_MODE, &info->mode);
412 (void) sd_netlink_message_read_u32(m, IFLA_BOND_MIIMON, &info->miimon);
413 (void) sd_netlink_message_read_u32(m, IFLA_BOND_DOWNDELAY, &info->downdelay);
414 (void) sd_netlink_message_read_u32(m, IFLA_BOND_UPDELAY, &info->updelay);
b24281aa
SS
415 } else if (streq(received_kind, "vxlan")) {
416 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
417
418 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
419 if (r >= 0)
420 info->vxlan_info.group_family = AF_INET;
421 else {
422 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
423 if (r >= 0)
424 info->vxlan_info.group_family = AF_INET6;
425 }
426
427 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
428 if (r >= 0)
429 info->vxlan_info.local_family = AF_INET;
430 else {
431 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
432 if (r >= 0)
433 info->vxlan_info.local_family = AF_INET6;
434 }
435
436 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
437 (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
22ae6c7d
SS
438 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_PROXY, &info->vxlan_info.proxy);
439 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_LEARNING, &info->vxlan_info.learning);
440 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_RSC, &info->vxlan_info.rsc);
441 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L3MISS, &info->vxlan_info.l3miss);
442 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L2MISS, &info->vxlan_info.l2miss);
443 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TOS, &info->vxlan_info.tos);
444 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TTL, &info->vxlan_info.ttl);
2b2a1ae6
SS
445 } else if (streq(received_kind, "vlan"))
446 (void) sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &info->vlan_id);
dca35224
SS
447 else if (STR_IN_SET(received_kind, "ipip", "sit")) {
448 (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_LOCAL, &info->local.in);
449 (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_REMOTE, &info->remote.in);
4e1a1991
SS
450 } else if (streq(received_kind, "geneve")) {
451 (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_ID, &info->vni);
452
453 r = sd_netlink_message_read_in_addr(m, IFLA_GENEVE_REMOTE, &info->remote.in);
454 if (r >= 0)
455 info->has_tunnel_ipv4 = true;
456 else
457 (void) sd_netlink_message_read_in6_addr(m, IFLA_GENEVE_REMOTE6, &info->remote.in6);
458
459 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL, &info->ttl);
e7b38d7d 460 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL_INHERIT, &info->inherit);
4e1a1991 461 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TOS, &info->tos);
e7b38d7d
SS
462 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_DF, &info->df);
463 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_CSUM, &info->csum);
464 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, &info->csum6_tx);
465 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, &info->csum6_rx);
4e1a1991 466 (void) sd_netlink_message_read_u16(m, IFLA_GENEVE_PORT, &info->tunnel_port);
e7b38d7d 467 (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_LABEL, &info->label);
4d75ea1e
SS
468 } else if (STR_IN_SET(received_kind, "gre", "gretap", "erspan")) {
469 (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_LOCAL, &info->local.in);
470 (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_REMOTE, &info->remote.in);
ad760bc1
SS
471 } else if (STR_IN_SET(received_kind, "ip6gre", "ip6gretap", "ip6erspan")) {
472 (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_LOCAL, &info->local.in6);
473 (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_REMOTE, &info->remote.in6);
5712d689
SS
474 } else if (streq(received_kind, "vti")) {
475 (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_LOCAL, &info->local.in);
476 (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_REMOTE, &info->remote.in);
477 } else if (streq(received_kind, "vti6")) {
478 (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_LOCAL, &info->local.in6);
479 (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_REMOTE, &info->remote.in6);
cf217a09
SS
480 } else if (STR_IN_SET(received_kind, "macvlan", "macvtap"))
481 (void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode);
851ef1ed
SS
482 else if (streq(received_kind, "ipvlan")) {
483 (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_MODE, &info->ipvlan_mode);
484 (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_FLAGS, &info->ipvlan_flags);
485 }
c82d1bf2
SS
486
487 strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
488
489 (void) sd_netlink_message_exit_container(m);
490 (void) sd_netlink_message_exit_container(m);
491
492 return 0;
493}
494
0ef84b80 495static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) {
f7581ed6 496 _cleanup_strv_free_ char **altnames = NULL;
e810df37 497 const char *name, *qdisc;
a6962904 498 int ifindex, r;
c82d1bf2 499 uint16_t type;
6d0c65ff 500
e997c4b0
LP
501 assert(m);
502 assert(info);
6d0c65ff 503
e997c4b0
LP
504 r = sd_netlink_message_get_type(m, &type);
505 if (r < 0)
506 return r;
6d0c65ff 507
e997c4b0
LP
508 if (type != RTM_NEWLINK)
509 return 0;
6d0c65ff 510
a6962904 511 r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
e997c4b0
LP
512 if (r < 0)
513 return r;
6d0c65ff 514
e997c4b0
LP
515 r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
516 if (r < 0)
517 return r;
6d0c65ff 518
f7581ed6 519 r = sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
48c53989 520 if (r < 0 && r != -ENODATA)
f7581ed6
YW
521 return r;
522
a6962904
YW
523 if (patterns) {
524 char str[DECIMAL_STR_MAX(int)];
0ef84b80
ZJS
525 size_t pos;
526
527 assert(matched_patterns);
a6962904
YW
528
529 xsprintf(str, "%i", ifindex);
0ef84b80
ZJS
530 if (!strv_fnmatch_full(patterns, str, 0, &pos) &&
531 !strv_fnmatch_full(patterns, name, 0, &pos)) {
f7581ed6
YW
532 bool match = false;
533 char **p;
534
535 STRV_FOREACH(p, altnames)
0ef84b80 536 if (strv_fnmatch_full(patterns, *p, 0, &pos)) {
f7581ed6
YW
537 match = true;
538 break;
539 }
540 if (!match)
541 return 0;
542 }
0ef84b80
ZJS
543
544 matched_patterns[pos] = true;
a6962904
YW
545 }
546
b147503e 547 r = sd_rtnl_message_link_get_type(m, &info->iftype);
e997c4b0
LP
548 if (r < 0)
549 return r;
6d0c65ff 550
2388b2f4 551 strscpy(info->name, sizeof info->name, name);
a6962904 552 info->ifindex = ifindex;
f7581ed6 553 info->alternative_names = TAKE_PTR(altnames);
b147503e 554
57ae8cd8 555 info->has_hw_address =
f1f17144 556 netlink_message_read_hw_addr(m, IFLA_ADDRESS, &info->hw_address) >= 0 &&
de0f1579 557 info->hw_address.length > 0;
b147503e 558
ed9fa69f 559 info->has_permanent_hw_address =
49c351a8
YW
560 (netlink_message_read_hw_addr(m, IFLA_PERM_ADDRESS, &info->permanent_hw_address) >= 0 ||
561 ethtool_get_permanent_hw_addr(NULL, info->name, &info->permanent_hw_address) >= 0) &&
ed9fa69f
YW
562 !hw_addr_is_null(&info->permanent_hw_address) &&
563 !hw_addr_equal(&info->permanent_hw_address, &info->hw_address);
caa8538a 564
c06ff86e
YW
565 (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu);
566 (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
567 (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
2c73f59b 568
0307afc6
SS
569 info->has_rx_queues =
570 sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
571 info->rx_queues > 0;
572
573 info->has_tx_queues =
574 sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
575 info->tx_queues > 0;
576
a459b24f
YW
577 if (sd_netlink_message_read(m, IFLA_STATS64, sizeof info->stats64, &info->stats64) >= 0)
578 info->has_stats64 = true;
579 else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
580 info->has_stats = true;
581
e810df37
SS
582 r = sd_netlink_message_read_string(m, IFLA_QDISC, &qdisc);
583 if (r >= 0) {
584 info->qdisc = strdup(qdisc);
585 if (!info->qdisc)
586 return log_oom();
587 }
588
6cfef1b3
SS
589 (void) sd_netlink_message_read_u32(m, IFLA_MASTER, &info->master);
590
d69b62de
SS
591 r = sd_netlink_message_enter_container(m, IFLA_AF_SPEC);
592 if (r >= 0) {
593 r = sd_netlink_message_enter_container(m, AF_INET6);
594 if (r >= 0) {
595 r = sd_netlink_message_read_u8(m, IFLA_INET6_ADDR_GEN_MODE, &info->addr_gen_mode);
7f20a9e5
SS
596 if (r >= 0 && IN_SET(info->addr_gen_mode,
597 IN6_ADDR_GEN_MODE_EUI64,
598 IN6_ADDR_GEN_MODE_NONE,
599 IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
600 IN6_ADDR_GEN_MODE_RANDOM))
d69b62de
SS
601 info->has_ipv6_address_generation_mode = true;
602
603 (void) sd_netlink_message_exit_container(m);
604 }
605 (void) sd_netlink_message_exit_container(m);
606 }
607
c82d1bf2
SS
608 /* fill kind info */
609 (void) decode_netdev(m, info);
610
e997c4b0
LP
611 return 1;
612}
613
090c923b
MAL
614static int link_get_property(
615 sd_bus *bus,
616 const LinkInfo *link,
617 sd_bus_error *error,
618 sd_bus_message **reply,
619 const char *iface,
620 const char *propname) {
335dd8ba
YW
621 _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
622 int r;
623
624 if (asprintf(&ifindex_str, "%i", link->ifindex) < 0)
625 return -ENOMEM;
626
627 r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path);
628 if (r < 0)
629 return r;
630
090c923b 631 return sd_bus_call_method(
335dd8ba
YW
632 bus,
633 "org.freedesktop.network1",
634 path,
635 "org.freedesktop.DBus.Properties",
636 "Get",
090c923b
MAL
637 error,
638 reply,
335dd8ba 639 "ss",
090c923b
MAL
640 iface,
641 propname);
642}
643
644static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
645 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
646 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
647 int r;
648
649 r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.Link", "BitRates");
335dd8ba 650 if (r < 0) {
955a6329
ZJS
651 bool quiet = sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY,
652 BUS_ERROR_SPEED_METER_INACTIVE);
8210a61a
ZJS
653
654 return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
655 r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
335dd8ba
YW
656 }
657
42a63431 658 r = sd_bus_message_enter_container(reply, 'v', "(tt)");
335dd8ba
YW
659 if (r < 0)
660 return bus_log_parse_error(r);
661
42a63431 662 r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
335dd8ba
YW
663 if (r < 0)
664 return bus_log_parse_error(r);
665
666 r = sd_bus_message_exit_container(reply);
667 if (r < 0)
668 return bus_log_parse_error(r);
669
e813de54 670 link->has_bitrates = link->tx_bitrate != UINT64_MAX && link->rx_bitrate != UINT64_MAX;
335dd8ba
YW
671
672 return 0;
673}
674
516e9c80
ZJS
675static void acquire_ether_link_info(int *fd, LinkInfo *link) {
676 if (ethtool_get_link_info(fd, link->name,
677 &link->autonegotiation,
678 &link->speed,
679 &link->duplex,
680 &link->port) >= 0)
681 link->has_ethtool_link_info = true;
682}
683
8d07de25
ZJS
684static void acquire_wlan_link_info(LinkInfo *link) {
685 _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
686 const char *type = NULL;
78404d22 687 int r, k = 0;
8d07de25
ZJS
688
689 if (link->sd_device)
690 (void) sd_device_get_devtype(link->sd_device, &type);
691 if (!streq_ptr(type, "wlan"))
692 return;
693
694 r = sd_genl_socket_open(&genl);
695 if (r < 0) {
696 log_debug_errno(r, "Failed to open generic netlink socket: %m");
697 return;
698 }
699
700 (void) sd_netlink_inc_rcvbuf(genl, RCVBUF_SIZE);
701
78404d22 702 r = wifi_get_interface(genl, link->ifindex, &link->wlan_iftype, &link->ssid);
8d07de25
ZJS
703 if (r < 0)
704 log_debug_errno(r, "%s: failed to query ssid: %m", link->name);
705
33ebda2e 706 if (link->wlan_iftype == NL80211_IFTYPE_STATION) {
78404d22
YW
707 k = wifi_get_station(genl, link->ifindex, &link->bssid);
708 if (k < 0)
709 log_debug_errno(k, "%s: failed to query bssid: %m", link->name);
710 }
8d07de25
ZJS
711
712 link->has_wlan_link_info = r > 0 || k > 0;
713}
714
335dd8ba 715static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
4afd3348 716 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
172353b1 717 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
c967d2c7 718 _cleanup_close_ int fd = -1;
319a4f4b 719 size_t c = 0;
7d367b45
LP
720 int r;
721
b147503e 722 assert(rtnl);
7d367b45 723 assert(ret);
ee8c4568 724
ee8c4568
LP
725 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
726 if (r < 0)
727 return rtnl_log_create_error(r);
728
1c4baffc 729 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
730 if (r < 0)
731 return rtnl_log_create_error(r);
732
1c4baffc 733 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
734 if (r < 0)
735 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 736
0ef84b80
ZJS
737 _cleanup_free_ bool *matched_patterns = NULL;
738 if (patterns) {
739 matched_patterns = new0(bool, strv_length(patterns));
740 if (!matched_patterns)
741 return log_oom();
742 }
743
cda7fc8d 744 for (sd_netlink_message *i = reply; i; i = sd_netlink_message_next(i)) {
319a4f4b 745 if (!GREEDY_REALLOC0(links, c + 2)) /* We keep one trailing one as marker */
7e5a080a 746 return -ENOMEM;
7d367b45 747
0ef84b80 748 r = decode_link(i, links + c, patterns, matched_patterns);
7e5a080a
LP
749 if (r < 0)
750 return r;
c967d2c7
YW
751 if (r == 0)
752 continue;
753
172353b1
ZJS
754 links[c].needs_freeing = true;
755
0ac655a6 756 (void) sd_device_new_from_ifindex(&links[c].sd_device, links[c].ifindex);
172353b1 757
516e9c80 758 acquire_ether_link_info(&fd, &links[c]);
8d07de25 759 acquire_wlan_link_info(&links[c]);
c967d2c7
YW
760
761 c++;
7e5a080a
LP
762 }
763
0ef84b80
ZJS
764 /* Look if we matched all our arguments that are not globs. It
765 * is OK for a glob to match nothing, but not for an exact argument. */
766 for (size_t pos = 0; pos < strv_length(patterns); pos++) {
767 if (matched_patterns[pos])
768 continue;
769
770 if (string_is_glob(patterns[pos]))
771 log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
772 patterns[pos]);
773 else
774 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
775 "Interface \"%s\" not found.", patterns[pos]);
776 }
777
93bab288 778 typesafe_qsort(links, c, link_info_compare);
7e5a080a 779
335dd8ba 780 if (bus)
cda7fc8d 781 for (size_t j = 0; j < c; j++)
335dd8ba
YW
782 (void) acquire_link_bitrates(bus, links + j);
783
1cc6c93a 784 *ret = TAKE_PTR(links);
7e5a080a 785
0ef84b80
ZJS
786 if (patterns && c == 0)
787 log_warning("No interfaces matched.");
788
7e5a080a 789 return (int) c;
7d367b45 790}
ee8c4568 791
7d367b45 792static int list_links(int argc, char *argv[], void *userdata) {
b147503e 793 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
172353b1 794 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
ff7c88a2
YW
795 _cleanup_(table_unrefp) Table *table = NULL;
796 TableCell *cell;
b48e277f 797 int c, r;
b147503e 798
bfc2b05e
YW
799 if (arg_json_format_flags != JSON_FORMAT_OFF) {
800 if (arg_all || argc <= 1)
801 return dump_manager_description();
802 else
803 return dump_link_description(strv_skip(argv, 1));
804 }
805
b147503e
LP
806 r = sd_netlink_open(&rtnl);
807 if (r < 0)
808 return log_error_errno(r, "Failed to connect to netlink: %m");
7d367b45 809
335dd8ba 810 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
6d0c65ff 811 if (c < 0)
7d367b45
LP
812 return c;
813
384c2c32 814 pager_open(arg_pager_flags);
7d367b45 815
4252171a 816 table = table_new("idx", "link", "type", "operational", "setup");
ff7c88a2
YW
817 if (!table)
818 return log_oom();
819
a42d9490
YW
820 if (arg_full)
821 table_set_width(table, 0);
822
ff7c88a2 823 table_set_header(table, arg_legend);
484f22dd
YW
824 if (table_set_empty_string(table, "n/a") < 0)
825 return log_oom();
ff7c88a2
YW
826
827 assert_se(cell = table_get_cell(table, 0, 0));
828 (void) table_set_minimum_width(table, cell, 3);
829 (void) table_set_weight(table, cell, 0);
81914d9f 830 (void) table_set_ellipsize_percent(table, cell, 100);
ff7c88a2
YW
831 (void) table_set_align_percent(table, cell, 100);
832
833 assert_se(cell = table_get_cell(table, 0, 1));
81914d9f 834 (void) table_set_ellipsize_percent(table, cell, 100);
6d0c65ff 835
b48e277f 836 for (int i = 0; i < c; i++) {
ab1525bc 837 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
37da8bca 838 const char *on_color_operational, *on_color_setup;
ee8c4568 839 _cleanup_free_ char *t = NULL;
ee8c4568 840
4abd866d 841 (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
37da8bca 842 operational_state_to_color(links[i].name, operational_state, &on_color_operational, NULL);
d57c365b 843
33d5013d
LP
844 r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
845 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
846 setup_state = strdup("unmanaged");
37da8bca 847 setup_state_to_color(setup_state, &on_color_setup, NULL);
ee8c4568 848
4eef6fad 849 r = net_get_type_string(links[i].sd_device, links[i].iftype, &t);
1a3caa49
YW
850 if (r == -ENOMEM)
851 return log_oom();
ee8c4568 852
ff7c88a2 853 r = table_add_many(table,
8d0e0af2 854 TABLE_INT, links[i].ifindex,
ff7c88a2 855 TABLE_STRING, links[i].name,
484f22dd
YW
856 TABLE_STRING, t,
857 TABLE_STRING, operational_state,
8d0e0af2 858 TABLE_SET_COLOR, on_color_operational,
484f22dd 859 TABLE_STRING, setup_state,
8d0e0af2 860 TABLE_SET_COLOR, on_color_setup);
ff7c88a2 861 if (r < 0)
bd17fa8c 862 return table_log_add_error(r);
ee8c4568
LP
863 }
864
ff7c88a2
YW
865 r = table_print(table, NULL);
866 if (r < 0)
4b6607d9 867 return table_log_print_error(r);
ff7c88a2 868
ee8c4568 869 if (arg_legend)
6d0c65ff 870 printf("\n%i links listed.\n", c);
ee8c4568
LP
871
872 return 0;
873}
874
c09da729 875/* IEEE Organizationally Unique Identifier vendor string */
b147503e 876static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
81fd1dd3 877 const char *description;
fbd0b64f 878 char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
81fd1dd3
TG
879 int r;
880
881 assert(ret);
c09da729 882
888943fc
LP
883 if (!hwdb)
884 return -EINVAL;
885
81fd1dd3
TG
886 if (!mac)
887 return -EINVAL;
888
c09da729
TG
889 /* skip commonly misused 00:00:00 (Xerox) prefix */
890 if (memcmp(mac, "\0\0\0", 3) == 0)
891 return -EINVAL;
892
d054f0a4
DM
893 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
894 ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 895
81fd1dd3
TG
896 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
897 if (r < 0)
898 return r;
c09da729 899
81fd1dd3
TG
900 desc = strdup(description);
901 if (!desc)
902 return -ENOMEM;
c09da729 903
81fd1dd3
TG
904 *ret = desc;
905
906 return 0;
c09da729
TG
907}
908
69fb1176 909static int get_gateway_description(
1c4baffc 910 sd_netlink *rtnl,
81fd1dd3 911 sd_hwdb *hwdb,
69fb1176
LP
912 int ifindex,
913 int family,
914 union in_addr_union *gateway,
915 char **gateway_description) {
4afd3348 916 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
c09da729
TG
917 int r;
918
919 assert(rtnl);
920 assert(ifindex >= 0);
4c701096 921 assert(IN_SET(family, AF_INET, AF_INET6));
c09da729
TG
922 assert(gateway);
923 assert(gateway_description);
924
925 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
926 if (r < 0)
927 return r;
928
1c4baffc 929 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
930 if (r < 0)
931 return r;
932
1c4baffc 933 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
934 if (r < 0)
935 return r;
936
b48e277f 937 for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
67a46833
YW
938 union in_addr_union gw = IN_ADDR_NULL;
939 struct ether_addr mac = ETHER_ADDR_NULL;
c09da729
TG
940 uint16_t type;
941 int ifi, fam;
942
1c4baffc 943 r = sd_netlink_message_get_errno(m);
c09da729
TG
944 if (r < 0) {
945 log_error_errno(r, "got error: %m");
946 continue;
947 }
948
1c4baffc 949 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
950 if (r < 0) {
951 log_error_errno(r, "could not get type: %m");
952 continue;
953 }
954
955 if (type != RTM_NEWNEIGH) {
956 log_error("type is not RTM_NEWNEIGH");
957 continue;
958 }
959
960 r = sd_rtnl_message_neigh_get_family(m, &fam);
961 if (r < 0) {
962 log_error_errno(r, "could not get family: %m");
963 continue;
964 }
965
966 if (fam != family) {
967 log_error("family is not correct");
968 continue;
969 }
970
971 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
972 if (r < 0) {
144232a8 973 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
974 continue;
975 }
976
977 if (ifindex > 0 && ifi != ifindex)
978 continue;
979
980 switch (fam) {
981 case AF_INET:
1c4baffc 982 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
983 if (r < 0)
984 continue;
985
986 break;
987 case AF_INET6:
1c4baffc 988 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
989 if (r < 0)
990 continue;
991
992 break;
993 default:
994 continue;
995 }
996
997 if (!in_addr_equal(fam, &gw, gateway))
998 continue;
999
49808e0e 1000 r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
c09da729
TG
1001 if (r < 0)
1002 continue;
1003
1004 r = ieee_oui(hwdb, &mac, gateway_description);
1005 if (r < 0)
1006 continue;
1007
1008 return 0;
1009 }
1010
1011 return -ENODATA;
1012}
1013
536cdd07
YW
1014static int dump_list(Table *table, const char *prefix, char * const *l) {
1015 int r;
1016
1017 if (strv_isempty(l))
1018 return 0;
1019
1020 r = table_add_many(table,
1021 TABLE_EMPTY,
1022 TABLE_STRING, prefix,
1023 TABLE_STRV, l);
1024 if (r < 0)
1025 return table_log_add_error(r);
1026
1027 return 0;
1028}
1029
69fb1176 1030static int dump_gateways(
1c4baffc 1031 sd_netlink *rtnl,
81fd1dd3 1032 sd_hwdb *hwdb,
98d5bef3 1033 Table *table,
69fb1176 1034 int ifindex) {
b6a3ca6d 1035 _cleanup_free_ struct local_address *local = NULL;
536cdd07 1036 _cleanup_strv_free_ char **buf = NULL;
cda7fc8d 1037 int r, n;
c09da729 1038
837f57da 1039 assert(rtnl);
98d5bef3 1040 assert(table);
837f57da 1041
b6a3ca6d 1042 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
536cdd07 1043 if (n <= 0)
b6a3ca6d 1044 return n;
c09da729 1045
cda7fc8d 1046 for (int i = 0; i < n; i++) {
4ff6ff9a 1047 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 1048
b6a3ca6d 1049 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 1050 if (r < 0)
4ff6ff9a 1051 return log_oom();
c09da729 1052
69fb1176 1053 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
c09da729 1054 if (r < 0)
536cdd07 1055 log_debug_errno(r, "Could not get description of gateway, ignoring: %m");
c09da729 1056
98d5bef3 1057 if (description) {
4ff6ff9a 1058 if (!strextend(&gateway, " (", description, ")"))
536cdd07 1059 return log_oom();
98d5bef3 1060 }
1693a943 1061
536cdd07
YW
1062 /* Show interface name for the entry if we show entries for all interfaces */
1063 r = strv_extendf(&buf, "%s%s%s",
4ff6ff9a 1064 gateway,
536cdd07 1065 ifindex <= 0 ? " on " : "",
01afd0f7 1066 ifindex <= 0 ? FORMAT_IFNAME_FULL(local[i].ifindex, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
98d5bef3 1067 if (r < 0)
536cdd07 1068 return log_oom();
c09da729
TG
1069 }
1070
536cdd07 1071 return dump_list(table, "Gateway:", buf);
c09da729
TG
1072}
1073
69fb1176 1074static int dump_addresses(
1c4baffc 1075 sd_netlink *rtnl,
d41fa6ee 1076 sd_dhcp_lease *lease,
98d5bef3 1077 Table *table,
69fb1176
LP
1078 int ifindex) {
1079
ee8c4568 1080 _cleanup_free_ struct local_address *local = NULL;
536cdd07 1081 _cleanup_strv_free_ char **buf = NULL;
d41fa6ee 1082 struct in_addr dhcp4_address = {};
b48e277f 1083 int r, n;
ee8c4568 1084
837f57da 1085 assert(rtnl);
98d5bef3 1086 assert(table);
837f57da 1087
1d050e1e 1088 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
536cdd07 1089 if (n <= 0)
ee8c4568
LP
1090 return n;
1091
d41fa6ee
YW
1092 if (lease)
1093 (void) sd_dhcp_lease_get_address(lease, &dhcp4_address);
cdf01b36 1094
b48e277f 1095 for (int i = 0; i < n; i++) {
ee8c4568 1096 _cleanup_free_ char *pretty = NULL;
1693a943 1097
98d5bef3
YW
1098 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
1099 if (r < 0)
1100 return r;
1693a943 1101
d41fa6ee 1102 if (local[i].family == AF_INET && in4_addr_equal(&local[i].address.in, &dhcp4_address)) {
3d0c8750
SS
1103 struct in_addr server_address;
1104 char *p, s[INET_ADDRSTRLEN];
1105
1106 r = sd_dhcp_lease_get_server_identifier(lease, &server_address);
1107 if (r >= 0 && inet_ntop(AF_INET, &server_address, s, sizeof(s)))
1108 p = strjoin(pretty, " (DHCP4 via ", s, ")");
1109 else
1110 p = strjoin(pretty, " (DHCP4)");
1111 if (!p)
cdf01b36 1112 return log_oom();
3d0c8750
SS
1113
1114 free_and_replace(pretty, p);
cdf01b36
SS
1115 }
1116
536cdd07
YW
1117 r = strv_extendf(&buf, "%s%s%s",
1118 pretty,
1119 ifindex <= 0 ? " on " : "",
01afd0f7 1120 ifindex <= 0 ? FORMAT_IFNAME_FULL(local[i].ifindex, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
98d5bef3 1121 if (r < 0)
536cdd07 1122 return log_oom();
ee8c4568
LP
1123 }
1124
536cdd07 1125 return dump_list(table, "Address:", buf);
ee8c4568
LP
1126}
1127
d37b7627
SS
1128static int dump_address_labels(sd_netlink *rtnl) {
1129 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
0232beed 1130 _cleanup_(table_unrefp) Table *table = NULL;
0232beed 1131 TableCell *cell;
d37b7627
SS
1132 int r;
1133
1134 assert(rtnl);
1135
1136 r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
1137 if (r < 0)
1138 return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
1139
1140 r = sd_netlink_message_request_dump(req, true);
1141 if (r < 0)
1142 return r;
1143
1144 r = sd_netlink_call(rtnl, req, 0, &reply);
1145 if (r < 0)
1146 return r;
1147
4252171a 1148 table = table_new("label", "prefix/prefixlen");
0232beed 1149 if (!table)
bd17fa8c 1150 return log_oom();
0232beed 1151
a42d9490
YW
1152 if (arg_full)
1153 table_set_width(table, 0);
1154
ef1e0b9a 1155 r = table_set_sort(table, (size_t) 0);
0232beed
YW
1156 if (r < 0)
1157 return r;
1158
1159 assert_se(cell = table_get_cell(table, 0, 0));
1160 (void) table_set_align_percent(table, cell, 100);
81914d9f 1161 (void) table_set_ellipsize_percent(table, cell, 100);
0232beed
YW
1162
1163 assert_se(cell = table_get_cell(table, 0, 1));
1164 (void) table_set_align_percent(table, cell, 100);
d37b7627 1165
b48e277f 1166 for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
d37b7627 1167 _cleanup_free_ char *pretty = NULL;
67a46833 1168 union in_addr_union prefix = IN_ADDR_NULL;
d37b7627
SS
1169 uint8_t prefixlen;
1170 uint32_t label;
1171
1172 r = sd_netlink_message_get_errno(m);
1173 if (r < 0) {
1174 log_error_errno(r, "got error: %m");
1175 continue;
1176 }
1177
1178 r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
1179 if (r < 0 && r != -ENODATA) {
1180 log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
1181 continue;
1182 }
1183
1184 r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
1185 if (r < 0)
1186 continue;
1187
1188 r = in_addr_to_string(AF_INET6, &prefix, &pretty);
1189 if (r < 0)
1190 continue;
1191
1192 r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
1193 if (r < 0)
1194 continue;
1195
81914d9f 1196 r = table_add_cell(table, NULL, TABLE_UINT32, &label);
0232beed 1197 if (r < 0)
bd17fa8c 1198 return table_log_add_error(r);
0232beed 1199
8d0e0af2 1200 r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
0232beed 1201 if (r < 0)
bd17fa8c 1202 return table_log_add_error(r);
d37b7627
SS
1203 }
1204
bd17fa8c
YW
1205 r = table_print(table, NULL);
1206 if (r < 0)
4b6607d9 1207 return table_log_print_error(r);
bd17fa8c
YW
1208
1209 return 0;
d37b7627
SS
1210}
1211
1212static int list_address_labels(int argc, char *argv[], void *userdata) {
1213 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1214 int r;
1215
1216 r = sd_netlink_open(&rtnl);
1217 if (r < 0)
1218 return log_error_errno(r, "Failed to connect to netlink: %m");
1219
1220 dump_address_labels(rtnl);
1221
1222 return 0;
1223}
1224
837f57da
LP
1225static int open_lldp_neighbors(int ifindex, FILE **ret) {
1226 _cleanup_free_ char *p = NULL;
1227 FILE *f;
1228
1229 if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
1230 return -ENOMEM;
1231
1232 f = fopen(p, "re");
1233 if (!f)
1234 return -errno;
1235
1236 *ret = f;
1237 return 0;
1238}
1239
1240static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
1241 _cleanup_free_ void *raw = NULL;
1242 size_t l;
1243 le64_t u;
1244 int r;
1245
1246 assert(f);
1247 assert(ret);
1248
1249 l = fread(&u, 1, sizeof(u), f);
1250 if (l == 0 && feof(f))
1251 return 0;
1252 if (l != sizeof(u))
1253 return -EBADMSG;
1254
d23c3e4c
FB
1255 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
1256 if (le64toh(u) >= 4096)
1257 return -EBADMSG;
1258
837f57da
LP
1259 raw = new(uint8_t, le64toh(u));
1260 if (!raw)
1261 return -ENOMEM;
1262
1263 if (fread(raw, 1, le64toh(u), f) != le64toh(u))
1264 return -EBADMSG;
1265
1266 r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
1267 if (r < 0)
1268 return r;
1269
1270 return 1;
1271}
1272
98d5bef3 1273static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
536cdd07 1274 _cleanup_strv_free_ char **buf = NULL;
837f57da 1275 _cleanup_fclose_ FILE *f = NULL;
536cdd07 1276 int r;
837f57da 1277
98d5bef3 1278 assert(table);
837f57da
LP
1279 assert(prefix);
1280 assert(ifindex > 0);
1281
1282 r = open_lldp_neighbors(ifindex, &f);
98d5bef3
YW
1283 if (r == -ENOENT)
1284 return 0;
837f57da
LP
1285 if (r < 0)
1286 return r;
1287
1288 for (;;) {
1289 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
1290 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1291
1292 r = next_lldp_neighbor(f, &n);
1293 if (r < 0)
1294 return r;
1295 if (r == 0)
1296 break;
1297
837f57da
LP
1298 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1299 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1300 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1301
536cdd07
YW
1302 r = strv_extendf(&buf, "%s on port %s%s%s%s",
1303 strna(system_name),
1304 strna(port_id),
1305 isempty(port_description) ? "" : " (",
1306 strempty(port_description),
1307 isempty(port_description) ? "" : ")");
98d5bef3 1308 if (r < 0)
536cdd07 1309 return log_oom();
837f57da
LP
1310 }
1311
536cdd07 1312 return dump_list(table, prefix, buf);
837f57da
LP
1313}
1314
d9ce1c24
MAL
1315static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
1316 _cleanup_strv_free_ char **buf = NULL;
1317 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1318 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1319 int r;
1320
1321 r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases");
1322 if (r < 0) {
1323 bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
1324
1325 log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
1326 r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r));
1327 return 0;
1328 }
1329
1330 r = sd_bus_message_enter_container(reply, 'v', "a(uayayayayt)");
1331 if (r < 0)
1332 return bus_log_parse_error(r);
1333
1334 r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)");
1335 if (r < 0)
1336 return bus_log_parse_error(r);
1337
1338 while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) {
1339 _cleanup_free_ char *id = NULL, *ip = NULL;
1340 const void *client_id, *addr, *gtw, *hwaddr;
1341 size_t client_id_sz, sz;
1342 uint64_t expiration;
1343 uint32_t family;
1344
1345 r = sd_bus_message_read(reply, "u", &family);
1346 if (r < 0)
1347 return bus_log_parse_error(r);
1348
1349 r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz);
1350 if (r < 0)
1351 return bus_log_parse_error(r);
1352
1353 r = sd_bus_message_read_array(reply, 'y', &addr, &sz);
1354 if (r < 0 || sz != 4)
1355 return bus_log_parse_error(r);
1356
1357 r = sd_bus_message_read_array(reply, 'y', &gtw, &sz);
1358 if (r < 0 || sz != 4)
1359 return bus_log_parse_error(r);
1360
1361 r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz);
1362 if (r < 0)
1363 return bus_log_parse_error(r);
1364
1365 r = sd_bus_message_read_basic(reply, 't', &expiration);
1366 if (r < 0)
1367 return bus_log_parse_error(r);
1368
1369 r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id);
1370 if (r < 0)
1371 return bus_log_parse_error(r);
1372
1373 r = in_addr_to_string(family, addr, &ip);
1374 if (r < 0)
1375 return bus_log_parse_error(r);
1376
1377 r = strv_extendf(&buf, "%s (to %s)", ip, id);
1378 if (r < 0)
1379 return log_oom();
1380
1381 r = sd_bus_message_exit_container(reply);
1382 if (r < 0)
1383 return bus_log_parse_error(r);
1384 }
1385 if (r < 0)
1386 return bus_log_parse_error(r);
1387
1388 r = sd_bus_message_exit_container(reply);
1389 if (r < 0)
1390 return bus_log_parse_error(r);
1391
1392 r = sd_bus_message_exit_container(reply);
1393 if (r < 0)
1394 return bus_log_parse_error(r);
1395
1396 if (strv_isempty(buf)) {
1397 r = strv_extendf(&buf, "none");
1398 if (r < 0)
1399 return log_oom();
1400 }
1401
1402 return dump_list(table, prefix, buf);
1403}
1404
98d5bef3 1405static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
98d5bef3 1406 int r;
b295beea
LP
1407
1408 assert(prefix);
1409
1410 if (!ifindexes || ifindexes[0] <= 0)
98d5bef3 1411 return 0;
b295beea 1412
b48e277f 1413 for (unsigned c = 0; ifindexes[c] > 0; c++) {
8d0e0af2
YW
1414 r = table_add_many(table,
1415 TABLE_EMPTY,
1416 TABLE_STRING, c == 0 ? prefix : "",
1417 TABLE_IFINDEX, ifindexes[c]);
98d5bef3 1418 if (r < 0)
bd17fa8c 1419 return table_log_add_error(r);
b295beea 1420 }
98d5bef3
YW
1421
1422 return 0;
b295beea
LP
1423}
1424
a459b24f 1425#define DUMP_STATS_ONE(name, val_name) \
8d0e0af2
YW
1426 r = table_add_many(table, \
1427 TABLE_EMPTY, \
1428 TABLE_STRING, name ":"); \
a459b24f 1429 if (r < 0) \
bd17fa8c 1430 return table_log_add_error(r); \
8d0e0af2
YW
1431 r = table_add_cell(table, NULL, \
1432 info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
a459b24f
YW
1433 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
1434 if (r < 0) \
bd17fa8c 1435 return table_log_add_error(r);
a459b24f
YW
1436
1437static int dump_statistics(Table *table, const LinkInfo *info) {
1438 int r;
1439
1440 if (!arg_stats)
1441 return 0;
1442
1443 if (!info->has_stats64 && !info->has_stats)
1444 return 0;
1445
1446 DUMP_STATS_ONE("Rx Packets", rx_packets);
1447 DUMP_STATS_ONE("Tx Packets", tx_packets);
1448 DUMP_STATS_ONE("Rx Bytes", rx_bytes);
1449 DUMP_STATS_ONE("Tx Bytes", tx_bytes);
1450 DUMP_STATS_ONE("Rx Errors", rx_errors);
1451 DUMP_STATS_ONE("Tx Errors", tx_errors);
1452 DUMP_STATS_ONE("Rx Dropped", rx_dropped);
1453 DUMP_STATS_ONE("Tx Dropped", tx_dropped);
1454 DUMP_STATS_ONE("Multicast Packets", multicast);
1455 DUMP_STATS_ONE("Collisions", collisions);
1456
1457 return 0;
1458}
1459
57ae8cd8
YW
1460static int dump_hw_address(Table *table, sd_hwdb *hwdb, const char *field, const struct hw_addr_data *addr) {
1461 _cleanup_free_ char *description = NULL;
1462 int r;
1463
1464 assert(table);
1465 assert(field);
1466 assert(addr);
1467
1468 if (addr->length == ETH_ALEN)
1469 (void) ieee_oui(hwdb, &addr->ether, &description);
1470
1471 r = table_add_many(table,
1472 TABLE_EMPTY,
1473 TABLE_STRING, field);
1474 if (r < 0)
1475 return table_log_add_error(r);
1476
1477 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
1478 HW_ADDR_TO_STR(addr),
1479 description ? " (" : "",
1480 strempty(description),
1481 description ? ")" : "");
1482 if (r < 0)
1483 return table_log_add_error(r);
1484
1485 return 0;
1486}
1487
10c71c36
YW
1488static OutputFlags get_output_flags(void) {
1489 return
1490 arg_all * OUTPUT_SHOW_ALL |
1491 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1492 colors_enabled() * OUTPUT_COLOR;
1493}
1494
1495static int show_logs(const LinkInfo *info) {
1496 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1497 int r;
1498
1499 if (arg_lines == 0)
1500 return 0;
1501
1502 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
1503 if (r < 0)
1504 return log_error_errno(r, "Failed to open journal: %m");
1505
1506 r = add_match_this_boot(j, NULL);
1507 if (r < 0)
1508 return log_error_errno(r, "Failed to add boot matches: %m");
1509
1510 if (info) {
1511 char m1[STRLEN("_KERNEL_DEVICE=n") + DECIMAL_STR_MAX(int)];
1512 const char *m2, *m3;
1513
1514 /* kernel */
1515 xsprintf(m1, "_KERNEL_DEVICE=n%i", info->ifindex);
1516 /* networkd */
1517 m2 = strjoina("INTERFACE=", info->name);
1518 /* udevd */
1519 m3 = strjoina("DEVICE=", info->name);
1520
1521 (void)(
1522 (r = sd_journal_add_match(j, m1, 0)) ||
1523 (r = sd_journal_add_disjunction(j)) ||
1524 (r = sd_journal_add_match(j, m2, 0)) ||
1525 (r = sd_journal_add_disjunction(j)) ||
1526 (r = sd_journal_add_match(j, m3, 0))
1527 );
1528 if (r < 0)
1529 return log_error_errno(r, "Failed to add link matches: %m");
1530 } else {
1531 r = add_matches_for_unit(j, "systemd-networkd.service");
1532 if (r < 0)
1533 return log_error_errno(r, "Failed to add unit matches: %m");
1534
1535 r = add_matches_for_unit(j, "systemd-networkd-wait-online.service");
1536 if (r < 0)
1537 return log_error_errno(r, "Failed to add unit matches: %m");
1538 }
1539
1540 return show_journal(
1541 stdout,
1542 j,
1543 OUTPUT_SHORT,
1544 0,
1545 0,
1546 arg_lines,
1547 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
1548 NULL);
1549}
1550
69fb1176 1551static int link_status_one(
d9ce1c24 1552 sd_bus *bus,
1c4baffc 1553 sd_netlink *rtnl,
81fd1dd3 1554 sd_hwdb *hwdb,
b147503e
LP
1555 const LinkInfo *info) {
1556
2a71d57f 1557 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL;
862e7108 1558 _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL,
4a481ec4 1559 *setup_state = NULL, *operational_state = NULL, *online_state = NULL, *lease_file = NULL, *activation_policy = NULL;
35cab5f9 1560 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL,
4a481ec4 1561 *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup, *on_color_online;
b295beea 1562 _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
35cab5f9 1563 _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
98d5bef3
YW
1564 _cleanup_(table_unrefp) Table *table = NULL;
1565 TableCell *cell;
b147503e 1566 int r;
9085f64a
LP
1567
1568 assert(rtnl);
b147503e 1569 assert(info);
9085f64a 1570
b147503e 1571 (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
ceb366df 1572 operational_state_to_color(info->name, operational_state, &on_color_operational, &off_color_operational);
d57c365b 1573
4a481ec4
1574 (void) sd_network_link_get_online_state(info->ifindex, &online_state);
1575 online_state_to_color(online_state, &on_color_online, NULL);
1576
33d5013d
LP
1577 r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
1578 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
1579 setup_state = strdup("unmanaged");
d57c365b 1580 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 1581
b147503e
LP
1582 (void) sd_network_link_get_dns(info->ifindex, &dns);
1583 (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
1584 (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
1585 (void) sd_network_link_get_ntp(info->ifindex, &ntp);
1f807af6 1586 (void) sd_network_link_get_sip(info->ifindex, &sip);
9085f64a 1587
172353b1
ZJS
1588 if (info->sd_device) {
1589 (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link);
1590 (void) sd_device_get_property_value(info->sd_device, "ID_NET_DRIVER", &driver);
1591 (void) sd_device_get_property_value(info->sd_device, "ID_PATH", &path);
9085f64a 1592
172353b1
ZJS
1593 if (sd_device_get_property_value(info->sd_device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
1594 (void) sd_device_get_property_value(info->sd_device, "ID_VENDOR", &vendor);
9085f64a 1595
172353b1
ZJS
1596 if (sd_device_get_property_value(info->sd_device, "ID_MODEL_FROM_DATABASE", &model) < 0)
1597 (void) sd_device_get_property_value(info->sd_device, "ID_MODEL", &model);
9085f64a
LP
1598 }
1599
4eef6fad 1600 r = net_get_type_string(info->sd_device, info->iftype, &t);
1a3caa49
YW
1601 if (r == -ENOMEM)
1602 return log_oom();
b1acce80 1603
4abd866d 1604 (void) sd_network_link_get_network_file(info->ifindex, &network);
df3fb561 1605
4abd866d
LP
1606 (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
1607 (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
0d4ad91d 1608
35cab5f9
YW
1609 if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", info->ifindex) < 0)
1610 return log_oom();
1611
1612 (void) dhcp_lease_load(&lease, lease_file);
1613
4252171a 1614 table = table_new("dot", "key", "value");
98d5bef3 1615 if (!table)
bd17fa8c 1616 return log_oom();
98d5bef3 1617
a42d9490
YW
1618 if (arg_full)
1619 table_set_width(table, 0);
1620
81914d9f
YW
1621 assert_se(cell = table_get_cell(table, 0, 0));
1622 (void) table_set_ellipsize_percent(table, cell, 100);
1623
1624 assert_se(cell = table_get_cell(table, 0, 1));
1625 (void) table_set_ellipsize_percent(table, cell, 100);
1626
98d5bef3
YW
1627 table_set_header(table, false);
1628
8d0e0af2
YW
1629 r = table_add_many(table,
1630 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1631 TABLE_SET_COLOR, on_color_operational);
98d5bef3 1632 if (r < 0)
bd17fa8c 1633 return table_log_add_error(r);
81914d9f 1634 r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
98d5bef3 1635 if (r < 0)
bd17fa8c 1636 return table_log_add_error(r);
81914d9f 1637 (void) table_set_align_percent(table, cell, 0);
98d5bef3 1638
8d0e0af2
YW
1639 r = table_add_many(table,
1640 TABLE_EMPTY,
1641 TABLE_EMPTY,
1642 TABLE_STRING, "Link File:",
1643 TABLE_SET_ALIGN_PERCENT, 100,
1644 TABLE_STRING, strna(link),
1645 TABLE_EMPTY,
1646 TABLE_STRING, "Network File:",
1647 TABLE_STRING, strna(network),
1648 TABLE_EMPTY,
1649 TABLE_STRING, "Type:",
1650 TABLE_STRING, strna(t),
1651 TABLE_EMPTY,
1652 TABLE_STRING, "State:");
98d5bef3 1653 if (r < 0)
bd17fa8c 1654 return table_log_add_error(r);
98d5bef3 1655 r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
64e7ebde
YW
1656 on_color_operational, strna(operational_state), off_color_operational,
1657 on_color_setup, strna(setup_state), off_color_setup);
98d5bef3 1658 if (r < 0)
bd17fa8c 1659 return table_log_add_error(r);
98d5bef3 1660
4a481ec4
1661 r = table_add_many(table,
1662 TABLE_EMPTY,
1663 TABLE_STRING, "Online state:",
1664 TABLE_STRING, online_state ?: "unknown",
1665 TABLE_SET_COLOR, on_color_online);
1666 if (r < 0)
1667 return table_log_add_error(r);
1668
ae5b7792 1669 strv_sort(info->alternative_names);
536cdd07
YW
1670 r = dump_list(table, "Alternative Names:", info->alternative_names);
1671 if (r < 0)
1672 return r;
511070ee 1673
98d5bef3 1674 if (path) {
8d0e0af2
YW
1675 r = table_add_many(table,
1676 TABLE_EMPTY,
1677 TABLE_STRING, "Path:",
1678 TABLE_STRING, path);
98d5bef3 1679 if (r < 0)
bd17fa8c 1680 return table_log_add_error(r);
98d5bef3
YW
1681 }
1682 if (driver) {
8d0e0af2
YW
1683 r = table_add_many(table,
1684 TABLE_EMPTY,
1685 TABLE_STRING, "Driver:",
1686 TABLE_STRING, driver);
98d5bef3 1687 if (r < 0)
bd17fa8c 1688 return table_log_add_error(r);
98d5bef3
YW
1689 }
1690 if (vendor) {
8d0e0af2
YW
1691 r = table_add_many(table,
1692 TABLE_EMPTY,
1693 TABLE_STRING, "Vendor:",
1694 TABLE_STRING, vendor);
98d5bef3 1695 if (r < 0)
bd17fa8c 1696 return table_log_add_error(r);
98d5bef3
YW
1697 }
1698 if (model) {
8d0e0af2
YW
1699 r = table_add_many(table,
1700 TABLE_EMPTY,
1701 TABLE_STRING, "Model:",
1702 TABLE_STRING, model);
98d5bef3 1703 if (r < 0)
bd17fa8c 1704 return table_log_add_error(r);
98d5bef3 1705 }
9085f64a 1706
57ae8cd8
YW
1707 if (info->has_hw_address) {
1708 r = dump_hw_address(table, hwdb, "Hardware Address:", &info->hw_address);
98d5bef3 1709 if (r < 0)
57ae8cd8 1710 return r;
db73295a 1711 }
9085f64a 1712
ed9fa69f
YW
1713 if (info->has_permanent_hw_address) {
1714 r = dump_hw_address(table, hwdb, "Permanent Hardware Address:", &info->permanent_hw_address);
caa8538a 1715 if (r < 0)
ed9fa69f 1716 return r;
caa8538a
YW
1717 }
1718
c06ff86e
YW
1719 if (info->mtu > 0) {
1720 char min_str[DECIMAL_STR_MAX(uint32_t)], max_str[DECIMAL_STR_MAX(uint32_t)];
1721
1722 xsprintf(min_str, "%" PRIu32, info->min_mtu);
1723 xsprintf(max_str, "%" PRIu32, info->max_mtu);
1724
8d0e0af2
YW
1725 r = table_add_many(table,
1726 TABLE_EMPTY,
1727 TABLE_STRING, "MTU:");
98d5bef3 1728 if (r < 0)
bd17fa8c 1729 return table_log_add_error(r);
c06ff86e
YW
1730 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "%s%s%s%s%s%s%s",
1731 info->mtu,
1732 info->min_mtu > 0 || info->max_mtu > 0 ? " (" : "",
90e29fe1 1733 info->min_mtu > 0 ? "min: " : "",
c06ff86e
YW
1734 info->min_mtu > 0 ? min_str : "",
1735 info->min_mtu > 0 && info->max_mtu > 0 ? ", " : "",
90e29fe1 1736 info->max_mtu > 0 ? "max: " : "",
c06ff86e
YW
1737 info->max_mtu > 0 ? max_str : "",
1738 info->min_mtu > 0 || info->max_mtu > 0 ? ")" : "");
98d5bef3 1739 if (r < 0)
e810df37
SS
1740 return table_log_add_error(r);
1741 }
1742
1743 if (info->qdisc) {
1744 r = table_add_many(table,
1745 TABLE_EMPTY,
1746 TABLE_STRING, "QDisc:",
1747 TABLE_STRING, info->qdisc);
1748 if (r < 0)
6cfef1b3
SS
1749 return table_log_add_error(r);
1750 }
1751
1752 if (info->master > 0) {
1753 r = table_add_many(table,
1754 TABLE_EMPTY,
1755 TABLE_STRING, "Master:",
1756 TABLE_IFINDEX, info->master);
1757 if (r < 0)
bd17fa8c 1758 return table_log_add_error(r);
98d5bef3 1759 }
8eb9058d 1760
d69b62de
SS
1761 if (info->has_ipv6_address_generation_mode) {
1762 static const struct {
1763 const char *mode;
1764 } mode_table[] = {
1765 { "eui64" },
1766 { "none" },
1767 { "stable-privacy" },
1768 { "random" },
1769 };
1770
1771 r = table_add_many(table,
1772 TABLE_EMPTY,
1773 TABLE_STRING, "IPv6 Address Generation Mode:",
1774 TABLE_STRING, mode_table[info->addr_gen_mode]);
1775 if (r < 0)
1776 return table_log_add_error(r);
1777 }
1778
c82d1bf2
SS
1779 if (streq_ptr(info->netdev_kind, "bridge")) {
1780 r = table_add_many(table,
1781 TABLE_EMPTY,
1782 TABLE_STRING, "Forward Delay:",
1783 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->forward_delay),
1784 TABLE_EMPTY,
1785 TABLE_STRING, "Hello Time:",
1786 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->hello_time),
1787 TABLE_EMPTY,
1788 TABLE_STRING, "Max Age:",
1789 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->max_age),
1790 TABLE_EMPTY,
1791 TABLE_STRING, "Ageing Time:",
1792 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->ageing_time),
1793 TABLE_EMPTY,
1794 TABLE_STRING, "Priority:",
1795 TABLE_UINT16, info->priority,
1796 TABLE_EMPTY,
1797 TABLE_STRING, "STP:",
1798 TABLE_BOOLEAN, info->stp_state > 0,
1799 TABLE_EMPTY,
1800 TABLE_STRING, "Multicast IGMP Version:",
12ef8fb6
SS
1801 TABLE_UINT8, info->mcast_igmp_version,
1802 TABLE_EMPTY,
1803 TABLE_STRING, "Cost:",
1804 TABLE_UINT32, info->cost);
c82d1bf2 1805 if (r < 0)
bd17fa8c 1806 return table_log_add_error(r);
b24281aa 1807
d46b79bb 1808 if (info->port_state <= BR_STATE_BLOCKING)
12ef8fb6
SS
1809 r = table_add_many(table,
1810 TABLE_EMPTY,
1811 TABLE_STRING, "Port State:",
a8389a33 1812 TABLE_STRING, bridge_state_to_string(info->port_state));
b1d6fe70 1813 } else if (streq_ptr(info->netdev_kind, "bond")) {
b1d6fe70
SS
1814 r = table_add_many(table,
1815 TABLE_EMPTY,
1816 TABLE_STRING, "Mode:",
43bf2874 1817 TABLE_STRING, bond_mode_to_string(info->mode),
b1d6fe70
SS
1818 TABLE_EMPTY,
1819 TABLE_STRING, "Miimon:",
1820 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->miimon),
1821 TABLE_EMPTY,
1822 TABLE_STRING, "Updelay:",
1823 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->updelay),
1824 TABLE_EMPTY,
1825 TABLE_STRING, "Downdelay:",
1826 TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->downdelay));
1827 if (r < 0)
1828 return table_log_add_error(r);
1829
b24281aa 1830 } else if (streq_ptr(info->netdev_kind, "vxlan")) {
22ae6c7d
SS
1831 char ttl[CONST_MAX(STRLEN("auto") + 1, DECIMAL_STR_MAX(uint8_t))];
1832
b24281aa
SS
1833 if (info->vxlan_info.vni > 0) {
1834 r = table_add_many(table,
1835 TABLE_EMPTY,
1836 TABLE_STRING, "VNI:",
1837 TABLE_UINT32, info->vxlan_info.vni);
1838 if (r < 0)
bd17fa8c 1839 return table_log_add_error(r);
b24281aa
SS
1840 }
1841
1842 if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
659f85a5
SS
1843 const char *p;
1844
1845 r = in_addr_is_multicast(info->vxlan_info.group_family, &info->vxlan_info.group);
1846 if (r <= 0)
1847 p = "Remote:";
1848 else
1849 p = "Group:";
1850
b24281aa
SS
1851 r = table_add_many(table,
1852 TABLE_EMPTY,
659f85a5 1853 TABLE_STRING, p,
b24281aa
SS
1854 info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1855 &info->vxlan_info.group);
1856 if (r < 0)
bd17fa8c 1857 return table_log_add_error(r);
b24281aa
SS
1858 }
1859
1860 if (IN_SET(info->vxlan_info.local_family, AF_INET, AF_INET6)) {
1861 r = table_add_many(table,
1862 TABLE_EMPTY,
1863 TABLE_STRING, "Local:",
1864 info->vxlan_info.local_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1865 &info->vxlan_info.local);
1866 if (r < 0)
bd17fa8c 1867 return table_log_add_error(r);
b24281aa
SS
1868 }
1869
1870 if (info->vxlan_info.dest_port > 0) {
1871 r = table_add_many(table,
1872 TABLE_EMPTY,
1873 TABLE_STRING, "Destination Port:",
1874 TABLE_UINT16, be16toh(info->vxlan_info.dest_port));
1875 if (r < 0)
bd17fa8c 1876 return table_log_add_error(r);
b24281aa
SS
1877 }
1878
1879 if (info->vxlan_info.link > 0) {
1880 r = table_add_many(table,
1881 TABLE_EMPTY,
1882 TABLE_STRING, "Underlying Device:",
1883 TABLE_IFINDEX, info->vxlan_info.link);
1884 if (r < 0)
bd17fa8c 1885 return table_log_add_error(r);
b24281aa 1886 }
22ae6c7d
SS
1887
1888 r = table_add_many(table,
1889 TABLE_EMPTY,
1890 TABLE_STRING, "Learning:",
1891 TABLE_BOOLEAN, info->vxlan_info.learning);
1892 if (r < 0)
1893 return table_log_add_error(r);
1894
1895 r = table_add_many(table,
1896 TABLE_EMPTY,
1897 TABLE_STRING, "RSC:",
1898 TABLE_BOOLEAN, info->vxlan_info.rsc);
1899 if (r < 0)
1900 return table_log_add_error(r);
1901
1902 r = table_add_many(table,
1903 TABLE_EMPTY,
1904 TABLE_STRING, "L3MISS:",
1905 TABLE_BOOLEAN, info->vxlan_info.l3miss);
1906 if (r < 0)
1907 return table_log_add_error(r);
1908
1909 r = table_add_many(table,
1910 TABLE_EMPTY,
1911 TABLE_STRING, "L2MISS:",
1912 TABLE_BOOLEAN, info->vxlan_info.l2miss);
1913 if (r < 0)
1914 return table_log_add_error(r);
1915
1916 if (info->vxlan_info.tos > 1) {
1917 r = table_add_many(table,
1918 TABLE_EMPTY,
1919 TABLE_STRING, "TOS:",
1920 TABLE_UINT8, info->vxlan_info.tos);
1921 if (r < 0)
1922 return table_log_add_error(r);
1923 }
1924
1925 if (info->vxlan_info.ttl > 0)
1926 xsprintf(ttl, "%" PRIu8, info->vxlan_info.ttl);
1927 else
1928 strcpy(ttl, "auto");
1929
1930 r = table_add_many(table,
1931 TABLE_EMPTY,
1932 TABLE_STRING, "TTL:",
1933 TABLE_STRING, ttl);
1934 if (r < 0)
1935 return table_log_add_error(r);
2b2a1ae6
SS
1936 } else if (streq_ptr(info->netdev_kind, "vlan") && info->vlan_id > 0) {
1937 r = table_add_many(table,
1938 TABLE_EMPTY,
1939 TABLE_STRING, "VLan Id:",
1940 TABLE_UINT16, info->vlan_id);
1941 if (r < 0)
1942 return table_log_add_error(r);
5712d689 1943 } else if (STRPTR_IN_SET(info->netdev_kind, "ipip", "sit", "gre", "gretap", "erspan", "vti")) {
94876904 1944 if (in_addr_is_set(AF_INET, &info->local)) {
dca35224
SS
1945 r = table_add_many(table,
1946 TABLE_EMPTY,
1947 TABLE_STRING, "Local:",
1948 TABLE_IN_ADDR, &info->local);
1949 if (r < 0)
1950 return table_log_add_error(r);
1951 }
1952
94876904 1953 if (in_addr_is_set(AF_INET, &info->remote)) {
dca35224
SS
1954 r = table_add_many(table,
1955 TABLE_EMPTY,
1956 TABLE_STRING, "Remote:",
1957 TABLE_IN_ADDR, &info->remote);
1958 if (r < 0)
1959 return table_log_add_error(r);
1960 }
5712d689 1961 } else if (STRPTR_IN_SET(info->netdev_kind, "ip6gre", "ip6gretap", "ip6erspan", "vti6")) {
94876904 1962 if (in_addr_is_set(AF_INET6, &info->local)) {
ad760bc1
SS
1963 r = table_add_many(table,
1964 TABLE_EMPTY,
1965 TABLE_STRING, "Local:",
1966 TABLE_IN6_ADDR, &info->local);
1967 if (r < 0)
1968 return table_log_add_error(r);
1969 }
1970
94876904 1971 if (in_addr_is_set(AF_INET6, &info->remote)) {
ad760bc1
SS
1972 r = table_add_many(table,
1973 TABLE_EMPTY,
1974 TABLE_STRING, "Remote:",
1975 TABLE_IN6_ADDR, &info->remote);
1976 if (r < 0)
1977 return table_log_add_error(r);
1978 }
4e1a1991
SS
1979 } else if (streq_ptr(info->netdev_kind, "geneve")) {
1980 r = table_add_many(table,
1981 TABLE_EMPTY,
1982 TABLE_STRING, "VNI:",
1983 TABLE_UINT32, info->vni);
1984 if (r < 0)
1985 return table_log_add_error(r);
1986
94876904 1987 if (info->has_tunnel_ipv4 && in_addr_is_set(AF_INET, &info->remote)) {
4e1a1991
SS
1988 r = table_add_many(table,
1989 TABLE_EMPTY,
1990 TABLE_STRING, "Remote:",
1991 TABLE_IN_ADDR, &info->remote);
1992 if (r < 0)
1993 return table_log_add_error(r);
94876904 1994 } else if (in_addr_is_set(AF_INET6, &info->remote)) {
4e1a1991
SS
1995 r = table_add_many(table,
1996 TABLE_EMPTY,
1997 TABLE_STRING, "Remote:",
1998 TABLE_IN6_ADDR, &info->remote);
1999 if (r < 0)
2000 return table_log_add_error(r);
2001 }
2002
2003 if (info->ttl > 0) {
2004 r = table_add_many(table,
2005 TABLE_EMPTY,
2006 TABLE_STRING, "TTL:",
2007 TABLE_UINT8, info->ttl);
2008 if (r < 0)
2009 return table_log_add_error(r);
2010 }
2011
2012 if (info->tos > 0) {
2013 r = table_add_many(table,
2014 TABLE_EMPTY,
2015 TABLE_STRING, "TOS:",
2016 TABLE_UINT8, info->tos);
2017 if (r < 0)
2018 return table_log_add_error(r);
2019 }
2020
2021 r = table_add_many(table,
2022 TABLE_EMPTY,
2023 TABLE_STRING, "Port:",
2024 TABLE_UINT16, info->tunnel_port);
2025 if (r < 0)
2026 return table_log_add_error(r);
e7b38d7d
SS
2027
2028 r = table_add_many(table,
2029 TABLE_EMPTY,
2030 TABLE_STRING, "Inherit:",
2031 TABLE_STRING, geneve_df_to_string(info->inherit));
2032 if (r < 0)
2033 return table_log_add_error(r);
2034
2035 if (info->df > 0) {
2036 r = table_add_many(table,
2037 TABLE_EMPTY,
2038 TABLE_STRING, "IPDoNotFragment:",
2039 TABLE_UINT8, info->df);
2040 if (r < 0)
2041 return table_log_add_error(r);
2042 }
2043
2044 r = table_add_many(table,
2045 TABLE_EMPTY,
2046 TABLE_STRING, "UDPChecksum:",
2047 TABLE_BOOLEAN, info->csum);
2048 if (r < 0)
2049 return table_log_add_error(r);
2050
2051 r = table_add_many(table,
2052 TABLE_EMPTY,
2053 TABLE_STRING, "UDP6ZeroChecksumTx:",
2054 TABLE_BOOLEAN, info->csum6_tx);
2055 if (r < 0)
2056 return table_log_add_error(r);
2057
2058 r = table_add_many(table,
2059 TABLE_EMPTY,
2060 TABLE_STRING, "UDP6ZeroChecksumRx:",
2061 TABLE_BOOLEAN, info->csum6_rx);
2062 if (r < 0)
2063 return table_log_add_error(r);
2064
2065 if (info->label > 0) {
2066 r = table_add_many(table,
2067 TABLE_EMPTY,
2068 TABLE_STRING, "FlowLabel:",
2069 TABLE_UINT32, info->label);
2070 if (r < 0)
2071 return table_log_add_error(r);
2072 }
cf217a09
SS
2073 } else if (STRPTR_IN_SET(info->netdev_kind, "macvlan", "macvtap")) {
2074 r = table_add_many(table,
2075 TABLE_EMPTY,
2076 TABLE_STRING, "Mode:",
2077 TABLE_STRING, macvlan_mode_to_string(info->macvlan_mode));
2078 if (r < 0)
2079 return table_log_add_error(r);
851ef1ed
SS
2080 } else if (streq_ptr(info->netdev_kind, "ipvlan")) {
2081 _cleanup_free_ char *p = NULL, *s = NULL;
2082
2083 if (info->ipvlan_flags & IPVLAN_F_PRIVATE)
2084 p = strdup("private");
2085 else if (info->ipvlan_flags & IPVLAN_F_VEPA)
2086 p = strdup("vepa");
2087 else
2088 p = strdup("bridge");
2089 if (!p)
2090 log_oom();
2091
2092 s = strjoin(ipvlan_mode_to_string(info->ipvlan_mode), " (", p, ")");
2093 if (!s)
2094 return log_oom();
2095
2096 r = table_add_many(table,
2097 TABLE_EMPTY,
2098 TABLE_STRING, "Mode:",
2099 TABLE_STRING, s);
2100 if (r < 0)
2101 return table_log_add_error(r);
c82d1bf2
SS
2102 }
2103
8d07de25
ZJS
2104 if (info->has_wlan_link_info) {
2105 _cleanup_free_ char *esc = NULL;
8d07de25
ZJS
2106
2107 r = table_add_many(table,
2108 TABLE_EMPTY,
2109 TABLE_STRING, "WiFi access point:");
2110 if (r < 0)
bd17fa8c 2111 return table_log_add_error(r);
8d07de25
ZJS
2112
2113 if (info->ssid)
2114 esc = cescape(info->ssid);
2115
2116 r = table_add_cell_stringf(table, NULL, "%s (%s)",
2117 strnull(esc),
4b574fd8 2118 ETHER_ADDR_TO_STR(&info->bssid));
8d07de25 2119 if (r < 0)
bd17fa8c 2120 return table_log_add_error(r);
8d07de25
ZJS
2121 }
2122
335dd8ba 2123 if (info->has_bitrates) {
8d0e0af2
YW
2124 r = table_add_many(table,
2125 TABLE_EMPTY,
2126 TABLE_STRING, "Bit Rate (Tx/Rx):");
335dd8ba 2127 if (r < 0)
bd17fa8c 2128 return table_log_add_error(r);
42a63431 2129 r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
46e23f9a
ZJS
2130 FORMAT_BYTES_FULL(info->tx_bitrate, 0),
2131 FORMAT_BYTES_FULL(info->rx_bitrate, 0));
335dd8ba 2132 if (r < 0)
bd17fa8c 2133 return table_log_add_error(r);
335dd8ba
YW
2134 }
2135
98d5bef3 2136 if (info->has_tx_queues || info->has_rx_queues) {
8d0e0af2
YW
2137 r = table_add_many(table,
2138 TABLE_EMPTY,
2139 TABLE_STRING, "Queue Length (Tx/Rx):");
98d5bef3 2140 if (r < 0)
bd17fa8c 2141 return table_log_add_error(r);
98d5bef3
YW
2142 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "/%" PRIu32, info->tx_queues, info->rx_queues);
2143 if (r < 0)
bd17fa8c 2144 return table_log_add_error(r);
98d5bef3 2145 }
0d4ad91d 2146
c967d2c7
YW
2147 if (info->has_ethtool_link_info) {
2148 const char *duplex = duplex_to_string(info->duplex);
2149 const char *port = port_to_string(info->port);
2150
2151 if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
8d0e0af2
YW
2152 r = table_add_many(table,
2153 TABLE_EMPTY,
2154 TABLE_STRING, "Auto negotiation:",
2155 TABLE_BOOLEAN, info->autonegotiation == AUTONEG_ENABLE);
c967d2c7 2156 if (r < 0)
bd17fa8c 2157 return table_log_add_error(r);
c967d2c7
YW
2158 }
2159
2160 if (info->speed > 0) {
8d0e0af2
YW
2161 r = table_add_many(table,
2162 TABLE_EMPTY,
2163 TABLE_STRING, "Speed:",
50299121 2164 TABLE_BPS, info->speed);
c967d2c7 2165 if (r < 0)
bd17fa8c 2166 return table_log_add_error(r);
c967d2c7
YW
2167 }
2168
2169 if (duplex) {
8d0e0af2
YW
2170 r = table_add_many(table,
2171 TABLE_EMPTY,
2172 TABLE_STRING, "Duplex:",
2173 TABLE_STRING, duplex);
c967d2c7 2174 if (r < 0)
bd17fa8c 2175 return table_log_add_error(r);
c967d2c7
YW
2176 }
2177
2178 if (port) {
8d0e0af2
YW
2179 r = table_add_many(table,
2180 TABLE_EMPTY,
2181 TABLE_STRING, "Port:",
2182 TABLE_STRING, port);
c967d2c7 2183 if (r < 0)
bd17fa8c 2184 return table_log_add_error(r);
c967d2c7
YW
2185 }
2186 }
2187
d41fa6ee 2188 r = dump_addresses(rtnl, lease, table, info->ifindex);
98d5bef3
YW
2189 if (r < 0)
2190 return r;
2191 r = dump_gateways(rtnl, hwdb, table, info->ifindex);
2192 if (r < 0)
2193 return r;
2194 r = dump_list(table, "DNS:", dns);
2195 if (r < 0)
2196 return r;
2197 r = dump_list(table, "Search Domains:", search_domains);
2198 if (r < 0)
2199 return r;
2200 r = dump_list(table, "Route Domains:", route_domains);
2201 if (r < 0)
2202 return r;
2203 r = dump_list(table, "NTP:", ntp);
1f807af6
SS
2204 if (r < 0)
2205 return r;
2206 r = dump_list(table, "SIP:", sip);
98d5bef3
YW
2207 if (r < 0)
2208 return r;
2209 r = dump_ifindexes(table, "Carrier Bound To:", carrier_bound_to);
2210 if (r < 0)
2211 return r;
2212 r = dump_ifindexes(table, "Carrier Bound By:", carrier_bound_by);
2213 if (r < 0)
2214 return r;
9085f64a 2215
a853652a
DS
2216 r = sd_network_link_get_activation_policy(info->ifindex, &activation_policy);
2217 if (r >= 0) {
2218 r = table_add_many(table,
2219 TABLE_EMPTY,
2220 TABLE_STRING, "Activation Policy:",
2221 TABLE_STRING, activation_policy);
2222 if (r < 0)
2223 return table_log_add_error(r);
2224 }
2225
003015af
DS
2226 r = sd_network_link_get_required_for_online(info->ifindex);
2227 if (r >= 0) {
2228 r = table_add_many(table,
2229 TABLE_EMPTY,
2230 TABLE_STRING, "Required For Online:",
2231 TABLE_BOOLEAN, r);
2232 if (r < 0)
2233 return table_log_add_error(r);
2234 }
2235
35cab5f9 2236 if (lease) {
862e7108
YW
2237 const void *client_id;
2238 size_t client_id_len;
35cab5f9
YW
2239 const char *tz;
2240
2241 r = sd_dhcp_lease_get_timezone(lease, &tz);
2242 if (r >= 0) {
2243 r = table_add_many(table,
2244 TABLE_EMPTY,
2245 TABLE_STRING, "Time Zone:",
2246 TABLE_STRING, tz);
2247 if (r < 0)
2248 return table_log_add_error(r);
2249 }
8eb9058d 2250
862e7108
YW
2251 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
2252 if (r >= 0) {
2253 _cleanup_free_ char *id = NULL;
2254
2255 r = sd_dhcp_client_id_to_string(client_id, client_id_len, &id);
2256 if (r >= 0) {
2257 r = table_add_many(table,
2258 TABLE_EMPTY,
2259 TABLE_STRING, "DHCP4 Client ID:",
2260 TABLE_STRING, id);
2261 if (r < 0)
2262 return table_log_add_error(r);
2263 }
2264 }
2153bbc8
SS
2265 }
2266
331ee15f
SS
2267 r = sd_network_link_get_dhcp6_client_iaid_string(info->ifindex, &iaid);
2268 if (r >= 0) {
2269 r = table_add_many(table,
2270 TABLE_EMPTY,
2271 TABLE_STRING, "DHCP6 Client IAID:",
2272 TABLE_STRING, iaid);
2273 if (r < 0)
2274 return table_log_add_error(r);
2275 }
2276
63911885
SS
2277 r = sd_network_link_get_dhcp6_client_duid_string(info->ifindex, &duid);
2278 if (r >= 0) {
2279 r = table_add_many(table,
2280 TABLE_EMPTY,
2281 TABLE_STRING, "DHCP6 Client DUID:",
2282 TABLE_STRING, duid);
2283 if (r < 0)
2284 return table_log_add_error(r);
2285 }
2286
98d5bef3
YW
2287 r = dump_lldp_neighbors(table, "Connected To:", info->ifindex);
2288 if (r < 0)
2289 return r;
837f57da 2290
d9ce1c24
MAL
2291 r = dump_dhcp_leases(table, "Offered DHCP leases:", bus, info);
2292 if (r < 0)
2293 return r;
2294
a459b24f
YW
2295 r = dump_statistics(table, info);
2296 if (r < 0)
2297 return r;
2298
10c71c36
YW
2299 r = table_print(table, NULL);
2300 if (r < 0)
4b6607d9 2301 return table_log_print_error(r);
10c71c36
YW
2302
2303 return show_logs(info);
9085f64a
LP
2304}
2305
0070333f 2306static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
4a481ec4 2307 _cleanup_free_ char *operational_state = NULL, *online_state = NULL;
0070333f 2308 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
4a481ec4 2309 const char *on_color_operational, *on_color_online;
98d5bef3
YW
2310 _cleanup_(table_unrefp) Table *table = NULL;
2311 TableCell *cell;
2312 int r;
0070333f
LP
2313
2314 assert(rtnl);
2315
4abd866d 2316 (void) sd_network_get_operational_state(&operational_state);
37da8bca 2317 operational_state_to_color(NULL, operational_state, &on_color_operational, NULL);
0070333f 2318
4a481ec4
2319 (void) sd_network_get_online_state(&online_state);
2320 online_state_to_color(online_state, &on_color_online, NULL);
2321
4252171a 2322 table = table_new("dot", "key", "value");
98d5bef3 2323 if (!table)
bd17fa8c 2324 return log_oom();
98d5bef3 2325
a42d9490
YW
2326 if (arg_full)
2327 table_set_width(table, 0);
2328
81914d9f
YW
2329 assert_se(cell = table_get_cell(table, 0, 0));
2330 (void) table_set_ellipsize_percent(table, cell, 100);
2331
2332 assert_se(cell = table_get_cell(table, 0, 1));
2333 (void) table_set_align_percent(table, cell, 100);
2334 (void) table_set_ellipsize_percent(table, cell, 100);
2335
98d5bef3
YW
2336 table_set_header(table, false);
2337
8d0e0af2
YW
2338 r = table_add_many(table,
2339 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
2340 TABLE_SET_COLOR, on_color_operational,
2341 TABLE_STRING, "State:",
2342 TABLE_STRING, strna(operational_state),
4a481ec4
2343 TABLE_SET_COLOR, on_color_operational,
2344 TABLE_EMPTY,
2345 TABLE_STRING, "Online state:",
2346 TABLE_STRING, online_state ?: "unknown",
2347 TABLE_SET_COLOR, on_color_online);
bd17fa8c
YW
2348 if (r < 0)
2349 return table_log_add_error(r);
0070333f 2350
d41fa6ee 2351 r = dump_addresses(rtnl, NULL, table, 0);
98d5bef3
YW
2352 if (r < 0)
2353 return r;
2354 r = dump_gateways(rtnl, hwdb, table, 0);
2355 if (r < 0)
2356 return r;
0070333f 2357
4abd866d 2358 (void) sd_network_get_dns(&dns);
98d5bef3
YW
2359 r = dump_list(table, "DNS:", dns);
2360 if (r < 0)
2361 return r;
0070333f 2362
4abd866d 2363 (void) sd_network_get_search_domains(&search_domains);
98d5bef3
YW
2364 r = dump_list(table, "Search Domains:", search_domains);
2365 if (r < 0)
2366 return r;
0070333f 2367
4abd866d 2368 (void) sd_network_get_route_domains(&route_domains);
98d5bef3
YW
2369 r = dump_list(table, "Route Domains:", route_domains);
2370 if (r < 0)
2371 return r;
0070333f 2372
4abd866d 2373 (void) sd_network_get_ntp(&ntp);
98d5bef3
YW
2374 r = dump_list(table, "NTP:", ntp);
2375 if (r < 0)
2376 return r;
0070333f 2377
10c71c36
YW
2378 r = table_print(table, NULL);
2379 if (r < 0)
4b6607d9 2380 return table_log_print_error(r);
10c71c36
YW
2381
2382 return show_logs(NULL);
0070333f
LP
2383}
2384
266b5389 2385static int link_status(int argc, char *argv[], void *userdata) {
335dd8ba 2386 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 2387 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b147503e 2388 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
172353b1 2389 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
b48e277f 2390 int r, c;
ee8c4568 2391
bfc2b05e
YW
2392 if (arg_json_format_flags != JSON_FORMAT_OFF) {
2393 if (arg_all || argc <= 1)
2394 return dump_manager_description();
2395 else
2396 return dump_link_description(strv_skip(argv, 1));
2397 }
2398
384c2c32 2399 pager_open(arg_pager_flags);
0070333f 2400
335dd8ba
YW
2401 r = sd_bus_open_system(&bus);
2402 if (r < 0)
2403 return log_error_errno(r, "Failed to connect system bus: %m");
2404
1c4baffc 2405 r = sd_netlink_open(&rtnl);
f647962d
MS
2406 if (r < 0)
2407 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 2408
81fd1dd3
TG
2409 r = sd_hwdb_new(&hwdb);
2410 if (r < 0)
2411 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 2412
b147503e 2413 if (arg_all)
335dd8ba 2414 c = acquire_link_info(bus, rtnl, NULL, &links);
b147503e 2415 else if (argc <= 1)
0070333f 2416 return system_status(rtnl, hwdb);
b147503e 2417 else
335dd8ba 2418 c = acquire_link_info(bus, rtnl, argv + 1, &links);
b147503e
LP
2419 if (c < 0)
2420 return c;
ee8c4568 2421
b48e277f 2422 for (int i = 0; i < c; i++) {
b147503e
LP
2423 if (i > 0)
2424 fputc('\n', stdout);
ee8c4568 2425
d9ce1c24 2426 link_status_one(bus, rtnl, hwdb, links + i);
ee8c4568
LP
2427 }
2428
2429 return 0;
2430}
2431
34437b4f
LP
2432static char *lldp_capabilities_to_string(uint16_t x) {
2433 static const char characters[] = {
2434 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
2435 };
2436 char *ret;
2437 unsigned i;
49699bac 2438
34437b4f
LP
2439 ret = new(char, ELEMENTSOF(characters) + 1);
2440 if (!ret)
49699bac
SS
2441 return NULL;
2442
34437b4f
LP
2443 for (i = 0; i < ELEMENTSOF(characters); i++)
2444 ret[i] = (x & (1U << i)) ? characters[i] : '.';
49699bac 2445
34437b4f
LP
2446 ret[i] = 0;
2447 return ret;
49699bac
SS
2448}
2449
4c3160f1 2450static void lldp_capabilities_legend(uint16_t x) {
b48e277f 2451 unsigned cols = columns();
404d53a9 2452 static const char* const table[] = {
4c3160f1
ZJS
2453 "o - Other",
2454 "p - Repeater",
2455 "b - Bridge",
2456 "w - WLAN Access Point",
2457 "r - Router",
2458 "t - Telephone",
2459 "d - DOCSIS cable device",
2460 "a - Station",
2461 "c - Customer VLAN",
2462 "s - Service VLAN",
2463 "m - Two-port MAC Relay (TPMR)",
2464 };
2465
2466 if (x == 0)
2467 return;
2468
2469 printf("\nCapability Flags:\n");
b48e277f 2470 for (unsigned w = 0, i = 0; i < ELEMENTSOF(table); i++)
4c3160f1
ZJS
2471 if (x & (1U << i) || arg_all) {
2472 bool newline;
2473
2474 newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols;
2475 if (newline)
2476 w = 0;
2477 w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]);
2478 }
2479 puts("");
2480}
2481
49699bac 2482static int link_lldp_status(int argc, char *argv[], void *userdata) {
b147503e 2483 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
172353b1 2484 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
658e9106 2485 _cleanup_(table_unrefp) Table *table = NULL;
b48e277f 2486 int r, c, m = 0;
4c3160f1 2487 uint16_t all = 0;
658e9106 2488 TableCell *cell;
b147503e
LP
2489
2490 r = sd_netlink_open(&rtnl);
2491 if (r < 0)
2492 return log_error_errno(r, "Failed to connect to netlink: %m");
49699bac 2493
335dd8ba 2494 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
49699bac 2495 if (c < 0)
7d367b45
LP
2496 return c;
2497
384c2c32 2498 pager_open(arg_pager_flags);
49699bac 2499
4252171a
ZJS
2500 table = table_new("link",
2501 "chassis id",
2502 "system name",
2503 "caps",
2504 "port id",
2505 "port description");
658e9106 2506 if (!table)
bd17fa8c 2507 return log_oom();
658e9106 2508
a42d9490
YW
2509 if (arg_full)
2510 table_set_width(table, 0);
2511
658e9106
YW
2512 table_set_header(table, arg_legend);
2513
2514 assert_se(cell = table_get_cell(table, 0, 0));
2515 table_set_minimum_width(table, cell, 16);
2516
2517 assert_se(cell = table_get_cell(table, 0, 1));
2518 table_set_minimum_width(table, cell, 17);
2519
2520 assert_se(cell = table_get_cell(table, 0, 2));
2521 table_set_minimum_width(table, cell, 16);
2522
2523 assert_se(cell = table_get_cell(table, 0, 3));
2524 table_set_minimum_width(table, cell, 11);
2525
2526 assert_se(cell = table_get_cell(table, 0, 4));
2527 table_set_minimum_width(table, cell, 17);
2528
2529 assert_se(cell = table_get_cell(table, 0, 5));
2530 table_set_minimum_width(table, cell, 16);
49699bac 2531
b48e277f 2532 for (int i = 0; i < c; i++) {
34437b4f 2533 _cleanup_fclose_ FILE *f = NULL;
49699bac 2534
837f57da
LP
2535 r = open_lldp_neighbors(links[i].ifindex, &f);
2536 if (r == -ENOENT)
2537 continue;
2538 if (r < 0) {
2539 log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
34437b4f
LP
2540 continue;
2541 }
49699bac 2542
34437b4f 2543 for (;;) {
e2835111
YW
2544 _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL, *capabilities = NULL;
2545 const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
34437b4f 2546 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
34437b4f 2547 uint16_t cc;
49699bac 2548
837f57da 2549 r = next_lldp_neighbor(f, &n);
34437b4f 2550 if (r < 0) {
837f57da 2551 log_warning_errno(r, "Failed to read neighbor data: %m");
34437b4f 2552 break;
49699bac 2553 }
837f57da
LP
2554 if (r == 0)
2555 break;
34437b4f
LP
2556
2557 (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
2558 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
2559 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
2560 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
2561
d08191a2
LP
2562 if (chassis_id) {
2563 cid = ellipsize(chassis_id, 17, 100);
2564 if (cid)
2565 chassis_id = cid;
2566 }
2567
2568 if (port_id) {
2569 pid = ellipsize(port_id, 17, 100);
2570 if (pid)
2571 port_id = pid;
2572 }
2573
2574 if (system_name) {
2575 sname = ellipsize(system_name, 16, 100);
2576 if (sname)
2577 system_name = sname;
2578 }
2579
2580 if (port_description) {
2581 pdesc = ellipsize(port_description, 16, 100);
2582 if (pdesc)
2583 port_description = pdesc;
2584 }
2585
4c3160f1 2586 if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
34437b4f 2587 capabilities = lldp_capabilities_to_string(cc);
4c3160f1
ZJS
2588 all |= cc;
2589 }
34437b4f 2590
658e9106
YW
2591 r = table_add_many(table,
2592 TABLE_STRING, links[i].name,
2593 TABLE_STRING, strna(chassis_id),
2594 TABLE_STRING, strna(system_name),
2595 TABLE_STRING, strna(capabilities),
2596 TABLE_STRING, strna(port_id),
2597 TABLE_STRING, strna(port_description));
2598 if (r < 0)
bd17fa8c 2599 return table_log_add_error(r);
b147503e
LP
2600
2601 m++;
49699bac
SS
2602 }
2603 }
2604
658e9106
YW
2605 r = table_print(table, NULL);
2606 if (r < 0)
4b6607d9 2607 return table_log_print_error(r);
658e9106 2608
4c3160f1
ZJS
2609 if (arg_legend) {
2610 lldp_capabilities_legend(all);
2611 printf("\n%i neighbors listed.\n", m);
2612 }
49699bac
SS
2613
2614 return 0;
2615}
2616
9cd8c766
SS
2617static int link_delete_send_message(sd_netlink *rtnl, int index) {
2618 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
2619 int r;
2620
2621 assert(rtnl);
2622
2623 r = sd_rtnl_message_new_link(rtnl, &req, RTM_DELLINK, index);
2624 if (r < 0)
2625 return rtnl_log_create_error(r);
2626
2627 r = sd_netlink_call(rtnl, req, 0, NULL);
2628 if (r < 0)
2629 return r;
2630
2631 return 0;
2632}
2633
c30ffcee
SS
2634static int link_up_down_send_message(sd_netlink *rtnl, char *command, int index) {
2635 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
2636 int r;
2637
2638 assert(rtnl);
2639
2640 r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, index);
2641 if (r < 0)
2642 return rtnl_log_create_error(r);
2643
2644 if (streq(command, "up"))
2645 r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
2646 else
2647 r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP);
2648 if (r < 0)
2649 return log_error_errno(r, "Could not set link flags: %m");
2650
2651 r = sd_netlink_call(rtnl, req, 0, NULL);
2652 if (r < 0)
2653 return r;
2654
2655 return 0;
2656}
2657
2658static int link_up_down(int argc, char *argv[], void *userdata) {
2659 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
2660 _cleanup_set_free_ Set *indexes = NULL;
b48e277f 2661 int index, r;
c30ffcee
SS
2662 void *p;
2663
2664 r = sd_netlink_open(&rtnl);
2665 if (r < 0)
2666 return log_error_errno(r, "Failed to connect to netlink: %m");
2667
2668 indexes = set_new(NULL);
2669 if (!indexes)
2670 return log_oom();
2671
b48e277f 2672 for (int i = 1; i < argc; i++) {
f6e49154 2673 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
c30ffcee
SS
2674 if (index < 0)
2675 return index;
2676
2677 r = set_put(indexes, INT_TO_PTR(index));
2678 if (r < 0)
2679 return log_oom();
2680 }
2681
90e74a66 2682 SET_FOREACH(p, indexes) {
c30ffcee
SS
2683 index = PTR_TO_INT(p);
2684 r = link_up_down_send_message(rtnl, argv[0], index);
01afd0f7 2685 if (r < 0)
6f50c94d 2686 return log_error_errno(r, "Failed to bring %s interface %s: %m",
01afd0f7 2687 argv[0], FORMAT_IFNAME_FULL(index, FORMAT_IFNAME_IFINDEX));
c30ffcee
SS
2688 }
2689
2690 return r;
2691}
2692
9cd8c766
SS
2693static int link_delete(int argc, char *argv[], void *userdata) {
2694 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
2695 _cleanup_set_free_ Set *indexes = NULL;
b48e277f 2696 int index, r;
38b9af61 2697 void *p;
9cd8c766
SS
2698
2699 r = sd_netlink_open(&rtnl);
2700 if (r < 0)
2701 return log_error_errno(r, "Failed to connect to netlink: %m");
2702
2703 indexes = set_new(NULL);
2704 if (!indexes)
2705 return log_oom();
2706
b48e277f 2707 for (int i = 1; i < argc; i++) {
f6e49154 2708 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2709 if (index < 0)
2710 return index;
9cd8c766
SS
2711
2712 r = set_put(indexes, INT_TO_PTR(index));
2713 if (r < 0)
2714 return log_oom();
2715 }
2716
90e74a66 2717 SET_FOREACH(p, indexes) {
d56d6cb8
YW
2718 index = PTR_TO_INT(p);
2719 r = link_delete_send_message(rtnl, index);
01afd0f7 2720 if (r < 0)
e4857ee2 2721 return log_error_errno(r, "Failed to delete interface %s: %m",
01afd0f7 2722 FORMAT_IFNAME_FULL(index, FORMAT_IFNAME_IFINDEX));
9cd8c766
SS
2723 }
2724
2725 return r;
2726}
2727
308e7dfd
YW
2728static int link_renew_one(sd_bus *bus, int index, const char *name) {
2729 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2730 int r;
2731
8a048c8c 2732 r = bus_call_method(bus, bus_network_mgr, "RenewLink", &error, NULL, "i", index);
308e7dfd
YW
2733 if (r < 0)
2734 return log_error_errno(r, "Failed to renew dynamic configuration of interface %s: %s",
2735 name, bus_error_message(&error, r));
2736
2737 return 0;
2738}
2739
2740static int link_renew(int argc, char *argv[], void *userdata) {
2741 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f7581ed6 2742 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b48e277f 2743 int index, k = 0, r;
308e7dfd
YW
2744
2745 r = sd_bus_open_system(&bus);
2746 if (r < 0)
2747 return log_error_errno(r, "Failed to connect system bus: %m");
2748
b48e277f 2749 for (int i = 1; i < argc; i++) {
f6e49154 2750 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2751 if (index < 0)
2752 return index;
308e7dfd
YW
2753
2754 r = link_renew_one(bus, index, argv[i]);
2755 if (r < 0 && k >= 0)
2756 k = r;
2757 }
2758
2759 return k;
2760}
2761
3efdd6af
SS
2762static int link_force_renew_one(sd_bus *bus, int index, const char *name) {
2763 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2764 int r;
2765
8a048c8c 2766 r = bus_call_method(bus, bus_network_mgr, "ForceRenewLink", &error, NULL, "i", index);
3efdd6af
SS
2767 if (r < 0)
2768 return log_error_errno(r, "Failed to force renew dynamic configuration of interface %s: %s",
2769 name, bus_error_message(&error, r));
2770
2771 return 0;
2772}
2773
2774static int link_force_renew(int argc, char *argv[], void *userdata) {
2775 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2776 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b48e277f 2777 int k = 0, r;
3efdd6af
SS
2778
2779 r = sd_bus_open_system(&bus);
2780 if (r < 0)
2781 return log_error_errno(r, "Failed to connect system bus: %m");
2782
b48e277f 2783 for (int i = 1; i < argc; i++) {
f6e49154 2784 int index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
3efdd6af
SS
2785 if (index < 0)
2786 return index;
2787
2788 r = link_force_renew_one(bus, index, argv[i]);
2789 if (r < 0 && k >= 0)
2790 k = r;
2791 }
2792
2793 return k;
2794}
2795
a227674c
YW
2796static int verb_reload(int argc, char *argv[], void *userdata) {
2797 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2798 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2799 int r;
2800
2801 r = sd_bus_open_system(&bus);
2802 if (r < 0)
2803 return log_error_errno(r, "Failed to connect system bus: %m");
2804
8a048c8c 2805 r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
a227674c
YW
2806 if (r < 0)
2807 return log_error_errno(r, "Failed to reload network settings: %m");
2808
2809 return 0;
2810}
2811
8dc85c5e
YW
2812static int verb_reconfigure(int argc, char *argv[], void *userdata) {
2813 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2814 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f7581ed6 2815 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
8dc85c5e 2816 _cleanup_set_free_ Set *indexes = NULL;
b48e277f 2817 int index, r;
8dc85c5e
YW
2818 void *p;
2819
2820 r = sd_bus_open_system(&bus);
2821 if (r < 0)
2822 return log_error_errno(r, "Failed to connect system bus: %m");
2823
2824 indexes = set_new(NULL);
2825 if (!indexes)
2826 return log_oom();
2827
b48e277f 2828 for (int i = 1; i < argc; i++) {
f6e49154 2829 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2830 if (index < 0)
2831 return index;
8dc85c5e
YW
2832
2833 r = set_put(indexes, INT_TO_PTR(index));
2834 if (r < 0)
2835 return log_oom();
2836 }
2837
90e74a66 2838 SET_FOREACH(p, indexes) {
8dc85c5e 2839 index = PTR_TO_INT(p);
8a048c8c 2840 r = bus_call_method(bus, bus_network_mgr, "ReconfigureLink", &error, NULL, "i", index);
01afd0f7 2841 if (r < 0)
117caf37 2842 return log_error_errno(r, "Failed to reconfigure network interface %s: %m",
01afd0f7 2843 FORMAT_IFNAME_FULL(index, FORMAT_IFNAME_IFINDEX));
8dc85c5e
YW
2844 }
2845
2846 return 0;
2847}
2848
37ec0fdd
LP
2849static int help(void) {
2850 _cleanup_free_ char *link = NULL;
2851 int r;
2852
2853 r = terminal_urlify_man("networkctl", "1", &link);
2854 if (r < 0)
2855 return log_oom();
2856
353b2baa
LP
2857 printf("%s [OPTIONS...] COMMAND\n\n"
2858 "%sQuery and control the networking subsystem.%s\n"
a459b24f 2859 "\nCommands:\n"
8dc85c5e
YW
2860 " list [PATTERN...] List links\n"
2861 " status [PATTERN...] Show link status\n"
2862 " lldp [PATTERN...] Show LLDP neighbors\n"
2863 " label Show current address label entries in the kernel\n"
2864 " delete DEVICES... Delete virtual netdevs\n"
c30ffcee
SS
2865 " up DEVICES... Bring devices up\n"
2866 " down DEVICES... Bring devices down\n"
8dc85c5e 2867 " renew DEVICES... Renew dynamic configurations\n"
3efdd6af 2868 " forcerenew DEVICES... Trigger DHCP reconfiguration of all connected clients\n"
8dc85c5e
YW
2869 " reconfigure DEVICES... Reconfigure interfaces\n"
2870 " reload Reload .network and .netdev files\n"
353b2baa 2871 "\nOptions:\n"
8dc85c5e
YW
2872 " -h --help Show this help\n"
2873 " --version Show package version\n"
2874 " --no-pager Do not pipe output into a pager\n"
2875 " --no-legend Do not show the headers and footers\n"
2876 " -a --all Show status for all links\n"
ba669952 2877 " -s --stats Show detailed link statistics\n"
10c71c36
YW
2878 " -l --full Do not ellipsize output\n"
2879 " -n --lines=INTEGER Number of journal entries to show\n"
bfc2b05e
YW
2880 " --json=pretty|short|off\n"
2881 " Generate JSON output\n"
bc556335
DDM
2882 "\nSee the %s for details.\n",
2883 program_invocation_short_name,
2884 ansi_highlight(),
2885 ansi_normal(),
2886 link);
37ec0fdd
LP
2887
2888 return 0;
ee8c4568
LP
2889}
2890
2891static int parse_argv(int argc, char *argv[]) {
2892
2893 enum {
2894 ARG_VERSION = 0x100,
2895 ARG_NO_PAGER,
2896 ARG_NO_LEGEND,
bfc2b05e 2897 ARG_JSON,
ee8c4568
LP
2898 };
2899
2900 static const struct option options[] = {
2901 { "help", no_argument, NULL, 'h' },
2902 { "version", no_argument, NULL, ARG_VERSION },
2903 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2904 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 2905 { "all", no_argument, NULL, 'a' },
a459b24f 2906 { "stats", no_argument, NULL, 's' },
10c71c36
YW
2907 { "full", no_argument, NULL, 'l' },
2908 { "lines", required_argument, NULL, 'n' },
bfc2b05e 2909 { "json", required_argument, NULL, ARG_JSON },
ee8c4568
LP
2910 {}
2911 };
2912
bfc2b05e 2913 int c, r;
ee8c4568
LP
2914
2915 assert(argc >= 0);
2916 assert(argv);
2917
10c71c36 2918 while ((c = getopt_long(argc, argv, "hasln:", options, NULL)) >= 0) {
ee8c4568
LP
2919
2920 switch (c) {
2921
2922 case 'h':
37ec0fdd 2923 return help();
ee8c4568
LP
2924
2925 case ARG_VERSION:
3f6fd1ba 2926 return version();
ee8c4568
LP
2927
2928 case ARG_NO_PAGER:
0221d68a 2929 arg_pager_flags |= PAGER_DISABLE;
ee8c4568
LP
2930 break;
2931
2932 case ARG_NO_LEGEND:
2933 arg_legend = false;
2934 break;
2935
9085f64a
LP
2936 case 'a':
2937 arg_all = true;
2938 break;
2939
a459b24f
YW
2940 case 's':
2941 arg_stats = true;
2942 break;
2943
10c71c36
YW
2944 case 'l':
2945 arg_full = true;
2946 break;
2947
2948 case 'n':
2949 if (safe_atou(optarg, &arg_lines) < 0)
2950 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2951 "Failed to parse lines '%s'", optarg);
2952 break;
2953
bfc2b05e
YW
2954 case ARG_JSON:
2955 r = parse_json_argument(optarg, &arg_json_format_flags);
2956 if (r <= 0)
2957 return r;
2958 break;
2959
ee8c4568
LP
2960 case '?':
2961 return -EINVAL;
2962
2963 default:
04499a70 2964 assert_not_reached();
ee8c4568
LP
2965 }
2966 }
2967
2968 return 1;
2969}
2970
2971static int networkctl_main(int argc, char *argv[]) {
15c3626e 2972 static const Verb verbs[] = {
8dc85c5e
YW
2973 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
2974 { "status", VERB_ANY, VERB_ANY, 0, link_status },
2975 { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
df696b14 2976 { "label", 1, 1, 0, list_address_labels },
8dc85c5e 2977 { "delete", 2, VERB_ANY, 0, link_delete },
c30ffcee
SS
2978 { "up", 2, VERB_ANY, 0, link_up_down },
2979 { "down", 2, VERB_ANY, 0, link_up_down },
8dc85c5e 2980 { "renew", 2, VERB_ANY, 0, link_renew },
3efdd6af 2981 { "forcerenew", 2, VERB_ANY, 0, link_force_renew },
8dc85c5e
YW
2982 { "reconfigure", 2, VERB_ANY, 0, verb_reconfigure },
2983 { "reload", 1, 1, 0, verb_reload },
266b5389 2984 {}
ee8c4568
LP
2985 };
2986
266b5389 2987 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
2988}
2989
3b085db3
LP
2990static int check_netns_match(void) {
2991 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2992 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2993 struct stat st;
2994 uint64_t id;
2995 int r;
2996
2997 r = sd_bus_open_system(&bus);
2998 if (r < 0)
2999 return log_error_errno(r, "Failed to connect system bus: %m");
3000
3001 r = sd_bus_get_property_trivial(
3002 bus,
3003 "org.freedesktop.network1",
3004 "/org/freedesktop/network1",
3005 "org.freedesktop.network1.Manager",
3006 "NamespaceId",
3007 &error,
3008 't',
3009 &id);
3010 if (r < 0) {
3011 log_debug_errno(r, "Failed to query network namespace of networkd, ignoring: %s", bus_error_message(&error, r));
3012 return 0;
3013 }
3014 if (id == 0) {
3015 log_debug("systemd-networkd.service not running in a network namespace (?), skipping netns check.");
3016 return 0;
3017 }
3018
3019 if (stat("/proc/self/ns/net", &st) < 0)
3020 return log_error_errno(r, "Failed to determine our own network namespace ID: %m");
3021
3022 if (id != st.st_ino)
3023 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
3024 "networkctl must be invoked in same network namespace as systemd-networkd.service.");
3025
3026 return 0;
3027}
3028
58fb3678
LP
3029static void warn_networkd_missing(void) {
3030
3031 if (access("/run/systemd/netif/state", F_OK) >= 0)
3032 return;
3033
3034 fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
3035}
3036
4e2ca442 3037static int run(int argc, char* argv[]) {
ee8c4568
LP
3038 int r;
3039
d2acb93d 3040 log_setup();
ee8c4568
LP
3041
3042 r = parse_argv(argc, argv);
3043 if (r <= 0)
4e2ca442 3044 return r;
ee8c4568 3045
3b085db3
LP
3046 r = check_netns_match();
3047 if (r < 0)
3048 return r;
3049
58fb3678
LP
3050 warn_networkd_missing();
3051
4e2ca442 3052 return networkctl_main(argc, argv);
ee8c4568 3053}
4e2ca442
ZJS
3054
3055DEFINE_MAIN_FUNCTION(run);