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