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