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