]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
meson: sort files
[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"
34437b4f 18#include "sd-lldp.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"
3f6fd1ba 45#include "netlink-util.h"
ef62949a 46#include "network-internal.h"
1929ed0e 47#include "network-util.h"
3f6fd1ba 48#include "pager.h"
bfc2b05e 49#include "parse-argument.h"
6bedfcbb 50#include "parse-util.h"
294bf0c3 51#include "pretty-print.h"
9cd8c766 52#include "set.h"
d308bb99 53#include "socket-netlink.h"
db73295a 54#include "socket-util.h"
760877e9 55#include "sort-util.h"
34437b4f 56#include "sparse-endian.h"
d054f0a4 57#include "stdio-util.h"
8b43440b 58#include "string-table.h"
8752c575 59#include "string-util.h"
3f6fd1ba 60#include "strv.h"
2388b2f4 61#include "strxcpyx.h"
288a74cc 62#include "terminal-util.h"
10c71c36 63#include "unit-def.h"
3f6fd1ba 64#include "verbs.h"
8d07de25 65#include "wifi-util.h"
ee8c4568 66
c82d1bf2
SS
67/* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
68#define NETDEV_KIND_MAX 64
69
8d07de25
ZJS
70/* use 128 kB for receive socket kernel queue, we shouldn't need more here */
71#define RCVBUF_SIZE (128*1024)
72
0221d68a 73static PagerFlags arg_pager_flags = 0;
ee8c4568 74static bool arg_legend = true;
9085f64a 75static bool arg_all = false;
a459b24f 76static bool arg_stats = false;
10c71c36
YW
77static bool arg_full = false;
78static unsigned arg_lines = 10;
bfc2b05e
YW
79static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
80
81static int get_description(JsonVariant **ret) {
82 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
83 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
84 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
85 const char *text = NULL;
86 int r;
87
88 r = sd_bus_open_system(&bus);
89 if (r < 0)
90 return log_error_errno(r, "Failed to connect system bus: %m");
91
92 r = bus_call_method(bus, bus_network_mgr, "Describe", &error, &reply, NULL);
93 if (r < 0)
94 return log_error_errno(r, "Failed to get description: %s", bus_error_message(&error, r));
95
96 r = sd_bus_message_read(reply, "s", &text);
97 if (r < 0)
98 return bus_log_parse_error(r);
99
100 r = json_parse(text, 0, ret, NULL, NULL);
101 if (r < 0)
102 return log_error_errno(r, "Failed to parse JSON: %m");
103
104 return 0;
105}
106
107static int dump_manager_description(void) {
108 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
109 int r;
110
111 r = get_description(&v);
112 if (r < 0)
113 return r;
114
115 json_variant_dump(v, arg_json_format_flags, NULL, NULL);
116 return 0;
117}
118
119static int dump_link_description(char **patterns) {
120 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
121 _cleanup_free_ bool *matched_patterns = NULL;
122 JsonVariant *i;
123 size_t c = 0;
124 int r;
125
126 r = get_description(&v);
127 if (r < 0)
128 return r;
129
130 matched_patterns = new0(bool, strv_length(patterns));
131 if (!matched_patterns)
132 return log_oom();
133
134 JSON_VARIANT_ARRAY_FOREACH(i, json_variant_by_key(v, "Interfaces")) {
135 char ifindex_str[DECIMAL_STR_MAX(intmax_t)];
136 const char *name;
137 intmax_t index;
138 size_t pos;
139
140 name = json_variant_string(json_variant_by_key(i, "Name"));
141 index = json_variant_integer(json_variant_by_key(i, "Index"));
142 xsprintf(ifindex_str, "%ji", index);
143
144 if (!strv_fnmatch_full(patterns, ifindex_str, 0, &pos) &&
145 !strv_fnmatch_full(patterns, name, 0, &pos)) {
146 bool match = false;
147 JsonVariant *a;
148
149 JSON_VARIANT_ARRAY_FOREACH(a, json_variant_by_key(i, "AlternativeNames"))
150 if (strv_fnmatch_full(patterns, json_variant_string(a), 0, &pos)) {
151 match = true;
152 break;
153 }
154
155 if (!match)
156 continue;
157 }
158
159 matched_patterns[pos] = true;
160 json_variant_dump(i, arg_json_format_flags, NULL, NULL);
161 c++;
162 }
163
164 /* Look if we matched all our arguments that are not globs. It is OK for a glob to match
165 * nothing, but not for an exact argument. */
166 for (size_t pos = 0; pos < strv_length(patterns); pos++) {
167 if (matched_patterns[pos])
168 continue;
169
170 if (string_is_glob(patterns[pos]))
171 log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
172 patterns[pos]);
173 else
174 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
175 "Interface \"%s\" not found.", patterns[pos]);
176 }
177
178 if (c == 0)
179 log_warning("No interfaces matched.");
180
181 return 0;
182}
ee8c4568 183
ceb366df 184static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
ceb366df
ZJS
185 if (STRPTR_IN_SET(state, "routable", "enslaved") ||
186 (streq_ptr(name, "lo") && streq_ptr(state, "carrier"))) {
37da8bca
YW
187 if (on)
188 *on = ansi_highlight_green();
189 if (off)
190 *off = ansi_normal();
7e5a080a 191 } else if (streq_ptr(state, "degraded")) {
37da8bca
YW
192 if (on)
193 *on = ansi_highlight_yellow();
194 if (off)
195 *off = ansi_normal();
196 } else {
197 if (on)
198 *on = "";
199 if (off)
200 *off = "";
201 }
7e5a080a
LP
202}
203
204static void setup_state_to_color(const char *state, const char **on, const char **off) {
7e5a080a 205 if (streq_ptr(state, "configured")) {
37da8bca
YW
206 if (on)
207 *on = ansi_highlight_green();
208 if (off)
209 *off = ansi_normal();
7e5a080a 210 } else if (streq_ptr(state, "configuring")) {
37da8bca
YW
211 if (on)
212 *on = ansi_highlight_yellow();
213 if (off)
214 *off = ansi_normal();
1cf03a4f 215 } else if (STRPTR_IN_SET(state, "failed", "linger")) {
37da8bca
YW
216 if (on)
217 *on = ansi_highlight_red();
218 if (off)
219 *off = ansi_normal();
220 } else {
221 if (on)
222 *on = "";
223 if (off)
224 *off = "";
225 }
7e5a080a
LP
226}
227
4a481ec4
228static void online_state_to_color(const char *state, const char **on, const char **off) {
229 if (streq_ptr(state, "online")) {
230 if (on)
231 *on = ansi_highlight_green();
232 if (off)
233 *off = ansi_normal();
234 } else if (streq_ptr(state, "partial")) {
235 if (on)
236 *on = ansi_highlight_yellow();
237 if (off)
238 *off = ansi_normal();
239 } else {
240 if (on)
241 *on = "";
242 if (off)
243 *off = "";
244 }
245}
246
b24281aa
SS
247typedef struct VxLanInfo {
248 uint32_t vni;
249 uint32_t link;
250
251 int local_family;
252 int group_family;
253
254 union in_addr_union local;
255 union in_addr_union group;
256
257 uint16_t dest_port;
258
22ae6c7d
SS
259 uint8_t proxy;
260 uint8_t learning;
22ae6c7d
SS
261 uint8_t rsc;
262 uint8_t l2miss;
263 uint8_t l3miss;
264 uint8_t tos;
265 uint8_t ttl;
b24281aa
SS
266} VxLanInfo;
267
6d0c65ff 268typedef struct LinkInfo {
e997c4b0 269 char name[IFNAMSIZ+1];
c82d1bf2 270 char netdev_kind[NETDEV_KIND_MAX];
172353b1 271 sd_device *sd_device;
6d0c65ff 272 int ifindex;
1c4a6088 273 unsigned short iftype;
ca2b7cd8 274 struct hw_addr_data hw_address;
caa8538a 275 struct ether_addr permanent_mac_address;
6cfef1b3 276 uint32_t master;
b147503e 277 uint32_t mtu;
2c73f59b
SS
278 uint32_t min_mtu;
279 uint32_t max_mtu;
0307afc6
SS
280 uint32_t tx_queues;
281 uint32_t rx_queues;
d69b62de 282 uint8_t addr_gen_mode;
e810df37 283 char *qdisc;
511070ee 284 char **alternative_names;
b147503e 285
a459b24f
YW
286 union {
287 struct rtnl_link_stats64 stats64;
288 struct rtnl_link_stats stats;
289 };
290
42a63431
YW
291 uint64_t tx_bitrate;
292 uint64_t rx_bitrate;
335dd8ba 293
c82d1bf2
SS
294 /* bridge info */
295 uint32_t forward_delay;
296 uint32_t hello_time;
297 uint32_t max_age;
298 uint32_t ageing_time;
299 uint32_t stp_state;
12ef8fb6 300 uint32_t cost;
c82d1bf2
SS
301 uint16_t priority;
302 uint8_t mcast_igmp_version;
12ef8fb6 303 uint8_t port_state;
c82d1bf2 304
b24281aa
SS
305 /* vxlan info */
306 VxLanInfo vxlan_info;
307
2b2a1ae6
SS
308 /* vlan info */
309 uint16_t vlan_id;
310
dca35224 311 /* tunnel info */
4e1a1991
SS
312 uint8_t ttl;
313 uint8_t tos;
e7b38d7d
SS
314 uint8_t inherit;
315 uint8_t df;
316 uint8_t csum;
317 uint8_t csum6_tx;
318 uint8_t csum6_rx;
4e1a1991
SS
319 uint16_t tunnel_port;
320 uint32_t vni;
e7b38d7d 321 uint32_t label;
dca35224
SS
322 union in_addr_union local;
323 union in_addr_union remote;
324
b1d6fe70
SS
325 /* bonding info */
326 uint8_t mode;
327 uint32_t miimon;
328 uint32_t updelay;
329 uint32_t downdelay;
330
cf217a09
SS
331 /* macvlan and macvtap info */
332 uint32_t macvlan_mode;
333
851ef1ed
SS
334 /* ipvlan info */
335 uint16_t ipvlan_mode;
336 uint16_t ipvlan_flags;
337
c967d2c7
YW
338 /* ethtool info */
339 int autonegotiation;
50299121 340 uint64_t speed;
c967d2c7
YW
341 Duplex duplex;
342 NetDevPort port;
343
8d07de25 344 /* wlan info */
78404d22 345 enum nl80211_iftype wlan_iftype;
8d07de25
ZJS
346 char *ssid;
347 struct ether_addr bssid;
348
b147503e 349 bool has_mac_address:1;
caa8538a 350 bool has_permanent_mac_address:1;
0307afc6
SS
351 bool has_tx_queues:1;
352 bool has_rx_queues:1;
a459b24f
YW
353 bool has_stats64:1;
354 bool has_stats:1;
335dd8ba 355 bool has_bitrates:1;
c967d2c7 356 bool has_ethtool_link_info:1;
8d07de25 357 bool has_wlan_link_info:1;
4e1a1991 358 bool has_tunnel_ipv4:1;
d69b62de 359 bool has_ipv6_address_generation_mode:1;
172353b1
ZJS
360
361 bool needs_freeing:1;
6d0c65ff
LP
362} LinkInfo;
363
93bab288
YW
364static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
365 return CMP(a->ifindex, b->ifindex);
6d0c65ff
LP
366}
367
75db809a 368static LinkInfo* link_info_array_free(LinkInfo *array) {
172353b1
ZJS
369 for (unsigned i = 0; array && array[i].needs_freeing; i++) {
370 sd_device_unref(array[i].sd_device);
8d07de25 371 free(array[i].ssid);
e810df37 372 free(array[i].qdisc);
511070ee 373 strv_free(array[i].alternative_names);
172353b1
ZJS
374 }
375
376 return mfree(array);
377}
378DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free);
379
c82d1bf2
SS
380static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
381 const char *received_kind;
382 int r;
383
384 assert(m);
385 assert(info);
386
387 r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
388 if (r < 0)
389 return r;
390
391 r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
392 if (r < 0)
393 return r;
394
395 r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
396 if (r < 0)
397 return r;
398
399 if (streq(received_kind, "bridge")) {
400 (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
401 (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
402 (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
403 (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
404 (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
12ef8fb6 405 (void) sd_netlink_message_read_u32(m, IFLA_BRPORT_COST, &info->cost);
c82d1bf2
SS
406 (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
407 (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
12ef8fb6 408 (void) sd_netlink_message_read_u8(m, IFLA_BRPORT_STATE, &info->port_state);
b1d6fe70
SS
409 } if (streq(received_kind, "bond")) {
410 (void) sd_netlink_message_read_u8(m, IFLA_BOND_MODE, &info->mode);
411 (void) sd_netlink_message_read_u32(m, IFLA_BOND_MIIMON, &info->miimon);
412 (void) sd_netlink_message_read_u32(m, IFLA_BOND_DOWNDELAY, &info->downdelay);
413 (void) sd_netlink_message_read_u32(m, IFLA_BOND_UPDELAY, &info->updelay);
b24281aa
SS
414 } else if (streq(received_kind, "vxlan")) {
415 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
416
417 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
418 if (r >= 0)
419 info->vxlan_info.group_family = AF_INET;
420 else {
421 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
422 if (r >= 0)
423 info->vxlan_info.group_family = AF_INET6;
424 }
425
426 r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
427 if (r >= 0)
428 info->vxlan_info.local_family = AF_INET;
429 else {
430 r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
431 if (r >= 0)
432 info->vxlan_info.local_family = AF_INET6;
433 }
434
435 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
436 (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
22ae6c7d
SS
437 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_PROXY, &info->vxlan_info.proxy);
438 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_LEARNING, &info->vxlan_info.learning);
439 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_RSC, &info->vxlan_info.rsc);
440 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L3MISS, &info->vxlan_info.l3miss);
441 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L2MISS, &info->vxlan_info.l2miss);
442 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TOS, &info->vxlan_info.tos);
443 (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TTL, &info->vxlan_info.ttl);
2b2a1ae6
SS
444 } else if (streq(received_kind, "vlan"))
445 (void) sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &info->vlan_id);
dca35224
SS
446 else if (STR_IN_SET(received_kind, "ipip", "sit")) {
447 (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_LOCAL, &info->local.in);
448 (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_REMOTE, &info->remote.in);
4e1a1991
SS
449 } else if (streq(received_kind, "geneve")) {
450 (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_ID, &info->vni);
451
452 r = sd_netlink_message_read_in_addr(m, IFLA_GENEVE_REMOTE, &info->remote.in);
453 if (r >= 0)
454 info->has_tunnel_ipv4 = true;
455 else
456 (void) sd_netlink_message_read_in6_addr(m, IFLA_GENEVE_REMOTE6, &info->remote.in6);
457
458 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL, &info->ttl);
e7b38d7d 459 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL_INHERIT, &info->inherit);
4e1a1991 460 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TOS, &info->tos);
e7b38d7d
SS
461 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_DF, &info->df);
462 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_CSUM, &info->csum);
463 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, &info->csum6_tx);
464 (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, &info->csum6_rx);
4e1a1991 465 (void) sd_netlink_message_read_u16(m, IFLA_GENEVE_PORT, &info->tunnel_port);
e7b38d7d 466 (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_LABEL, &info->label);
4d75ea1e
SS
467 } else if (STR_IN_SET(received_kind, "gre", "gretap", "erspan")) {
468 (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_LOCAL, &info->local.in);
469 (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_REMOTE, &info->remote.in);
ad760bc1
SS
470 } else if (STR_IN_SET(received_kind, "ip6gre", "ip6gretap", "ip6erspan")) {
471 (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_LOCAL, &info->local.in6);
472 (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_REMOTE, &info->remote.in6);
5712d689
SS
473 } else if (streq(received_kind, "vti")) {
474 (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_LOCAL, &info->local.in);
475 (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_REMOTE, &info->remote.in);
476 } else if (streq(received_kind, "vti6")) {
477 (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_LOCAL, &info->local.in6);
478 (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_REMOTE, &info->remote.in6);
cf217a09
SS
479 } else if (STR_IN_SET(received_kind, "macvlan", "macvtap"))
480 (void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode);
851ef1ed
SS
481 else if (streq(received_kind, "ipvlan")) {
482 (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_MODE, &info->ipvlan_mode);
483 (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_FLAGS, &info->ipvlan_flags);
484 }
c82d1bf2
SS
485
486 strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
487
488 (void) sd_netlink_message_exit_container(m);
489 (void) sd_netlink_message_exit_container(m);
490
491 return 0;
492}
493
0ef84b80 494static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) {
f7581ed6 495 _cleanup_strv_free_ char **altnames = NULL;
e810df37 496 const char *name, *qdisc;
a6962904 497 int ifindex, r;
c82d1bf2 498 uint16_t type;
6d0c65ff 499
e997c4b0
LP
500 assert(m);
501 assert(info);
6d0c65ff 502
e997c4b0
LP
503 r = sd_netlink_message_get_type(m, &type);
504 if (r < 0)
505 return r;
6d0c65ff 506
e997c4b0
LP
507 if (type != RTM_NEWLINK)
508 return 0;
6d0c65ff 509
a6962904 510 r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
e997c4b0
LP
511 if (r < 0)
512 return r;
6d0c65ff 513
e997c4b0
LP
514 r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
515 if (r < 0)
516 return r;
6d0c65ff 517
f7581ed6 518 r = sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
48c53989 519 if (r < 0 && r != -ENODATA)
f7581ed6
YW
520 return r;
521
a6962904
YW
522 if (patterns) {
523 char str[DECIMAL_STR_MAX(int)];
0ef84b80
ZJS
524 size_t pos;
525
526 assert(matched_patterns);
a6962904
YW
527
528 xsprintf(str, "%i", ifindex);
0ef84b80
ZJS
529 if (!strv_fnmatch_full(patterns, str, 0, &pos) &&
530 !strv_fnmatch_full(patterns, name, 0, &pos)) {
f7581ed6
YW
531 bool match = false;
532 char **p;
533
534 STRV_FOREACH(p, altnames)
0ef84b80 535 if (strv_fnmatch_full(patterns, *p, 0, &pos)) {
f7581ed6
YW
536 match = true;
537 break;
538 }
539 if (!match)
540 return 0;
541 }
0ef84b80
ZJS
542
543 matched_patterns[pos] = true;
a6962904
YW
544 }
545
b147503e 546 r = sd_rtnl_message_link_get_type(m, &info->iftype);
e997c4b0
LP
547 if (r < 0)
548 return r;
6d0c65ff 549
2388b2f4 550 strscpy(info->name, sizeof info->name, name);
a6962904 551 info->ifindex = ifindex;
f7581ed6 552 info->alternative_names = TAKE_PTR(altnames);
b147503e
LP
553
554 info->has_mac_address =
f1f17144 555 netlink_message_read_hw_addr(m, IFLA_ADDRESS, &info->hw_address) >= 0 &&
f929f18c 556 !hw_addr_is_null(&info->hw_address);
b147503e 557
caa8538a 558 info->has_permanent_mac_address =
6666c4fa 559 ethtool_get_permanent_macaddr(NULL, info->name, &info->permanent_mac_address) >= 0 &&
f929f18c 560 !ether_addr_is_null(&info->permanent_mac_address) &&
f1f17144 561 (info->hw_address.length != sizeof(struct ether_addr) ||
ca2b7cd8 562 memcmp(&info->permanent_mac_address, info->hw_address.bytes, sizeof(struct ether_addr)) != 0);
caa8538a 563
c06ff86e
YW
564 (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu);
565 (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
566 (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
2c73f59b 567
0307afc6
SS
568 info->has_rx_queues =
569 sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
570 info->rx_queues > 0;
571
572 info->has_tx_queues =
573 sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
574 info->tx_queues > 0;
575
a459b24f
YW
576 if (sd_netlink_message_read(m, IFLA_STATS64, sizeof info->stats64, &info->stats64) >= 0)
577 info->has_stats64 = true;
578 else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
579 info->has_stats = true;
580
e810df37
SS
581 r = sd_netlink_message_read_string(m, IFLA_QDISC, &qdisc);
582 if (r >= 0) {
583 info->qdisc = strdup(qdisc);
584 if (!info->qdisc)
585 return log_oom();
586 }
587
6cfef1b3
SS
588 (void) sd_netlink_message_read_u32(m, IFLA_MASTER, &info->master);
589
d69b62de
SS
590 r = sd_netlink_message_enter_container(m, IFLA_AF_SPEC);
591 if (r >= 0) {
592 r = sd_netlink_message_enter_container(m, AF_INET6);
593 if (r >= 0) {
594 r = sd_netlink_message_read_u8(m, IFLA_INET6_ADDR_GEN_MODE, &info->addr_gen_mode);
7f20a9e5
SS
595 if (r >= 0 && IN_SET(info->addr_gen_mode,
596 IN6_ADDR_GEN_MODE_EUI64,
597 IN6_ADDR_GEN_MODE_NONE,
598 IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
599 IN6_ADDR_GEN_MODE_RANDOM))
d69b62de
SS
600 info->has_ipv6_address_generation_mode = true;
601
602 (void) sd_netlink_message_exit_container(m);
603 }
604 (void) sd_netlink_message_exit_container(m);
605 }
606
c82d1bf2
SS
607 /* fill kind info */
608 (void) decode_netdev(m, info);
609
e997c4b0
LP
610 return 1;
611}
612
090c923b
MAL
613static int link_get_property(
614 sd_bus *bus,
615 const LinkInfo *link,
616 sd_bus_error *error,
617 sd_bus_message **reply,
618 const char *iface,
619 const char *propname) {
335dd8ba
YW
620 _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
621 int r;
622
623 if (asprintf(&ifindex_str, "%i", link->ifindex) < 0)
624 return -ENOMEM;
625
626 r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path);
627 if (r < 0)
628 return r;
629
090c923b 630 return sd_bus_call_method(
335dd8ba
YW
631 bus,
632 "org.freedesktop.network1",
633 path,
634 "org.freedesktop.DBus.Properties",
635 "Get",
090c923b
MAL
636 error,
637 reply,
335dd8ba 638 "ss",
090c923b
MAL
639 iface,
640 propname);
641}
642
643static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
644 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
645 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
646 int r;
647
648 r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.Link", "BitRates");
335dd8ba 649 if (r < 0) {
955a6329
ZJS
650 bool quiet = sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY,
651 BUS_ERROR_SPEED_METER_INACTIVE);
8210a61a
ZJS
652
653 return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
654 r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
335dd8ba
YW
655 }
656
42a63431 657 r = sd_bus_message_enter_container(reply, 'v', "(tt)");
335dd8ba
YW
658 if (r < 0)
659 return bus_log_parse_error(r);
660
42a63431 661 r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
335dd8ba
YW
662 if (r < 0)
663 return bus_log_parse_error(r);
664
665 r = sd_bus_message_exit_container(reply);
666 if (r < 0)
667 return bus_log_parse_error(r);
668
e813de54 669 link->has_bitrates = link->tx_bitrate != UINT64_MAX && link->rx_bitrate != UINT64_MAX;
335dd8ba
YW
670
671 return 0;
672}
673
516e9c80
ZJS
674static void acquire_ether_link_info(int *fd, LinkInfo *link) {
675 if (ethtool_get_link_info(fd, link->name,
676 &link->autonegotiation,
677 &link->speed,
678 &link->duplex,
679 &link->port) >= 0)
680 link->has_ethtool_link_info = true;
681}
682
8d07de25
ZJS
683static void acquire_wlan_link_info(LinkInfo *link) {
684 _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
685 const char *type = NULL;
78404d22 686 int r, k = 0;
8d07de25
ZJS
687
688 if (link->sd_device)
689 (void) sd_device_get_devtype(link->sd_device, &type);
690 if (!streq_ptr(type, "wlan"))
691 return;
692
693 r = sd_genl_socket_open(&genl);
694 if (r < 0) {
695 log_debug_errno(r, "Failed to open generic netlink socket: %m");
696 return;
697 }
698
699 (void) sd_netlink_inc_rcvbuf(genl, RCVBUF_SIZE);
700
78404d22 701 r = wifi_get_interface(genl, link->ifindex, &link->wlan_iftype, &link->ssid);
8d07de25
ZJS
702 if (r < 0)
703 log_debug_errno(r, "%s: failed to query ssid: %m", link->name);
704
33ebda2e 705 if (link->wlan_iftype == NL80211_IFTYPE_STATION) {
78404d22
YW
706 k = wifi_get_station(genl, link->ifindex, &link->bssid);
707 if (k < 0)
708 log_debug_errno(k, "%s: failed to query bssid: %m", link->name);
709 }
8d07de25
ZJS
710
711 link->has_wlan_link_info = r > 0 || k > 0;
712}
713
335dd8ba 714static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
4afd3348 715 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
172353b1 716 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
c967d2c7 717 _cleanup_close_ int fd = -1;
319a4f4b 718 size_t c = 0;
7d367b45
LP
719 int r;
720
b147503e 721 assert(rtnl);
7d367b45 722 assert(ret);
ee8c4568 723
ee8c4568
LP
724 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
725 if (r < 0)
726 return rtnl_log_create_error(r);
727
1c4baffc 728 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
729 if (r < 0)
730 return rtnl_log_create_error(r);
731
1c4baffc 732 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
733 if (r < 0)
734 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 735
0ef84b80
ZJS
736 _cleanup_free_ bool *matched_patterns = NULL;
737 if (patterns) {
738 matched_patterns = new0(bool, strv_length(patterns));
739 if (!matched_patterns)
740 return log_oom();
741 }
742
cda7fc8d 743 for (sd_netlink_message *i = reply; i; i = sd_netlink_message_next(i)) {
319a4f4b 744 if (!GREEDY_REALLOC0(links, c + 2)) /* We keep one trailing one as marker */
7e5a080a 745 return -ENOMEM;
7d367b45 746
0ef84b80 747 r = decode_link(i, links + c, patterns, matched_patterns);
7e5a080a
LP
748 if (r < 0)
749 return r;
c967d2c7
YW
750 if (r == 0)
751 continue;
752
172353b1
ZJS
753 links[c].needs_freeing = true;
754
0ac655a6 755 (void) sd_device_new_from_ifindex(&links[c].sd_device, links[c].ifindex);
172353b1 756
516e9c80 757 acquire_ether_link_info(&fd, &links[c]);
8d07de25 758 acquire_wlan_link_info(&links[c]);
c967d2c7
YW
759
760 c++;
7e5a080a
LP
761 }
762
0ef84b80
ZJS
763 /* Look if we matched all our arguments that are not globs. It
764 * is OK for a glob to match nothing, but not for an exact argument. */
765 for (size_t pos = 0; pos < strv_length(patterns); pos++) {
766 if (matched_patterns[pos])
767 continue;
768
769 if (string_is_glob(patterns[pos]))
770 log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
771 patterns[pos]);
772 else
773 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
774 "Interface \"%s\" not found.", patterns[pos]);
775 }
776
93bab288 777 typesafe_qsort(links, c, link_info_compare);
7e5a080a 778
335dd8ba 779 if (bus)
cda7fc8d 780 for (size_t j = 0; j < c; j++)
335dd8ba
YW
781 (void) acquire_link_bitrates(bus, links + j);
782
1cc6c93a 783 *ret = TAKE_PTR(links);
7e5a080a 784
0ef84b80
ZJS
785 if (patterns && c == 0)
786 log_warning("No interfaces matched.");
787
7e5a080a 788 return (int) c;
7d367b45 789}
ee8c4568 790
7d367b45 791static int list_links(int argc, char *argv[], void *userdata) {
b147503e 792 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
172353b1 793 _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
ff7c88a2
YW
794 _cleanup_(table_unrefp) Table *table = NULL;
795 TableCell *cell;
b48e277f 796 int c, r;
b147503e 797
bfc2b05e
YW
798 if (arg_json_format_flags != JSON_FORMAT_OFF) {
799 if (arg_all || argc <= 1)
800 return dump_manager_description();
801 else
802 return dump_link_description(strv_skip(argv, 1));
803 }
804
b147503e
LP
805 r = sd_netlink_open(&rtnl);
806 if (r < 0)
807 return log_error_errno(r, "Failed to connect to netlink: %m");
7d367b45 808
335dd8ba 809 c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
6d0c65ff 810 if (c < 0)
7d367b45
LP
811 return c;
812
0221d68a 813 (void) pager_open(arg_pager_flags);
7d367b45 814
4252171a 815 table = table_new("idx", "link", "type", "operational", "setup");
ff7c88a2
YW
816 if (!table)
817 return log_oom();
818
a42d9490
YW
819 if (arg_full)
820 table_set_width(table, 0);
821
ff7c88a2 822 table_set_header(table, arg_legend);
484f22dd
YW
823 if (table_set_empty_string(table, "n/a") < 0)
824 return log_oom();
ff7c88a2
YW
825
826 assert_se(cell = table_get_cell(table, 0, 0));
827 (void) table_set_minimum_width(table, cell, 3);
828 (void) table_set_weight(table, cell, 0);
81914d9f 829 (void) table_set_ellipsize_percent(table, cell, 100);
ff7c88a2
YW
830 (void) table_set_align_percent(table, cell, 100);
831
832 assert_se(cell = table_get_cell(table, 0, 1));
81914d9f 833 (void) table_set_ellipsize_percent(table, cell, 100);
6d0c65ff 834
b48e277f 835 for (int i = 0; i < c; i++) {
ab1525bc 836 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
37da8bca 837 const char *on_color_operational, *on_color_setup;
ee8c4568 838 _cleanup_free_ char *t = NULL;
ee8c4568 839
4abd866d 840 (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
37da8bca 841 operational_state_to_color(links[i].name, operational_state, &on_color_operational, NULL);
d57c365b 842
33d5013d
LP
843 r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
844 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
845 setup_state = strdup("unmanaged");
37da8bca 846 setup_state_to_color(setup_state, &on_color_setup, NULL);
ee8c4568 847
1a3caa49
YW
848 r = link_get_type_string(links[i].sd_device, links[i].iftype, &t);
849 if (r == -ENOMEM)
850 return log_oom();
ee8c4568 851
ff7c88a2 852 r = table_add_many(table,
8d0e0af2 853 TABLE_INT, links[i].ifindex,
ff7c88a2 854 TABLE_STRING, links[i].name,
484f22dd
YW
855 TABLE_STRING, t,
856 TABLE_STRING, operational_state,
8d0e0af2 857 TABLE_SET_COLOR, on_color_operational,
484f22dd 858 TABLE_STRING, setup_state,
8d0e0af2 859 TABLE_SET_COLOR, on_color_setup);
ff7c88a2 860 if (r < 0)
bd17fa8c 861 return table_log_add_error(r);
ee8c4568
LP
862 }
863
ff7c88a2
YW
864 r = table_print(table, NULL);
865 if (r < 0)
4b6607d9 866 return table_log_print_error(r);
ff7c88a2 867
ee8c4568 868 if (arg_legend)
6d0c65ff 869 printf("\n%i links listed.\n", c);
ee8c4568
LP
870
871 return 0;
872}
873
c09da729 874/* IEEE Organizationally Unique Identifier vendor string */
b147503e 875static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
81fd1dd3 876 const char *description;
fbd0b64f 877 char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
81fd1dd3
TG
878 int r;
879
880 assert(ret);
c09da729 881
888943fc
LP
882 if (!hwdb)
883 return -EINVAL;
884
81fd1dd3
TG
885 if (!mac)
886 return -EINVAL;
887
c09da729
TG
888 /* skip commonly misused 00:00:00 (Xerox) prefix */
889 if (memcmp(mac, "\0\0\0", 3) == 0)
890 return -EINVAL;
891
d054f0a4
DM
892 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
893 ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 894
81fd1dd3
TG
895 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
896 if (r < 0)
897 return r;
c09da729 898
81fd1dd3
TG
899 desc = strdup(description);
900 if (!desc)
901 return -ENOMEM;
c09da729 902
81fd1dd3
TG
903 *ret = desc;
904
905 return 0;
c09da729
TG
906}
907
69fb1176 908static int get_gateway_description(
1c4baffc 909 sd_netlink *rtnl,
81fd1dd3 910 sd_hwdb *hwdb,
69fb1176
LP
911 int ifindex,
912 int family,
913 union in_addr_union *gateway,
914 char **gateway_description) {
4afd3348 915 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
c09da729
TG
916 int r;
917
918 assert(rtnl);
919 assert(ifindex >= 0);
4c701096 920 assert(IN_SET(family, AF_INET, AF_INET6));
c09da729
TG
921 assert(gateway);
922 assert(gateway_description);
923
924 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
925 if (r < 0)
926 return r;
927
1c4baffc 928 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
929 if (r < 0)
930 return r;
931
1c4baffc 932 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
933 if (r < 0)
934 return r;
935
b48e277f 936 for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
67a46833
YW
937 union in_addr_union gw = IN_ADDR_NULL;
938 struct ether_addr mac = ETHER_ADDR_NULL;
c09da729
TG
939 uint16_t type;
940 int ifi, fam;
941
1c4baffc 942 r = sd_netlink_message_get_errno(m);
c09da729
TG
943 if (r < 0) {
944 log_error_errno(r, "got error: %m");
945 continue;
946 }
947
1c4baffc 948 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
949 if (r < 0) {
950 log_error_errno(r, "could not get type: %m");
951 continue;
952 }
953
954 if (type != RTM_NEWNEIGH) {
955 log_error("type is not RTM_NEWNEIGH");
956 continue;
957 }
958
959 r = sd_rtnl_message_neigh_get_family(m, &fam);
960 if (r < 0) {
961 log_error_errno(r, "could not get family: %m");
962 continue;
963 }
964
965 if (fam != family) {
966 log_error("family is not correct");
967 continue;
968 }
969
970 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
971 if (r < 0) {
144232a8 972 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
973 continue;
974 }
975
976 if (ifindex > 0 && ifi != ifindex)
977 continue;
978
979 switch (fam) {
980 case AF_INET:
1c4baffc 981 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
982 if (r < 0)
983 continue;
984
985 break;
986 case AF_INET6:
1c4baffc 987 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
988 if (r < 0)
989 continue;
990
991 break;
992 default:
993 continue;
994 }
995
996 if (!in_addr_equal(fam, &gw, gateway))
997 continue;
998
49808e0e 999 r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
c09da729
TG
1000 if (r < 0)
1001 continue;
1002
1003 r = ieee_oui(hwdb, &mac, gateway_description);
1004 if (r < 0)
1005 continue;
1006
1007 return 0;
1008 }
1009
1010 return -ENODATA;
1011}
1012
536cdd07
YW
1013static int dump_list(Table *table, const char *prefix, char * const *l) {
1014 int r;
1015
1016 if (strv_isempty(l))
1017 return 0;
1018
1019 r = table_add_many(table,
1020 TABLE_EMPTY,
1021 TABLE_STRING, prefix,
1022 TABLE_STRV, l);
1023 if (r < 0)
1024 return table_log_add_error(r);
1025
1026 return 0;
1027}
1028
69fb1176 1029static int dump_gateways(
1c4baffc 1030 sd_netlink *rtnl,
81fd1dd3 1031 sd_hwdb *hwdb,
98d5bef3 1032 Table *table,
69fb1176 1033 int ifindex) {
b6a3ca6d 1034 _cleanup_free_ struct local_address *local = NULL;
536cdd07 1035 _cleanup_strv_free_ char **buf = NULL;
cda7fc8d 1036 int r, n;
c09da729 1037
837f57da 1038 assert(rtnl);
98d5bef3 1039 assert(table);
837f57da 1040
b6a3ca6d 1041 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
536cdd07 1042 if (n <= 0)
b6a3ca6d 1043 return n;
c09da729 1044
cda7fc8d 1045 for (int i = 0; i < n; i++) {
4ff6ff9a 1046 _cleanup_free_ char *gateway = NULL, *description = NULL;
536cdd07 1047 char name[IF_NAMESIZE+1];
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
YW
1065 ifindex <= 0 ? " on " : "",
1066 ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, 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;
536cdd07 1097 char name[IF_NAMESIZE+1];
1693a943 1098
98d5bef3
YW
1099 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
1100 if (r < 0)
1101 return r;
1693a943 1102
d41fa6ee 1103 if (local[i].family == AF_INET && in4_addr_equal(&local[i].address.in, &dhcp4_address)) {
3d0c8750
SS
1104 struct in_addr server_address;
1105 char *p, s[INET_ADDRSTRLEN];
1106
1107 r = sd_dhcp_lease_get_server_identifier(lease, &server_address);
1108 if (r >= 0 && inet_ntop(AF_INET, &server_address, s, sizeof(s)))
1109 p = strjoin(pretty, " (DHCP4 via ", s, ")");
1110 else
1111 p = strjoin(pretty, " (DHCP4)");
1112 if (!p)
cdf01b36 1113 return log_oom();
3d0c8750
SS
1114
1115 free_and_replace(pretty, p);
cdf01b36
SS
1116 }
1117
536cdd07
YW
1118 r = strv_extendf(&buf, "%s%s%s",
1119 pretty,
1120 ifindex <= 0 ? " on " : "",
1121 ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
98d5bef3 1122 if (r < 0)
536cdd07 1123 return log_oom();
ee8c4568
LP
1124 }
1125
536cdd07 1126 return dump_list(table, "Address:", buf);
ee8c4568
LP
1127}
1128
d37b7627
SS
1129static int dump_address_labels(sd_netlink *rtnl) {
1130 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
0232beed 1131 _cleanup_(table_unrefp) Table *table = NULL;
0232beed 1132 TableCell *cell;
d37b7627
SS
1133 int r;
1134
1135 assert(rtnl);
1136
1137 r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
1138 if (r < 0)
1139 return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
1140
1141 r = sd_netlink_message_request_dump(req, true);
1142 if (r < 0)
1143 return r;
1144
1145 r = sd_netlink_call(rtnl, req, 0, &reply);
1146 if (r < 0)
1147 return r;
1148
4252171a 1149 table = table_new("label", "prefix/prefixlen");
0232beed 1150 if (!table)
bd17fa8c 1151 return log_oom();
0232beed 1152
a42d9490
YW
1153 if (arg_full)
1154 table_set_width(table, 0);
1155
ef1e0b9a 1156 r = table_set_sort(table, (size_t) 0);
0232beed
YW
1157 if (r < 0)
1158 return r;
1159
1160 assert_se(cell = table_get_cell(table, 0, 0));
1161 (void) table_set_align_percent(table, cell, 100);
81914d9f 1162 (void) table_set_ellipsize_percent(table, cell, 100);
0232beed
YW
1163
1164 assert_se(cell = table_get_cell(table, 0, 1));
1165 (void) table_set_align_percent(table, cell, 100);
d37b7627 1166
b48e277f 1167 for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
d37b7627 1168 _cleanup_free_ char *pretty = NULL;
67a46833 1169 union in_addr_union prefix = IN_ADDR_NULL;
d37b7627
SS
1170 uint8_t prefixlen;
1171 uint32_t label;
1172
1173 r = sd_netlink_message_get_errno(m);
1174 if (r < 0) {
1175 log_error_errno(r, "got error: %m");
1176 continue;
1177 }
1178
1179 r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
1180 if (r < 0 && r != -ENODATA) {
1181 log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
1182 continue;
1183 }
1184
1185 r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
1186 if (r < 0)
1187 continue;
1188
1189 r = in_addr_to_string(AF_INET6, &prefix, &pretty);
1190 if (r < 0)
1191 continue;
1192
1193 r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
1194 if (r < 0)
1195 continue;
1196
81914d9f 1197 r = table_add_cell(table, NULL, TABLE_UINT32, &label);
0232beed 1198 if (r < 0)
bd17fa8c 1199 return table_log_add_error(r);
0232beed 1200
8d0e0af2 1201 r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
0232beed 1202 if (r < 0)
bd17fa8c 1203 return table_log_add_error(r);
d37b7627
SS
1204 }
1205
bd17fa8c
YW
1206 r = table_print(table, NULL);
1207 if (r < 0)
4b6607d9 1208 return table_log_print_error(r);
bd17fa8c
YW
1209
1210 return 0;
d37b7627
SS
1211}
1212
1213static int list_address_labels(int argc, char *argv[], void *userdata) {
1214 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1215 int r;
1216
1217 r = sd_netlink_open(&rtnl);
1218 if (r < 0)
1219 return log_error_errno(r, "Failed to connect to netlink: %m");
1220
1221 dump_address_labels(rtnl);
1222
1223 return 0;
1224}
1225
837f57da
LP
1226static int open_lldp_neighbors(int ifindex, FILE **ret) {
1227 _cleanup_free_ char *p = NULL;
1228 FILE *f;
1229
1230 if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
1231 return -ENOMEM;
1232
1233 f = fopen(p, "re");
1234 if (!f)
1235 return -errno;
1236
1237 *ret = f;
1238 return 0;
1239}
1240
1241static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
1242 _cleanup_free_ void *raw = NULL;
1243 size_t l;
1244 le64_t u;
1245 int r;
1246
1247 assert(f);
1248 assert(ret);
1249
1250 l = fread(&u, 1, sizeof(u), f);
1251 if (l == 0 && feof(f))
1252 return 0;
1253 if (l != sizeof(u))
1254 return -EBADMSG;
1255
d23c3e4c
FB
1256 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
1257 if (le64toh(u) >= 4096)
1258 return -EBADMSG;
1259
837f57da
LP
1260 raw = new(uint8_t, le64toh(u));
1261 if (!raw)
1262 return -ENOMEM;
1263
1264 if (fread(raw, 1, le64toh(u), f) != le64toh(u))
1265 return -EBADMSG;
1266
1267 r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
1268 if (r < 0)
1269 return r;
1270
1271 return 1;
1272}
1273
98d5bef3 1274static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
536cdd07 1275 _cleanup_strv_free_ char **buf = NULL;
837f57da 1276 _cleanup_fclose_ FILE *f = NULL;
536cdd07 1277 int r;
837f57da 1278
98d5bef3 1279 assert(table);
837f57da
LP
1280 assert(prefix);
1281 assert(ifindex > 0);
1282
1283 r = open_lldp_neighbors(ifindex, &f);
98d5bef3
YW
1284 if (r == -ENOENT)
1285 return 0;
837f57da
LP
1286 if (r < 0)
1287 return r;
1288
1289 for (;;) {
1290 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
1291 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1292
1293 r = next_lldp_neighbor(f, &n);
1294 if (r < 0)
1295 return r;
1296 if (r == 0)
1297 break;
1298
837f57da
LP
1299 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1300 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1301 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1302
536cdd07
YW
1303 r = strv_extendf(&buf, "%s on port %s%s%s%s",
1304 strna(system_name),
1305 strna(port_id),
1306 isempty(port_description) ? "" : " (",
1307 strempty(port_description),
1308 isempty(port_description) ? "" : ")");
98d5bef3 1309 if (r < 0)
536cdd07 1310 return log_oom();
837f57da
LP
1311 }
1312
536cdd07 1313 return dump_list(table, prefix, buf);
837f57da
LP
1314}
1315
d9ce1c24
MAL
1316static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
1317 _cleanup_strv_free_ char **buf = NULL;
1318 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1319 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1320 int r;
1321
1322 r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases");
1323 if (r < 0) {
1324 bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
1325
1326 log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
1327 r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r));
1328 return 0;
1329 }
1330
1331 r = sd_bus_message_enter_container(reply, 'v', "a(uayayayayt)");
1332 if (r < 0)
1333 return bus_log_parse_error(r);
1334
1335 r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)");
1336 if (r < 0)
1337 return bus_log_parse_error(r);
1338
1339 while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) {
1340 _cleanup_free_ char *id = NULL, *ip = NULL;
1341 const void *client_id, *addr, *gtw, *hwaddr;
1342 size_t client_id_sz, sz;
1343 uint64_t expiration;
1344 uint32_t family;
1345
1346 r = sd_bus_message_read(reply, "u", &family);
1347 if (r < 0)
1348 return bus_log_parse_error(r);
1349
1350 r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz);
1351 if (r < 0)
1352 return bus_log_parse_error(r);
1353
1354 r = sd_bus_message_read_array(reply, 'y', &addr, &sz);
1355 if (r < 0 || sz != 4)
1356 return bus_log_parse_error(r);
1357
1358 r = sd_bus_message_read_array(reply, 'y', &gtw, &sz);
1359 if (r < 0 || sz != 4)
1360 return bus_log_parse_error(r);
1361
1362 r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz);
1363 if (r < 0)
1364 return bus_log_parse_error(r);
1365
1366 r = sd_bus_message_read_basic(reply, 't', &expiration);
1367 if (r < 0)
1368 return bus_log_parse_error(r);
1369
1370 r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id);
1371 if (r < 0)
1372 return bus_log_parse_error(r);
1373
1374 r = in_addr_to_string(family, addr, &ip);
1375 if (r < 0)
1376 return bus_log_parse_error(r);
1377
1378 r = strv_extendf(&buf, "%s (to %s)", ip, id);
1379 if (r < 0)
1380 return log_oom();
1381
1382 r = sd_bus_message_exit_container(reply);
1383 if (r < 0)
1384 return bus_log_parse_error(r);
1385 }
1386 if (r < 0)
1387 return bus_log_parse_error(r);
1388
1389 r = sd_bus_message_exit_container(reply);
1390 if (r < 0)
1391 return bus_log_parse_error(r);
1392
1393 r = sd_bus_message_exit_container(reply);
1394 if (r < 0)
1395 return bus_log_parse_error(r);
1396
1397 if (strv_isempty(buf)) {
1398 r = strv_extendf(&buf, "none");
1399 if (r < 0)
1400 return log_oom();
1401 }
1402
1403 return dump_list(table, prefix, buf);
1404}
1405
98d5bef3 1406static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
98d5bef3 1407 int r;
b295beea
LP
1408
1409 assert(prefix);
1410
1411 if (!ifindexes || ifindexes[0] <= 0)
98d5bef3 1412 return 0;
b295beea 1413
b48e277f 1414 for (unsigned c = 0; ifindexes[c] > 0; c++) {
8d0e0af2
YW
1415 r = table_add_many(table,
1416 TABLE_EMPTY,
1417 TABLE_STRING, c == 0 ? prefix : "",
1418 TABLE_IFINDEX, ifindexes[c]);
98d5bef3 1419 if (r < 0)
bd17fa8c 1420 return table_log_add_error(r);
b295beea 1421 }
98d5bef3
YW
1422
1423 return 0;
b295beea
LP
1424}
1425
a459b24f 1426#define DUMP_STATS_ONE(name, val_name) \
8d0e0af2
YW
1427 r = table_add_many(table, \
1428 TABLE_EMPTY, \
1429 TABLE_STRING, name ":"); \
a459b24f 1430 if (r < 0) \
bd17fa8c 1431 return table_log_add_error(r); \
8d0e0af2
YW
1432 r = table_add_cell(table, NULL, \
1433 info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
a459b24f
YW
1434 info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
1435 if (r < 0) \
bd17fa8c 1436 return table_log_add_error(r);
a459b24f
YW
1437
1438static int dump_statistics(Table *table, const LinkInfo *info) {
1439 int r;
1440
1441 if (!arg_stats)
1442 return 0;
1443
1444 if (!info->has_stats64 && !info->has_stats)
1445 return 0;
1446
1447 DUMP_STATS_ONE("Rx Packets", rx_packets);
1448 DUMP_STATS_ONE("Tx Packets", tx_packets);
1449 DUMP_STATS_ONE("Rx Bytes", rx_bytes);
1450 DUMP_STATS_ONE("Tx Bytes", tx_bytes);
1451 DUMP_STATS_ONE("Rx Errors", rx_errors);
1452 DUMP_STATS_ONE("Tx Errors", tx_errors);
1453 DUMP_STATS_ONE("Rx Dropped", rx_dropped);
1454 DUMP_STATS_ONE("Tx Dropped", tx_dropped);
1455 DUMP_STATS_ONE("Multicast Packets", multicast);
1456 DUMP_STATS_ONE("Collisions", collisions);
1457
1458 return 0;
1459}
1460
10c71c36
YW
1461static OutputFlags get_output_flags(void) {
1462 return
1463 arg_all * OUTPUT_SHOW_ALL |
1464 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1465 colors_enabled() * OUTPUT_COLOR;
1466}
1467
1468static int show_logs(const LinkInfo *info) {
1469 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1470 int r;
1471
1472 if (arg_lines == 0)
1473 return 0;
1474
1475 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
1476 if (r < 0)
1477 return log_error_errno(r, "Failed to open journal: %m");
1478
1479 r = add_match_this_boot(j, NULL);
1480 if (r < 0)
1481 return log_error_errno(r, "Failed to add boot matches: %m");
1482
1483 if (info) {
1484 char m1[STRLEN("_KERNEL_DEVICE=n") + DECIMAL_STR_MAX(int)];
1485 const char *m2, *m3;
1486
1487 /* kernel */
1488 xsprintf(m1, "_KERNEL_DEVICE=n%i", info->ifindex);
1489 /* networkd */
1490 m2 = strjoina("INTERFACE=", info->name);
1491 /* udevd */
1492 m3 = strjoina("DEVICE=", info->name);
1493
1494 (void)(
1495 (r = sd_journal_add_match(j, m1, 0)) ||
1496 (r = sd_journal_add_disjunction(j)) ||
1497 (r = sd_journal_add_match(j, m2, 0)) ||
1498 (r = sd_journal_add_disjunction(j)) ||
1499 (r = sd_journal_add_match(j, m3, 0))
1500 );
1501 if (r < 0)
1502 return log_error_errno(r, "Failed to add link matches: %m");
1503 } else {
1504 r = add_matches_for_unit(j, "systemd-networkd.service");
1505 if (r < 0)
1506 return log_error_errno(r, "Failed to add unit matches: %m");
1507
1508 r = add_matches_for_unit(j, "systemd-networkd-wait-online.service");
1509 if (r < 0)
1510 return log_error_errno(r, "Failed to add unit matches: %m");
1511 }
1512
1513 return show_journal(
1514 stdout,
1515 j,
1516 OUTPUT_SHORT,
1517 0,
1518 0,
1519 arg_lines,
1520 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
1521 NULL);
1522}
1523
69fb1176 1524static int link_status_one(
d9ce1c24 1525 sd_bus *bus,
1c4baffc 1526 sd_netlink *rtnl,
81fd1dd3 1527 sd_hwdb *hwdb,
b147503e
LP
1528 const LinkInfo *info) {
1529
2a71d57f 1530 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL;
862e7108 1531 _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL,
4a481ec4 1532 *setup_state = NULL, *operational_state = NULL, *online_state = NULL, *lease_file = NULL, *activation_policy = NULL;
35cab5f9 1533 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL,
4a481ec4 1534 *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup, *on_color_online;
b295beea 1535 _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
35cab5f9 1536 _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
98d5bef3
YW
1537 _cleanup_(table_unrefp) Table *table = NULL;
1538 TableCell *cell;
b147503e 1539 int r;
9085f64a
LP
1540
1541 assert(rtnl);
b147503e 1542 assert(info);
9085f64a 1543
b147503e 1544 (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
ceb366df 1545 operational_state_to_color(info->name, operational_state, &on_color_operational, &off_color_operational);
d57c365b 1546
4a481ec4
1547 (void) sd_network_link_get_online_state(info->ifindex, &online_state);
1548 online_state_to_color(online_state, &on_color_online, NULL);
1549
33d5013d
LP
1550 r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
1551 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
1552 setup_state = strdup("unmanaged");
d57c365b 1553 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 1554
b147503e
LP
1555 (void) sd_network_link_get_dns(info->ifindex, &dns);
1556 (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
1557 (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
1558 (void) sd_network_link_get_ntp(info->ifindex, &ntp);
1f807af6 1559 (void) sd_network_link_get_sip(info->ifindex, &sip);
9085f64a 1560
172353b1
ZJS
1561 if (info->sd_device) {
1562 (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link);
1563 (void) sd_device_get_property_value(info->sd_device, "ID_NET_DRIVER", &driver);
1564 (void) sd_device_get_property_value(info->sd_device, "ID_PATH", &path);
9085f64a 1565
172353b1
ZJS
1566 if (sd_device_get_property_value(info->sd_device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
1567 (void) sd_device_get_property_value(info->sd_device, "ID_VENDOR", &vendor);
9085f64a 1568
172353b1
ZJS
1569 if (sd_device_get_property_value(info->sd_device, "ID_MODEL_FROM_DATABASE", &model) < 0)
1570 (void) sd_device_get_property_value(info->sd_device, "ID_MODEL", &model);
9085f64a
LP
1571 }
1572
1a3caa49
YW
1573 r = link_get_type_string(info->sd_device, info->iftype, &t);
1574 if (r == -ENOMEM)
1575 return log_oom();
b1acce80 1576
4abd866d 1577 (void) sd_network_link_get_network_file(info->ifindex, &network);
df3fb561 1578
4abd866d
LP
1579 (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
1580 (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
0d4ad91d 1581
35cab5f9
YW
1582 if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", info->ifindex) < 0)
1583 return log_oom();
1584
1585 (void) dhcp_lease_load(&lease, lease_file);
1586
4252171a 1587 table = table_new("dot", "key", "value");
98d5bef3 1588 if (!table)
bd17fa8c 1589 return log_oom();
98d5bef3 1590
a42d9490
YW
1591 if (arg_full)
1592 table_set_width(table, 0);
1593
81914d9f
YW
1594 assert_se(cell = table_get_cell(table, 0, 0));
1595 (void) table_set_ellipsize_percent(table, cell, 100);
1596
1597 assert_se(cell = table_get_cell(table, 0, 1));
1598 (void) table_set_ellipsize_percent(table, cell, 100);
1599
98d5bef3
YW
1600 table_set_header(table, false);
1601
8d0e0af2
YW
1602 r = table_add_many(table,
1603 TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1604 TABLE_SET_COLOR, on_color_operational);
98d5bef3 1605 if (r < 0)
bd17fa8c 1606 return table_log_add_error(r);
81914d9f 1607 r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
98d5bef3 1608 if (r < 0)
bd17fa8c 1609 return table_log_add_error(r);
81914d9f 1610 (void) table_set_align_percent(table, cell, 0);
98d5bef3 1611
8d0e0af2
YW
1612 r = table_add_many(table,
1613 TABLE_EMPTY,
1614 TABLE_EMPTY,
1615 TABLE_STRING, "Link File:",
1616 TABLE_SET_ALIGN_PERCENT, 100,
1617 TABLE_STRING, strna(link),
1618 TABLE_EMPTY,
1619 TABLE_STRING, "Network File:",
1620 TABLE_STRING, strna(network),
1621 TABLE_EMPTY,
1622 TABLE_STRING, "Type:",
1623 TABLE_STRING, strna(t),
1624 TABLE_EMPTY,
1625 TABLE_STRING, "State:");
98d5bef3 1626 if (r < 0)
bd17fa8c 1627 return table_log_add_error(r);
98d5bef3 1628 r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
64e7ebde
YW
1629 on_color_operational, strna(operational_state), off_color_operational,
1630 on_color_setup, strna(setup_state), off_color_setup);
98d5bef3 1631 if (r < 0)
bd17fa8c 1632 return table_log_add_error(r);
98d5bef3 1633
4a481ec4
1634 r = table_add_many(table,
1635 TABLE_EMPTY,
1636 TABLE_STRING, "Online state:",
1637 TABLE_STRING, online_state ?: "unknown",
1638 TABLE_SET_COLOR, on_color_online);
1639 if (r < 0)
1640 return table_log_add_error(r);
1641
ae5b7792 1642 strv_sort(info->alternative_names);
536cdd07
YW
1643 r = dump_list(table, "Alternative Names:", info->alternative_names);
1644 if (r < 0)
1645 return r;
511070ee 1646
98d5bef3 1647 if (path) {
8d0e0af2
YW
1648 r = table_add_many(table,
1649 TABLE_EMPTY,
1650 TABLE_STRING, "Path:",
1651 TABLE_STRING, path);
98d5bef3 1652 if (r < 0)
bd17fa8c 1653 return table_log_add_error(r);
98d5bef3
YW
1654 }
1655 if (driver) {
8d0e0af2
YW
1656 r = table_add_many(table,
1657 TABLE_EMPTY,
1658 TABLE_STRING, "Driver:",
1659 TABLE_STRING, driver);
98d5bef3 1660 if (r < 0)
bd17fa8c 1661 return table_log_add_error(r);
98d5bef3
YW
1662 }
1663 if (vendor) {
8d0e0af2
YW
1664 r = table_add_many(table,
1665 TABLE_EMPTY,
1666 TABLE_STRING, "Vendor:",
1667 TABLE_STRING, vendor);
98d5bef3 1668 if (r < 0)
bd17fa8c 1669 return table_log_add_error(r);
98d5bef3
YW
1670 }
1671 if (model) {
8d0e0af2
YW
1672 r = table_add_many(table,
1673 TABLE_EMPTY,
1674 TABLE_STRING, "Model:",
1675 TABLE_STRING, model);
98d5bef3 1676 if (r < 0)
bd17fa8c 1677 return table_log_add_error(r);
98d5bef3 1678 }
9085f64a 1679
b147503e 1680 if (info->has_mac_address) {
888943fc 1681 _cleanup_free_ char *description = NULL;
888943fc 1682
f1f17144 1683 if (info->hw_address.length == ETH_ALEN)
ca2b7cd8 1684 (void) ieee_oui(hwdb, &info->hw_address.ether, &description);
888943fc 1685
8d0e0af2
YW
1686 r = table_add_many(table,
1687 TABLE_EMPTY,
1688 TABLE_STRING, "HW Address:");
98d5bef3 1689 if (r < 0)
bd17fa8c 1690 return table_log_add_error(r);
98d5bef3 1691 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
f1f17144 1692 HW_ADDR_TO_STR(&info->hw_address),
64e7ebde
YW
1693 description ? " (" : "",
1694 strempty(description),
1695 description ? ")" : "");
98d5bef3 1696 if (r < 0)
bd17fa8c 1697 return table_log_add_error(r);
db73295a 1698 }
9085f64a 1699
caa8538a
YW
1700 if (info->has_permanent_mac_address) {
1701 _cleanup_free_ char *description = NULL;
caa8538a
YW
1702
1703 (void) ieee_oui(hwdb, &info->permanent_mac_address, &description);
1704
1705 r = table_add_many(table,
1706 TABLE_EMPTY,
1707 TABLE_STRING, "HW Permanent Address:");
1708 if (r < 0)
bd17fa8c 1709 return table_log_add_error(r);
caa8538a 1710 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
4b574fd8 1711 ETHER_ADDR_TO_STR(&info->permanent_mac_address),
caa8538a
YW
1712 description ? " (" : "",
1713 strempty(description),
1714 description ? ")" : "");
1715 if (r < 0)
bd17fa8c 1716 return table_log_add_error(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
0221d68a 2399 (void) 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
0221d68a 2498 (void) 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);
2685 if (r < 0) {
2686 char ifname[IF_NAMESIZE + 1];
2687
6f50c94d
SL
2688 return log_error_errno(r, "Failed to bring %s interface %s: %m",
2689 argv[0], format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
c30ffcee
SS
2690 }
2691 }
2692
2693 return r;
2694}
2695
9cd8c766
SS
2696static int link_delete(int argc, char *argv[], void *userdata) {
2697 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
2698 _cleanup_set_free_ Set *indexes = NULL;
b48e277f 2699 int index, r;
38b9af61 2700 void *p;
9cd8c766
SS
2701
2702 r = sd_netlink_open(&rtnl);
2703 if (r < 0)
2704 return log_error_errno(r, "Failed to connect to netlink: %m");
2705
2706 indexes = set_new(NULL);
2707 if (!indexes)
2708 return log_oom();
2709
b48e277f 2710 for (int i = 1; i < argc; i++) {
f6e49154 2711 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2712 if (index < 0)
2713 return index;
9cd8c766
SS
2714
2715 r = set_put(indexes, INT_TO_PTR(index));
2716 if (r < 0)
2717 return log_oom();
2718 }
2719
90e74a66 2720 SET_FOREACH(p, indexes) {
d56d6cb8
YW
2721 index = PTR_TO_INT(p);
2722 r = link_delete_send_message(rtnl, index);
9cd8c766 2723 if (r < 0) {
518a66ec
YW
2724 char ifname[IF_NAMESIZE + 1];
2725
e4857ee2
YW
2726 return log_error_errno(r, "Failed to delete interface %s: %m",
2727 format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
9cd8c766
SS
2728 }
2729 }
2730
2731 return r;
2732}
2733
308e7dfd
YW
2734static int link_renew_one(sd_bus *bus, int index, const char *name) {
2735 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2736 int r;
2737
8a048c8c 2738 r = bus_call_method(bus, bus_network_mgr, "RenewLink", &error, NULL, "i", index);
308e7dfd
YW
2739 if (r < 0)
2740 return log_error_errno(r, "Failed to renew dynamic configuration of interface %s: %s",
2741 name, bus_error_message(&error, r));
2742
2743 return 0;
2744}
2745
2746static int link_renew(int argc, char *argv[], void *userdata) {
2747 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f7581ed6 2748 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b48e277f 2749 int index, k = 0, r;
308e7dfd
YW
2750
2751 r = sd_bus_open_system(&bus);
2752 if (r < 0)
2753 return log_error_errno(r, "Failed to connect system bus: %m");
2754
b48e277f 2755 for (int i = 1; i < argc; i++) {
f6e49154 2756 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2757 if (index < 0)
2758 return index;
308e7dfd
YW
2759
2760 r = link_renew_one(bus, index, argv[i]);
2761 if (r < 0 && k >= 0)
2762 k = r;
2763 }
2764
2765 return k;
2766}
2767
3efdd6af
SS
2768static int link_force_renew_one(sd_bus *bus, int index, const char *name) {
2769 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2770 int r;
2771
8a048c8c 2772 r = bus_call_method(bus, bus_network_mgr, "ForceRenewLink", &error, NULL, "i", index);
3efdd6af
SS
2773 if (r < 0)
2774 return log_error_errno(r, "Failed to force renew dynamic configuration of interface %s: %s",
2775 name, bus_error_message(&error, r));
2776
2777 return 0;
2778}
2779
2780static int link_force_renew(int argc, char *argv[], void *userdata) {
2781 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2782 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b48e277f 2783 int k = 0, r;
3efdd6af
SS
2784
2785 r = sd_bus_open_system(&bus);
2786 if (r < 0)
2787 return log_error_errno(r, "Failed to connect system bus: %m");
2788
b48e277f 2789 for (int i = 1; i < argc; i++) {
f6e49154 2790 int index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
3efdd6af
SS
2791 if (index < 0)
2792 return index;
2793
2794 r = link_force_renew_one(bus, index, argv[i]);
2795 if (r < 0 && k >= 0)
2796 k = r;
2797 }
2798
2799 return k;
2800}
2801
a227674c
YW
2802static int verb_reload(int argc, char *argv[], void *userdata) {
2803 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2804 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2805 int r;
2806
2807 r = sd_bus_open_system(&bus);
2808 if (r < 0)
2809 return log_error_errno(r, "Failed to connect system bus: %m");
2810
8a048c8c 2811 r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
a227674c
YW
2812 if (r < 0)
2813 return log_error_errno(r, "Failed to reload network settings: %m");
2814
2815 return 0;
2816}
2817
8dc85c5e
YW
2818static int verb_reconfigure(int argc, char *argv[], void *userdata) {
2819 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2820 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f7581ed6 2821 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
8dc85c5e 2822 _cleanup_set_free_ Set *indexes = NULL;
b48e277f 2823 int index, r;
8dc85c5e
YW
2824 void *p;
2825
2826 r = sd_bus_open_system(&bus);
2827 if (r < 0)
2828 return log_error_errno(r, "Failed to connect system bus: %m");
2829
2830 indexes = set_new(NULL);
2831 if (!indexes)
2832 return log_oom();
2833
b48e277f 2834 for (int i = 1; i < argc; i++) {
f6e49154 2835 index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]);
231d9de1
ZJS
2836 if (index < 0)
2837 return index;
8dc85c5e
YW
2838
2839 r = set_put(indexes, INT_TO_PTR(index));
2840 if (r < 0)
2841 return log_oom();
2842 }
2843
90e74a66 2844 SET_FOREACH(p, indexes) {
8dc85c5e 2845 index = PTR_TO_INT(p);
8a048c8c 2846 r = bus_call_method(bus, bus_network_mgr, "ReconfigureLink", &error, NULL, "i", index);
8dc85c5e
YW
2847 if (r < 0) {
2848 char ifname[IF_NAMESIZE + 1];
2849
117caf37
ZJS
2850 return log_error_errno(r, "Failed to reconfigure network interface %s: %m",
2851 format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
8dc85c5e
YW
2852 }
2853 }
2854
2855 return 0;
2856}
2857
37ec0fdd
LP
2858static int help(void) {
2859 _cleanup_free_ char *link = NULL;
2860 int r;
2861
2862 r = terminal_urlify_man("networkctl", "1", &link);
2863 if (r < 0)
2864 return log_oom();
2865
353b2baa
LP
2866 printf("%s [OPTIONS...] COMMAND\n\n"
2867 "%sQuery and control the networking subsystem.%s\n"
a459b24f 2868 "\nCommands:\n"
8dc85c5e
YW
2869 " list [PATTERN...] List links\n"
2870 " status [PATTERN...] Show link status\n"
2871 " lldp [PATTERN...] Show LLDP neighbors\n"
2872 " label Show current address label entries in the kernel\n"
2873 " delete DEVICES... Delete virtual netdevs\n"
c30ffcee
SS
2874 " up DEVICES... Bring devices up\n"
2875 " down DEVICES... Bring devices down\n"
8dc85c5e 2876 " renew DEVICES... Renew dynamic configurations\n"
3efdd6af 2877 " forcerenew DEVICES... Trigger DHCP reconfiguration of all connected clients\n"
8dc85c5e
YW
2878 " reconfigure DEVICES... Reconfigure interfaces\n"
2879 " reload Reload .network and .netdev files\n"
353b2baa 2880 "\nOptions:\n"
8dc85c5e
YW
2881 " -h --help Show this help\n"
2882 " --version Show package version\n"
2883 " --no-pager Do not pipe output into a pager\n"
2884 " --no-legend Do not show the headers and footers\n"
2885 " -a --all Show status for all links\n"
2886 " -s --stats Show detailed link statics\n"
10c71c36
YW
2887 " -l --full Do not ellipsize output\n"
2888 " -n --lines=INTEGER Number of journal entries to show\n"
bfc2b05e
YW
2889 " --json=pretty|short|off\n"
2890 " Generate JSON output\n"
bc556335
DDM
2891 "\nSee the %s for details.\n",
2892 program_invocation_short_name,
2893 ansi_highlight(),
2894 ansi_normal(),
2895 link);
37ec0fdd
LP
2896
2897 return 0;
ee8c4568
LP
2898}
2899
2900static int parse_argv(int argc, char *argv[]) {
2901
2902 enum {
2903 ARG_VERSION = 0x100,
2904 ARG_NO_PAGER,
2905 ARG_NO_LEGEND,
bfc2b05e 2906 ARG_JSON,
ee8c4568
LP
2907 };
2908
2909 static const struct option options[] = {
2910 { "help", no_argument, NULL, 'h' },
2911 { "version", no_argument, NULL, ARG_VERSION },
2912 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2913 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 2914 { "all", no_argument, NULL, 'a' },
a459b24f 2915 { "stats", no_argument, NULL, 's' },
10c71c36
YW
2916 { "full", no_argument, NULL, 'l' },
2917 { "lines", required_argument, NULL, 'n' },
bfc2b05e 2918 { "json", required_argument, NULL, ARG_JSON },
ee8c4568
LP
2919 {}
2920 };
2921
bfc2b05e 2922 int c, r;
ee8c4568
LP
2923
2924 assert(argc >= 0);
2925 assert(argv);
2926
10c71c36 2927 while ((c = getopt_long(argc, argv, "hasln:", options, NULL)) >= 0) {
ee8c4568
LP
2928
2929 switch (c) {
2930
2931 case 'h':
37ec0fdd 2932 return help();
ee8c4568
LP
2933
2934 case ARG_VERSION:
3f6fd1ba 2935 return version();
ee8c4568
LP
2936
2937 case ARG_NO_PAGER:
0221d68a 2938 arg_pager_flags |= PAGER_DISABLE;
ee8c4568
LP
2939 break;
2940
2941 case ARG_NO_LEGEND:
2942 arg_legend = false;
2943 break;
2944
9085f64a
LP
2945 case 'a':
2946 arg_all = true;
2947 break;
2948
a459b24f
YW
2949 case 's':
2950 arg_stats = true;
2951 break;
2952
10c71c36
YW
2953 case 'l':
2954 arg_full = true;
2955 break;
2956
2957 case 'n':
2958 if (safe_atou(optarg, &arg_lines) < 0)
2959 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2960 "Failed to parse lines '%s'", optarg);
2961 break;
2962
bfc2b05e
YW
2963 case ARG_JSON:
2964 r = parse_json_argument(optarg, &arg_json_format_flags);
2965 if (r <= 0)
2966 return r;
2967 break;
2968
ee8c4568
LP
2969 case '?':
2970 return -EINVAL;
2971
2972 default:
04499a70 2973 assert_not_reached();
ee8c4568
LP
2974 }
2975 }
2976
2977 return 1;
2978}
2979
2980static int networkctl_main(int argc, char *argv[]) {
15c3626e 2981 static const Verb verbs[] = {
8dc85c5e
YW
2982 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
2983 { "status", VERB_ANY, VERB_ANY, 0, link_status },
2984 { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
df696b14 2985 { "label", 1, 1, 0, list_address_labels },
8dc85c5e 2986 { "delete", 2, VERB_ANY, 0, link_delete },
c30ffcee
SS
2987 { "up", 2, VERB_ANY, 0, link_up_down },
2988 { "down", 2, VERB_ANY, 0, link_up_down },
8dc85c5e 2989 { "renew", 2, VERB_ANY, 0, link_renew },
3efdd6af 2990 { "forcerenew", 2, VERB_ANY, 0, link_force_renew },
8dc85c5e
YW
2991 { "reconfigure", 2, VERB_ANY, 0, verb_reconfigure },
2992 { "reload", 1, 1, 0, verb_reload },
266b5389 2993 {}
ee8c4568
LP
2994 };
2995
266b5389 2996 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
2997}
2998
3b085db3
LP
2999static int check_netns_match(void) {
3000 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3001 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
3002 struct stat st;
3003 uint64_t id;
3004 int r;
3005
3006 r = sd_bus_open_system(&bus);
3007 if (r < 0)
3008 return log_error_errno(r, "Failed to connect system bus: %m");
3009
3010 r = sd_bus_get_property_trivial(
3011 bus,
3012 "org.freedesktop.network1",
3013 "/org/freedesktop/network1",
3014 "org.freedesktop.network1.Manager",
3015 "NamespaceId",
3016 &error,
3017 't',
3018 &id);
3019 if (r < 0) {
3020 log_debug_errno(r, "Failed to query network namespace of networkd, ignoring: %s", bus_error_message(&error, r));
3021 return 0;
3022 }
3023 if (id == 0) {
3024 log_debug("systemd-networkd.service not running in a network namespace (?), skipping netns check.");
3025 return 0;
3026 }
3027
3028 if (stat("/proc/self/ns/net", &st) < 0)
3029 return log_error_errno(r, "Failed to determine our own network namespace ID: %m");
3030
3031 if (id != st.st_ino)
3032 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
3033 "networkctl must be invoked in same network namespace as systemd-networkd.service.");
3034
3035 return 0;
3036}
3037
58fb3678
LP
3038static void warn_networkd_missing(void) {
3039
3040 if (access("/run/systemd/netif/state", F_OK) >= 0)
3041 return;
3042
3043 fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
3044}
3045
4e2ca442 3046static int run(int argc, char* argv[]) {
ee8c4568
LP
3047 int r;
3048
d2acb93d 3049 log_setup();
ee8c4568
LP
3050
3051 r = parse_argv(argc, argv);
3052 if (r <= 0)
4e2ca442 3053 return r;
ee8c4568 3054
3b085db3
LP
3055 r = check_netns_match();
3056 if (r < 0)
3057 return r;
3058
58fb3678
LP
3059 warn_networkd_missing();
3060
4e2ca442 3061 return networkctl_main(argc, argv);
ee8c4568 3062}
4e2ca442
ZJS
3063
3064DEFINE_MAIN_FUNCTION(run);