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