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