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