Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <getopt.h>
4 : #include <linux/if_addrlabel.h>
5 : #include <net/if.h>
6 : #include <stdbool.h>
7 : #include <sys/stat.h>
8 : #include <sys/types.h>
9 : #include <unistd.h>
10 :
11 : #include "sd-bus.h"
12 : #include "sd-device.h"
13 : #include "sd-hwdb.h"
14 : #include "sd-lldp.h"
15 : #include "sd-netlink.h"
16 : #include "sd-network.h"
17 :
18 : #include "alloc-util.h"
19 : #include "arphrd-list.h"
20 : #include "bus-common-errors.h"
21 : #include "bus-error.h"
22 : #include "bus-util.h"
23 : #include "device-util.h"
24 : #include "ether-addr-util.h"
25 : #include "ethtool-util.h"
26 : #include "fd-util.h"
27 : #include "format-table.h"
28 : #include "format-util.h"
29 : #include "hwdb-util.h"
30 : #include "local-addresses.h"
31 : #include "locale-util.h"
32 : #include "macro.h"
33 : #include "main-func.h"
34 : #include "netlink-util.h"
35 : #include "pager.h"
36 : #include "parse-util.h"
37 : #include "pretty-print.h"
38 : #include "set.h"
39 : #include "socket-util.h"
40 : #include "sort-util.h"
41 : #include "sparse-endian.h"
42 : #include "stdio-util.h"
43 : #include "string-table.h"
44 : #include "string-util.h"
45 : #include "strv.h"
46 : #include "strxcpyx.h"
47 : #include "terminal-util.h"
48 : #include "verbs.h"
49 :
50 : /* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
51 : #define NETDEV_KIND_MAX 64
52 :
53 : static PagerFlags arg_pager_flags = 0;
54 : static bool arg_legend = true;
55 : static bool arg_all = false;
56 : static bool arg_stats = false;
57 :
58 0 : static char *link_get_type_string(unsigned short iftype, sd_device *d) {
59 : const char *t, *devtype;
60 : char *p;
61 :
62 0 : if (d &&
63 0 : sd_device_get_devtype(d, &devtype) >= 0 &&
64 0 : !isempty(devtype))
65 0 : return strdup(devtype);
66 :
67 0 : t = arphrd_to_name(iftype);
68 0 : if (!t)
69 0 : return NULL;
70 :
71 0 : p = strdup(t);
72 0 : if (!p)
73 0 : return NULL;
74 :
75 0 : ascii_strlower(p);
76 0 : return p;
77 : }
78 :
79 0 : static void operational_state_to_color(const char *state, const char **on, const char **off) {
80 0 : assert(on);
81 0 : assert(off);
82 :
83 0 : if (STRPTR_IN_SET(state, "routable", "enslaved")) {
84 0 : *on = ansi_highlight_green();
85 0 : *off = ansi_normal();
86 0 : } else if (streq_ptr(state, "degraded")) {
87 0 : *on = ansi_highlight_yellow();
88 0 : *off = ansi_normal();
89 : } else
90 0 : *on = *off = "";
91 0 : }
92 :
93 0 : static void setup_state_to_color(const char *state, const char **on, const char **off) {
94 0 : assert(on);
95 0 : assert(off);
96 :
97 0 : if (streq_ptr(state, "configured")) {
98 0 : *on = ansi_highlight_green();
99 0 : *off = ansi_normal();
100 0 : } else if (streq_ptr(state, "configuring")) {
101 0 : *on = ansi_highlight_yellow();
102 0 : *off = ansi_normal();
103 0 : } else if (STRPTR_IN_SET(state, "failed", "linger")) {
104 0 : *on = ansi_highlight_red();
105 0 : *off = ansi_normal();
106 : } else
107 0 : *on = *off = "";
108 0 : }
109 :
110 : typedef 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 :
122 : } VxLanInfo;
123 :
124 : typedef struct LinkInfo {
125 : char name[IFNAMSIZ+1];
126 : char netdev_kind[NETDEV_KIND_MAX];
127 : int ifindex;
128 : unsigned short iftype;
129 : struct ether_addr mac_address;
130 : uint32_t mtu;
131 : uint32_t min_mtu;
132 : uint32_t max_mtu;
133 : uint32_t tx_queues;
134 : uint32_t rx_queues;
135 :
136 : union {
137 : struct rtnl_link_stats64 stats64;
138 : struct rtnl_link_stats stats;
139 : };
140 :
141 : uint64_t tx_bitrate;
142 : uint64_t rx_bitrate;
143 :
144 : /* bridge info */
145 : uint32_t forward_delay;
146 : uint32_t hello_time;
147 : uint32_t max_age;
148 : uint32_t ageing_time;
149 : uint32_t stp_state;
150 : uint16_t priority;
151 : uint8_t mcast_igmp_version;
152 :
153 : /* vxlan info */
154 : VxLanInfo vxlan_info;
155 :
156 : /* ethtool info */
157 : int autonegotiation;
158 : size_t speed;
159 : Duplex duplex;
160 : NetDevPort port;
161 :
162 : bool has_mac_address:1;
163 : bool has_tx_queues:1;
164 : bool has_rx_queues:1;
165 : bool has_stats64:1;
166 : bool has_stats:1;
167 : bool has_bitrates:1;
168 : bool has_ethtool_link_info:1;
169 : } LinkInfo;
170 :
171 0 : static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
172 0 : return CMP(a->ifindex, b->ifindex);
173 : }
174 :
175 0 : static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
176 : const char *received_kind;
177 : int r;
178 :
179 0 : assert(m);
180 0 : assert(info);
181 :
182 0 : r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
183 0 : if (r < 0)
184 0 : return r;
185 :
186 0 : r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
187 0 : if (r < 0)
188 0 : return r;
189 :
190 0 : r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
191 0 : if (r < 0)
192 0 : return r;
193 :
194 0 : if (streq(received_kind, "bridge")) {
195 0 : (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
196 0 : (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
197 0 : (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
198 0 : (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
199 0 : (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
200 0 : (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
201 0 : (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
202 :
203 0 : } else if (streq(received_kind, "vxlan")) {
204 0 : (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
205 :
206 0 : r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
207 0 : if (r >= 0)
208 0 : info->vxlan_info.group_family = AF_INET;
209 : else {
210 0 : r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
211 0 : if (r >= 0)
212 0 : info->vxlan_info.group_family = AF_INET6;
213 : }
214 :
215 0 : r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
216 0 : if (r >= 0)
217 0 : info->vxlan_info.local_family = AF_INET;
218 : else {
219 0 : r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
220 0 : if (r >= 0)
221 0 : info->vxlan_info.local_family = AF_INET6;
222 : }
223 :
224 0 : (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
225 0 : (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
226 : }
227 :
228 0 : strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
229 :
230 0 : (void) sd_netlink_message_exit_container(m);
231 0 : (void) sd_netlink_message_exit_container(m);
232 :
233 0 : return 0;
234 : }
235 :
236 0 : static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
237 : const char *name;
238 : int ifindex, r;
239 : uint16_t type;
240 :
241 0 : assert(m);
242 0 : assert(info);
243 :
244 0 : r = sd_netlink_message_get_type(m, &type);
245 0 : if (r < 0)
246 0 : return r;
247 :
248 0 : if (type != RTM_NEWLINK)
249 0 : return 0;
250 :
251 0 : r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
252 0 : if (r < 0)
253 0 : return r;
254 :
255 0 : r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
256 0 : if (r < 0)
257 0 : return r;
258 :
259 0 : if (patterns) {
260 : char str[DECIMAL_STR_MAX(int)];
261 :
262 0 : xsprintf(str, "%i", ifindex);
263 :
264 0 : if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0))
265 0 : return 0;
266 : }
267 :
268 0 : r = sd_rtnl_message_link_get_type(m, &info->iftype);
269 0 : if (r < 0)
270 0 : return r;
271 :
272 0 : strscpy(info->name, sizeof info->name, name);
273 0 : info->ifindex = ifindex;
274 :
275 0 : info->has_mac_address =
276 0 : sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 &&
277 0 : memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
278 :
279 0 : (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu);
280 0 : (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu);
281 0 : (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu);
282 :
283 0 : info->has_rx_queues =
284 0 : sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
285 0 : info->rx_queues > 0;
286 :
287 0 : info->has_tx_queues =
288 0 : sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
289 0 : info->tx_queues > 0;
290 :
291 0 : if (sd_netlink_message_read(m, IFLA_STATS64, sizeof info->stats64, &info->stats64) >= 0)
292 0 : info->has_stats64 = true;
293 0 : else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
294 0 : info->has_stats = true;
295 :
296 : /* fill kind info */
297 0 : (void) decode_netdev(m, info);
298 :
299 0 : return 1;
300 : }
301 :
302 0 : static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
303 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
304 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
305 0 : _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
306 : int r;
307 :
308 0 : if (asprintf(&ifindex_str, "%i", link->ifindex) < 0)
309 0 : return -ENOMEM;
310 :
311 0 : r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path);
312 0 : if (r < 0)
313 0 : return r;
314 :
315 0 : r = sd_bus_call_method(
316 : bus,
317 : "org.freedesktop.network1",
318 : path,
319 : "org.freedesktop.DBus.Properties",
320 : "Get",
321 : &error,
322 : &reply,
323 : "ss",
324 : "org.freedesktop.network1.Link",
325 : "BitRates");
326 0 : if (r < 0) {
327 0 : bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) ||
328 0 : sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
329 :
330 0 : return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
331 : r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
332 : }
333 :
334 0 : r = sd_bus_message_enter_container(reply, 'v', "(tt)");
335 0 : if (r < 0)
336 0 : return bus_log_parse_error(r);
337 :
338 0 : r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
339 0 : if (r < 0)
340 0 : return bus_log_parse_error(r);
341 :
342 0 : r = sd_bus_message_exit_container(reply);
343 0 : if (r < 0)
344 0 : return bus_log_parse_error(r);
345 :
346 0 : link->has_bitrates = true;
347 :
348 0 : return 0;
349 : }
350 :
351 0 : static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
352 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
353 0 : _cleanup_free_ LinkInfo *links = NULL;
354 0 : _cleanup_close_ int fd = -1;
355 0 : size_t allocated = 0, c = 0, j;
356 : sd_netlink_message *i;
357 : int r;
358 :
359 0 : assert(rtnl);
360 0 : assert(ret);
361 :
362 0 : r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
363 0 : if (r < 0)
364 0 : return rtnl_log_create_error(r);
365 :
366 0 : r = sd_netlink_message_request_dump(req, true);
367 0 : if (r < 0)
368 0 : return rtnl_log_create_error(r);
369 :
370 0 : r = sd_netlink_call(rtnl, req, 0, &reply);
371 0 : if (r < 0)
372 0 : return log_error_errno(r, "Failed to enumerate links: %m");
373 :
374 0 : for (i = reply; i; i = sd_netlink_message_next(i)) {
375 0 : if (!GREEDY_REALLOC0(links, allocated, c+1))
376 0 : return -ENOMEM;
377 :
378 0 : r = decode_link(i, links + c, patterns);
379 0 : if (r < 0)
380 0 : return r;
381 0 : if (r == 0)
382 0 : continue;
383 :
384 0 : r = ethtool_get_link_info(&fd, links[c].name,
385 0 : &links[c].autonegotiation, &links[c].speed,
386 0 : &links[c].duplex, &links[c].port);
387 0 : if (r >= 0)
388 0 : links[c].has_ethtool_link_info = true;
389 :
390 0 : c++;
391 : }
392 :
393 0 : typesafe_qsort(links, c, link_info_compare);
394 :
395 0 : if (bus)
396 0 : for (j = 0; j < c; j++)
397 0 : (void) acquire_link_bitrates(bus, links + j);
398 :
399 0 : *ret = TAKE_PTR(links);
400 :
401 0 : return (int) c;
402 : }
403 :
404 0 : static int list_links(int argc, char *argv[], void *userdata) {
405 0 : _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
406 0 : _cleanup_free_ LinkInfo *links = NULL;
407 0 : _cleanup_(table_unrefp) Table *table = NULL;
408 : TableCell *cell;
409 : int c, i, r;
410 :
411 0 : r = sd_netlink_open(&rtnl);
412 0 : if (r < 0)
413 0 : return log_error_errno(r, "Failed to connect to netlink: %m");
414 :
415 0 : c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
416 0 : if (c < 0)
417 0 : return c;
418 :
419 0 : (void) pager_open(arg_pager_flags);
420 :
421 0 : table = table_new("idx", "link", "type", "operational", "setup");
422 0 : if (!table)
423 0 : return log_oom();
424 :
425 0 : table_set_header(table, arg_legend);
426 :
427 0 : assert_se(cell = table_get_cell(table, 0, 0));
428 0 : (void) table_set_minimum_width(table, cell, 3);
429 0 : (void) table_set_weight(table, cell, 0);
430 0 : (void) table_set_ellipsize_percent(table, cell, 100);
431 0 : (void) table_set_align_percent(table, cell, 100);
432 :
433 0 : assert_se(cell = table_get_cell(table, 0, 1));
434 0 : (void) table_set_ellipsize_percent(table, cell, 100);
435 :
436 0 : for (i = 0; i < c; i++) {
437 0 : _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
438 0 : _cleanup_(sd_device_unrefp) sd_device *d = NULL;
439 : const char *on_color_operational, *off_color_operational,
440 : *on_color_setup, *off_color_setup;
441 : char devid[2 + DECIMAL_STR_MAX(int)];
442 0 : _cleanup_free_ char *t = NULL;
443 :
444 0 : (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
445 0 : operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
446 :
447 0 : r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
448 0 : if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
449 0 : setup_state = strdup("unmanaged");
450 0 : setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
451 :
452 0 : xsprintf(devid, "n%i", links[i].ifindex);
453 0 : (void) sd_device_new_from_device_id(&d, devid);
454 :
455 0 : t = link_get_type_string(links[i].iftype, d);
456 :
457 0 : r = table_add_many(table,
458 : TABLE_INT, links[i].ifindex,
459 : TABLE_STRING, links[i].name,
460 : TABLE_STRING, strna(t),
461 : TABLE_STRING, strna(operational_state),
462 : TABLE_SET_COLOR, on_color_operational,
463 : TABLE_STRING, strna(setup_state),
464 : TABLE_SET_COLOR, on_color_setup);
465 0 : if (r < 0)
466 0 : return r;
467 : }
468 :
469 0 : r = table_print(table, NULL);
470 0 : if (r < 0)
471 0 : return log_error_errno(r, "Failed to print table: %m");
472 :
473 0 : if (arg_legend)
474 0 : printf("\n%i links listed.\n", c);
475 :
476 0 : return 0;
477 : }
478 :
479 : /* IEEE Organizationally Unique Identifier vendor string */
480 0 : static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
481 : const char *description;
482 : char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
483 : int r;
484 :
485 0 : assert(ret);
486 :
487 0 : if (!hwdb)
488 0 : return -EINVAL;
489 :
490 0 : if (!mac)
491 0 : return -EINVAL;
492 :
493 : /* skip commonly misused 00:00:00 (Xerox) prefix */
494 0 : if (memcmp(mac, "\0\0\0", 3) == 0)
495 0 : return -EINVAL;
496 :
497 0 : xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
498 : ETHER_ADDR_FORMAT_VAL(*mac));
499 :
500 0 : r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
501 0 : if (r < 0)
502 0 : return r;
503 :
504 0 : desc = strdup(description);
505 0 : if (!desc)
506 0 : return -ENOMEM;
507 :
508 0 : *ret = desc;
509 :
510 0 : return 0;
511 : }
512 :
513 0 : static int get_gateway_description(
514 : sd_netlink *rtnl,
515 : sd_hwdb *hwdb,
516 : int ifindex,
517 : int family,
518 : union in_addr_union *gateway,
519 : char **gateway_description) {
520 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
521 : sd_netlink_message *m;
522 : int r;
523 :
524 0 : assert(rtnl);
525 0 : assert(ifindex >= 0);
526 0 : assert(IN_SET(family, AF_INET, AF_INET6));
527 0 : assert(gateway);
528 0 : assert(gateway_description);
529 :
530 0 : r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
531 0 : if (r < 0)
532 0 : return r;
533 :
534 0 : r = sd_netlink_message_request_dump(req, true);
535 0 : if (r < 0)
536 0 : return r;
537 :
538 0 : r = sd_netlink_call(rtnl, req, 0, &reply);
539 0 : if (r < 0)
540 0 : return r;
541 :
542 0 : for (m = reply; m; m = sd_netlink_message_next(m)) {
543 0 : union in_addr_union gw = IN_ADDR_NULL;
544 0 : struct ether_addr mac = ETHER_ADDR_NULL;
545 : uint16_t type;
546 : int ifi, fam;
547 :
548 0 : r = sd_netlink_message_get_errno(m);
549 0 : if (r < 0) {
550 0 : log_error_errno(r, "got error: %m");
551 0 : continue;
552 : }
553 :
554 0 : r = sd_netlink_message_get_type(m, &type);
555 0 : if (r < 0) {
556 0 : log_error_errno(r, "could not get type: %m");
557 0 : continue;
558 : }
559 :
560 0 : if (type != RTM_NEWNEIGH) {
561 0 : log_error("type is not RTM_NEWNEIGH");
562 0 : continue;
563 : }
564 :
565 0 : r = sd_rtnl_message_neigh_get_family(m, &fam);
566 0 : if (r < 0) {
567 0 : log_error_errno(r, "could not get family: %m");
568 0 : continue;
569 : }
570 :
571 0 : if (fam != family) {
572 0 : log_error("family is not correct");
573 0 : continue;
574 : }
575 :
576 0 : r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
577 0 : if (r < 0) {
578 0 : log_error_errno(r, "could not get ifindex: %m");
579 0 : continue;
580 : }
581 :
582 0 : if (ifindex > 0 && ifi != ifindex)
583 0 : continue;
584 :
585 0 : switch (fam) {
586 0 : case AF_INET:
587 0 : r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
588 0 : if (r < 0)
589 0 : continue;
590 :
591 0 : break;
592 0 : case AF_INET6:
593 0 : r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
594 0 : if (r < 0)
595 0 : continue;
596 :
597 0 : break;
598 0 : default:
599 0 : continue;
600 : }
601 :
602 0 : if (!in_addr_equal(fam, &gw, gateway))
603 0 : continue;
604 :
605 0 : r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
606 0 : if (r < 0)
607 0 : continue;
608 :
609 0 : r = ieee_oui(hwdb, &mac, gateway_description);
610 0 : if (r < 0)
611 0 : continue;
612 :
613 0 : return 0;
614 : }
615 :
616 0 : return -ENODATA;
617 : }
618 :
619 0 : static int dump_gateways(
620 : sd_netlink *rtnl,
621 : sd_hwdb *hwdb,
622 : Table *table,
623 : int ifindex) {
624 0 : _cleanup_free_ struct local_address *local = NULL;
625 : int r, n, i;
626 :
627 0 : assert(rtnl);
628 0 : assert(table);
629 :
630 0 : n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
631 0 : if (n < 0)
632 0 : return n;
633 :
634 0 : for (i = 0; i < n; i++) {
635 0 : _cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
636 :
637 0 : r = table_add_many(table,
638 : TABLE_EMPTY,
639 : TABLE_STRING, i == 0 ? "Gateway:" : "");
640 0 : if (r < 0)
641 0 : return r;
642 :
643 0 : r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
644 0 : if (r < 0)
645 0 : return r;
646 :
647 0 : r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
648 0 : if (r < 0)
649 0 : log_debug_errno(r, "Could not get description of gateway: %m");
650 :
651 0 : if (description) {
652 0 : with_description = strjoin(gateway, " (", description, ")");
653 0 : if (!with_description)
654 0 : return -ENOMEM;
655 : }
656 :
657 : /* Show interface name for the entry if we show
658 : * entries for all interfaces */
659 0 : if (ifindex <= 0) {
660 : char name[IF_NAMESIZE+1];
661 :
662 0 : if (format_ifname(local[i].ifindex, name))
663 0 : r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway, name);
664 : else
665 0 : r = table_add_cell_stringf(table, NULL, "%s on %%%i", with_description ?: gateway, local[i].ifindex);
666 : } else
667 0 : r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway);
668 0 : if (r < 0)
669 0 : return r;
670 : }
671 :
672 0 : return 0;
673 : }
674 :
675 0 : static int dump_addresses(
676 : sd_netlink *rtnl,
677 : Table *table,
678 : int ifindex) {
679 :
680 0 : _cleanup_free_ struct local_address *local = NULL;
681 : int r, n, i;
682 :
683 0 : assert(rtnl);
684 0 : assert(table);
685 :
686 0 : n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
687 0 : if (n < 0)
688 0 : return n;
689 :
690 0 : for (i = 0; i < n; i++) {
691 0 : _cleanup_free_ char *pretty = NULL;
692 :
693 0 : r = table_add_many(table,
694 : TABLE_EMPTY,
695 : TABLE_STRING, i == 0 ? "Address:" : "");
696 0 : if (r < 0)
697 0 : return r;
698 :
699 0 : r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
700 0 : if (r < 0)
701 0 : return r;
702 :
703 0 : if (ifindex <= 0) {
704 : char name[IF_NAMESIZE+1];
705 :
706 0 : if (format_ifname(local[i].ifindex, name))
707 0 : r = table_add_cell_stringf(table, NULL, "%s on %s", pretty, name);
708 : else
709 0 : r = table_add_cell_stringf(table, NULL, "%s on %%%i", pretty, local[i].ifindex);
710 : } else
711 0 : r = table_add_cell(table, NULL, TABLE_STRING, pretty);
712 0 : if (r < 0)
713 0 : return r;
714 : }
715 :
716 0 : return 0;
717 : }
718 :
719 0 : static int dump_address_labels(sd_netlink *rtnl) {
720 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
721 0 : _cleanup_(table_unrefp) Table *table = NULL;
722 : sd_netlink_message *m;
723 : TableCell *cell;
724 : int r;
725 :
726 0 : assert(rtnl);
727 :
728 0 : r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
729 0 : if (r < 0)
730 0 : return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
731 :
732 0 : r = sd_netlink_message_request_dump(req, true);
733 0 : if (r < 0)
734 0 : return r;
735 :
736 0 : r = sd_netlink_call(rtnl, req, 0, &reply);
737 0 : if (r < 0)
738 0 : return r;
739 :
740 0 : table = table_new("label", "prefix/prefixlen");
741 0 : if (!table)
742 0 : return -ENOMEM;
743 :
744 0 : r = table_set_sort(table, 0, SIZE_MAX);
745 0 : if (r < 0)
746 0 : return r;
747 :
748 0 : assert_se(cell = table_get_cell(table, 0, 0));
749 0 : (void) table_set_align_percent(table, cell, 100);
750 0 : (void) table_set_ellipsize_percent(table, cell, 100);
751 :
752 0 : assert_se(cell = table_get_cell(table, 0, 1));
753 0 : (void) table_set_align_percent(table, cell, 100);
754 :
755 0 : for (m = reply; m; m = sd_netlink_message_next(m)) {
756 0 : _cleanup_free_ char *pretty = NULL;
757 0 : union in_addr_union prefix = IN_ADDR_NULL;
758 : uint8_t prefixlen;
759 : uint32_t label;
760 :
761 0 : r = sd_netlink_message_get_errno(m);
762 0 : if (r < 0) {
763 0 : log_error_errno(r, "got error: %m");
764 0 : continue;
765 : }
766 :
767 0 : r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
768 0 : if (r < 0 && r != -ENODATA) {
769 0 : log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
770 0 : continue;
771 : }
772 :
773 0 : r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
774 0 : if (r < 0)
775 0 : continue;
776 :
777 0 : r = in_addr_to_string(AF_INET6, &prefix, &pretty);
778 0 : if (r < 0)
779 0 : continue;
780 :
781 0 : r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
782 0 : if (r < 0)
783 0 : continue;
784 :
785 0 : r = table_add_cell(table, NULL, TABLE_UINT32, &label);
786 0 : if (r < 0)
787 0 : return r;
788 :
789 0 : r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
790 0 : if (r < 0)
791 0 : return r;
792 : }
793 :
794 0 : return table_print(table, NULL);
795 : }
796 :
797 0 : static int list_address_labels(int argc, char *argv[], void *userdata) {
798 0 : _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
799 : int r;
800 :
801 0 : r = sd_netlink_open(&rtnl);
802 0 : if (r < 0)
803 0 : return log_error_errno(r, "Failed to connect to netlink: %m");
804 :
805 0 : dump_address_labels(rtnl);
806 :
807 0 : return 0;
808 : }
809 :
810 0 : static int open_lldp_neighbors(int ifindex, FILE **ret) {
811 0 : _cleanup_free_ char *p = NULL;
812 : FILE *f;
813 :
814 0 : if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
815 0 : return -ENOMEM;
816 :
817 0 : f = fopen(p, "re");
818 0 : if (!f)
819 0 : return -errno;
820 :
821 0 : *ret = f;
822 0 : return 0;
823 : }
824 :
825 0 : static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
826 0 : _cleanup_free_ void *raw = NULL;
827 : size_t l;
828 : le64_t u;
829 : int r;
830 :
831 0 : assert(f);
832 0 : assert(ret);
833 :
834 0 : l = fread(&u, 1, sizeof(u), f);
835 0 : if (l == 0 && feof(f))
836 0 : return 0;
837 0 : if (l != sizeof(u))
838 0 : return -EBADMSG;
839 :
840 : /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
841 0 : if (le64toh(u) >= 4096)
842 0 : return -EBADMSG;
843 :
844 0 : raw = new(uint8_t, le64toh(u));
845 0 : if (!raw)
846 0 : return -ENOMEM;
847 :
848 0 : if (fread(raw, 1, le64toh(u), f) != le64toh(u))
849 0 : return -EBADMSG;
850 :
851 0 : r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
852 0 : if (r < 0)
853 0 : return r;
854 :
855 0 : return 1;
856 : }
857 :
858 0 : static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
859 0 : _cleanup_fclose_ FILE *f = NULL;
860 0 : int r, c = 0;
861 :
862 0 : assert(table);
863 0 : assert(prefix);
864 0 : assert(ifindex > 0);
865 :
866 0 : r = open_lldp_neighbors(ifindex, &f);
867 0 : if (r == -ENOENT)
868 0 : return 0;
869 0 : if (r < 0)
870 0 : return r;
871 :
872 0 : for (;;) {
873 0 : const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
874 0 : _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
875 :
876 0 : r = next_lldp_neighbor(f, &n);
877 0 : if (r < 0)
878 0 : return r;
879 0 : if (r == 0)
880 0 : break;
881 :
882 0 : r = table_add_many(table,
883 : TABLE_EMPTY,
884 : TABLE_STRING, c == 0 ? prefix : "");
885 0 : if (r < 0)
886 0 : return r;
887 :
888 0 : (void) sd_lldp_neighbor_get_system_name(n, &system_name);
889 0 : (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
890 0 : (void) sd_lldp_neighbor_get_port_description(n, &port_description);
891 :
892 0 : r = table_add_cell_stringf(table, NULL,
893 : "%s on port %s%s%s%s",
894 : strna(system_name), strna(port_id),
895 0 : isempty(port_description) ? "" : " (",
896 : strempty(port_description),
897 0 : isempty(port_description) ? "" : ")");
898 0 : if (r < 0)
899 0 : return r;
900 :
901 0 : c++;
902 : }
903 :
904 0 : return c;
905 : }
906 :
907 0 : static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
908 : unsigned c;
909 : int r;
910 :
911 0 : assert(prefix);
912 :
913 0 : if (!ifindexes || ifindexes[0] <= 0)
914 0 : return 0;
915 :
916 0 : for (c = 0; ifindexes[c] > 0; c++) {
917 0 : r = table_add_many(table,
918 : TABLE_EMPTY,
919 : TABLE_STRING, c == 0 ? prefix : "",
920 : TABLE_IFINDEX, ifindexes[c]);
921 0 : if (r < 0)
922 0 : return r;
923 : }
924 :
925 0 : return 0;
926 : }
927 :
928 0 : static int dump_list(Table *table, const char *prefix, char **l) {
929 : char **i;
930 : int r;
931 :
932 0 : if (strv_isempty(l))
933 0 : return 0;
934 :
935 0 : STRV_FOREACH(i, l) {
936 0 : r = table_add_many(table,
937 : TABLE_EMPTY,
938 : TABLE_STRING, i == l ? prefix : "",
939 : TABLE_STRING, *i);
940 0 : if (r < 0)
941 0 : return r;
942 : }
943 :
944 0 : return 0;
945 : }
946 :
947 : #define DUMP_STATS_ONE(name, val_name) \
948 : r = table_add_many(table, \
949 : TABLE_EMPTY, \
950 : TABLE_STRING, name ":"); \
951 : if (r < 0) \
952 : return r; \
953 : r = table_add_cell(table, NULL, \
954 : info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
955 : info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
956 : if (r < 0) \
957 : return r;
958 :
959 0 : static int dump_statistics(Table *table, const LinkInfo *info) {
960 : int r;
961 :
962 0 : if (!arg_stats)
963 0 : return 0;
964 :
965 0 : if (!info->has_stats64 && !info->has_stats)
966 0 : return 0;
967 :
968 0 : DUMP_STATS_ONE("Rx Packets", rx_packets);
969 0 : DUMP_STATS_ONE("Tx Packets", tx_packets);
970 0 : DUMP_STATS_ONE("Rx Bytes", rx_bytes);
971 0 : DUMP_STATS_ONE("Tx Bytes", tx_bytes);
972 0 : DUMP_STATS_ONE("Rx Errors", rx_errors);
973 0 : DUMP_STATS_ONE("Tx Errors", tx_errors);
974 0 : DUMP_STATS_ONE("Rx Dropped", rx_dropped);
975 0 : DUMP_STATS_ONE("Tx Dropped", tx_dropped);
976 0 : DUMP_STATS_ONE("Multicast Packets", multicast);
977 0 : DUMP_STATS_ONE("Collisions", collisions);
978 :
979 0 : return 0;
980 : }
981 :
982 0 : static int link_status_one(
983 : sd_netlink *rtnl,
984 : sd_hwdb *hwdb,
985 : const LinkInfo *info) {
986 :
987 0 : _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
988 0 : _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
989 0 : _cleanup_(sd_device_unrefp) sd_device *d = NULL;
990 : char devid[2 + DECIMAL_STR_MAX(int)];
991 0 : _cleanup_free_ char *t = NULL, *network = NULL;
992 0 : const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
993 : const char *on_color_operational, *off_color_operational,
994 : *on_color_setup, *off_color_setup;
995 0 : _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
996 0 : _cleanup_(table_unrefp) Table *table = NULL;
997 : TableCell *cell;
998 : int r;
999 :
1000 0 : assert(rtnl);
1001 0 : assert(info);
1002 :
1003 0 : (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
1004 0 : operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
1005 :
1006 0 : r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
1007 0 : if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
1008 0 : setup_state = strdup("unmanaged");
1009 0 : setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
1010 :
1011 0 : (void) sd_network_link_get_dns(info->ifindex, &dns);
1012 0 : (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
1013 0 : (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
1014 0 : (void) sd_network_link_get_ntp(info->ifindex, &ntp);
1015 :
1016 0 : xsprintf(devid, "n%i", info->ifindex);
1017 :
1018 0 : (void) sd_device_new_from_device_id(&d, devid);
1019 :
1020 0 : if (d) {
1021 0 : (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
1022 0 : (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
1023 0 : (void) sd_device_get_property_value(d, "ID_PATH", &path);
1024 :
1025 0 : if (sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
1026 0 : (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
1027 :
1028 0 : if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) < 0)
1029 0 : (void) sd_device_get_property_value(d, "ID_MODEL", &model);
1030 : }
1031 :
1032 0 : t = link_get_type_string(info->iftype, d);
1033 :
1034 0 : (void) sd_network_link_get_network_file(info->ifindex, &network);
1035 :
1036 0 : (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
1037 0 : (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
1038 :
1039 0 : table = table_new("dot", "key", "value");
1040 0 : if (!table)
1041 0 : return -ENOMEM;
1042 :
1043 0 : assert_se(cell = table_get_cell(table, 0, 0));
1044 0 : (void) table_set_ellipsize_percent(table, cell, 100);
1045 :
1046 0 : assert_se(cell = table_get_cell(table, 0, 1));
1047 0 : (void) table_set_ellipsize_percent(table, cell, 100);
1048 :
1049 0 : table_set_header(table, false);
1050 :
1051 0 : r = table_add_many(table,
1052 : TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1053 : TABLE_SET_COLOR, on_color_operational);
1054 0 : if (r < 0)
1055 0 : return r;
1056 0 : r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
1057 0 : if (r < 0)
1058 0 : return r;
1059 0 : (void) table_set_align_percent(table, cell, 0);
1060 :
1061 0 : r = table_add_many(table,
1062 : TABLE_EMPTY,
1063 : TABLE_EMPTY,
1064 : TABLE_STRING, "Link File:",
1065 : TABLE_SET_ALIGN_PERCENT, 100,
1066 : TABLE_STRING, strna(link),
1067 : TABLE_EMPTY,
1068 : TABLE_STRING, "Network File:",
1069 : TABLE_STRING, strna(network),
1070 : TABLE_EMPTY,
1071 : TABLE_STRING, "Type:",
1072 : TABLE_STRING, strna(t),
1073 : TABLE_EMPTY,
1074 : TABLE_STRING, "State:");
1075 0 : if (r < 0)
1076 0 : return r;
1077 0 : r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
1078 : on_color_operational, strna(operational_state), off_color_operational,
1079 : on_color_setup, strna(setup_state), off_color_setup);
1080 0 : if (r < 0)
1081 0 : return r;
1082 :
1083 0 : if (path) {
1084 0 : r = table_add_many(table,
1085 : TABLE_EMPTY,
1086 : TABLE_STRING, "Path:",
1087 : TABLE_STRING, path);
1088 0 : if (r < 0)
1089 0 : return r;
1090 : }
1091 0 : if (driver) {
1092 0 : r = table_add_many(table,
1093 : TABLE_EMPTY,
1094 : TABLE_STRING, "Driver:",
1095 : TABLE_STRING, driver);
1096 0 : if (r < 0)
1097 0 : return r;
1098 : }
1099 0 : if (vendor) {
1100 0 : r = table_add_many(table,
1101 : TABLE_EMPTY,
1102 : TABLE_STRING, "Vendor:",
1103 : TABLE_STRING, vendor);
1104 0 : if (r < 0)
1105 0 : return r;
1106 : }
1107 0 : if (model) {
1108 0 : r = table_add_many(table,
1109 : TABLE_EMPTY,
1110 : TABLE_STRING, "Model:",
1111 : TABLE_STRING, model);
1112 0 : if (r < 0)
1113 0 : return r;
1114 : }
1115 :
1116 0 : if (info->has_mac_address) {
1117 0 : _cleanup_free_ char *description = NULL;
1118 : char ea[ETHER_ADDR_TO_STRING_MAX];
1119 :
1120 0 : (void) ieee_oui(hwdb, &info->mac_address, &description);
1121 :
1122 0 : r = table_add_many(table,
1123 : TABLE_EMPTY,
1124 : TABLE_STRING, "HW Address:");
1125 0 : if (r < 0)
1126 0 : return r;
1127 0 : r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
1128 : ether_addr_to_string(&info->mac_address, ea),
1129 0 : description ? " (" : "",
1130 : strempty(description),
1131 0 : description ? ")" : "");
1132 0 : if (r < 0)
1133 0 : return r;
1134 : }
1135 :
1136 0 : if (info->mtu > 0) {
1137 : char min_str[DECIMAL_STR_MAX(uint32_t)], max_str[DECIMAL_STR_MAX(uint32_t)];
1138 :
1139 0 : xsprintf(min_str, "%" PRIu32, info->min_mtu);
1140 0 : xsprintf(max_str, "%" PRIu32, info->max_mtu);
1141 :
1142 0 : r = table_add_many(table,
1143 : TABLE_EMPTY,
1144 : TABLE_STRING, "MTU:");
1145 0 : if (r < 0)
1146 0 : return r;
1147 0 : r = table_add_cell_stringf(table, NULL, "%" PRIu32 "%s%s%s%s%s%s%s",
1148 : info->mtu,
1149 0 : info->min_mtu > 0 || info->max_mtu > 0 ? " (" : "",
1150 0 : info->min_mtu > 0 ? "min: " : "",
1151 0 : info->min_mtu > 0 ? min_str : "",
1152 0 : info->min_mtu > 0 && info->max_mtu > 0 ? ", " : "",
1153 0 : info->max_mtu > 0 ? "max: " : "",
1154 0 : info->max_mtu > 0 ? max_str : "",
1155 0 : info->min_mtu > 0 || info->max_mtu > 0 ? ")" : "");
1156 0 : if (r < 0)
1157 0 : return r;
1158 : }
1159 :
1160 0 : if (streq_ptr(info->netdev_kind, "bridge")) {
1161 0 : r = table_add_many(table,
1162 : TABLE_EMPTY,
1163 : TABLE_STRING, "Forward Delay:",
1164 : TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->forward_delay),
1165 : TABLE_EMPTY,
1166 : TABLE_STRING, "Hello Time:",
1167 : TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->hello_time),
1168 : TABLE_EMPTY,
1169 : TABLE_STRING, "Max Age:",
1170 : TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->max_age),
1171 : TABLE_EMPTY,
1172 : TABLE_STRING, "Ageing Time:",
1173 : TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->ageing_time),
1174 : TABLE_EMPTY,
1175 : TABLE_STRING, "Priority:",
1176 : TABLE_UINT16, info->priority,
1177 : TABLE_EMPTY,
1178 : TABLE_STRING, "STP:",
1179 : TABLE_BOOLEAN, info->stp_state > 0,
1180 : TABLE_EMPTY,
1181 : TABLE_STRING, "Multicast IGMP Version:",
1182 : TABLE_UINT8, info->mcast_igmp_version);
1183 0 : if (r < 0)
1184 0 : return r;
1185 :
1186 0 : } else if (streq_ptr(info->netdev_kind, "vxlan")) {
1187 0 : if (info->vxlan_info.vni > 0) {
1188 0 : r = table_add_many(table,
1189 : TABLE_EMPTY,
1190 : TABLE_STRING, "VNI:",
1191 : TABLE_UINT32, info->vxlan_info.vni);
1192 0 : if (r < 0)
1193 0 : return r;
1194 : }
1195 :
1196 0 : if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
1197 0 : r = table_add_many(table,
1198 : TABLE_EMPTY,
1199 : TABLE_STRING, "Group:",
1200 : info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1201 : &info->vxlan_info.group);
1202 0 : if (r < 0)
1203 0 : return r;
1204 : }
1205 :
1206 0 : if (IN_SET(info->vxlan_info.local_family, AF_INET, AF_INET6)) {
1207 0 : r = table_add_many(table,
1208 : TABLE_EMPTY,
1209 : TABLE_STRING, "Local:",
1210 : info->vxlan_info.local_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
1211 : &info->vxlan_info.local);
1212 0 : if (r < 0)
1213 0 : return r;
1214 : }
1215 :
1216 0 : if (info->vxlan_info.dest_port > 0) {
1217 0 : r = table_add_many(table,
1218 : TABLE_EMPTY,
1219 : TABLE_STRING, "Destination Port:",
1220 : TABLE_UINT16, be16toh(info->vxlan_info.dest_port));
1221 0 : if (r < 0)
1222 0 : return r;
1223 : }
1224 :
1225 0 : if (info->vxlan_info.link > 0) {
1226 0 : r = table_add_many(table,
1227 : TABLE_EMPTY,
1228 : TABLE_STRING, "Underlying Device:",
1229 : TABLE_IFINDEX, info->vxlan_info.link);
1230 0 : if (r < 0)
1231 0 : return r;
1232 : }
1233 : }
1234 :
1235 0 : if (info->has_bitrates) {
1236 : char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX];
1237 :
1238 0 : r = table_add_many(table,
1239 : TABLE_EMPTY,
1240 : TABLE_STRING, "Bit Rate (Tx/Rx):");
1241 0 : if (r < 0)
1242 0 : return r;
1243 0 : r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
1244 : format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0),
1245 : format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0));
1246 0 : if (r < 0)
1247 0 : return r;
1248 : }
1249 :
1250 0 : if (info->has_tx_queues || info->has_rx_queues) {
1251 0 : r = table_add_many(table,
1252 : TABLE_EMPTY,
1253 : TABLE_STRING, "Queue Length (Tx/Rx):");
1254 0 : if (r < 0)
1255 0 : return r;
1256 0 : r = table_add_cell_stringf(table, NULL, "%" PRIu32 "/%" PRIu32, info->tx_queues, info->rx_queues);
1257 0 : if (r < 0)
1258 0 : return r;
1259 : }
1260 :
1261 0 : if (info->has_ethtool_link_info) {
1262 0 : const char *duplex = duplex_to_string(info->duplex);
1263 0 : const char *port = port_to_string(info->port);
1264 :
1265 0 : if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
1266 0 : r = table_add_many(table,
1267 : TABLE_EMPTY,
1268 : TABLE_STRING, "Auto negotiation:",
1269 : TABLE_BOOLEAN, info->autonegotiation == AUTONEG_ENABLE);
1270 0 : if (r < 0)
1271 0 : return r;
1272 : }
1273 :
1274 0 : if (info->speed > 0) {
1275 0 : r = table_add_many(table,
1276 : TABLE_EMPTY,
1277 : TABLE_STRING, "Speed:",
1278 : TABLE_BPS, (uint64_t) info->speed);
1279 0 : if (r < 0)
1280 0 : return r;
1281 : }
1282 :
1283 0 : if (duplex) {
1284 0 : r = table_add_many(table,
1285 : TABLE_EMPTY,
1286 : TABLE_STRING, "Duplex:",
1287 : TABLE_STRING, duplex);
1288 0 : if (r < 0)
1289 0 : return r;
1290 : }
1291 :
1292 0 : if (port) {
1293 0 : r = table_add_many(table,
1294 : TABLE_EMPTY,
1295 : TABLE_STRING, "Port:",
1296 : TABLE_STRING, port);
1297 0 : if (r < 0)
1298 0 : return r;
1299 : }
1300 : }
1301 :
1302 0 : r = dump_addresses(rtnl, table, info->ifindex);
1303 0 : if (r < 0)
1304 0 : return r;
1305 0 : r = dump_gateways(rtnl, hwdb, table, info->ifindex);
1306 0 : if (r < 0)
1307 0 : return r;
1308 0 : r = dump_list(table, "DNS:", dns);
1309 0 : if (r < 0)
1310 0 : return r;
1311 0 : r = dump_list(table, "Search Domains:", search_domains);
1312 0 : if (r < 0)
1313 0 : return r;
1314 0 : r = dump_list(table, "Route Domains:", route_domains);
1315 0 : if (r < 0)
1316 0 : return r;
1317 0 : r = dump_list(table, "NTP:", ntp);
1318 0 : if (r < 0)
1319 0 : return r;
1320 0 : r = dump_ifindexes(table, "Carrier Bound To:", carrier_bound_to);
1321 0 : if (r < 0)
1322 0 : return r;
1323 0 : r = dump_ifindexes(table, "Carrier Bound By:", carrier_bound_by);
1324 0 : if (r < 0)
1325 0 : return r;
1326 :
1327 0 : (void) sd_network_link_get_timezone(info->ifindex, &tz);
1328 0 : if (tz) {
1329 0 : r = table_add_many(table,
1330 : TABLE_EMPTY,
1331 : TABLE_STRING, "Time Zone:",
1332 : TABLE_STRING, tz);
1333 0 : if (r < 0)
1334 0 : return r;
1335 : }
1336 :
1337 0 : r = dump_lldp_neighbors(table, "Connected To:", info->ifindex);
1338 0 : if (r < 0)
1339 0 : return r;
1340 :
1341 0 : r = dump_statistics(table, info);
1342 0 : if (r < 0)
1343 0 : return r;
1344 :
1345 0 : return table_print(table, NULL);
1346 : }
1347 :
1348 0 : static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
1349 0 : _cleanup_free_ char *operational_state = NULL;
1350 0 : _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
1351 : const char *on_color_operational, *off_color_operational;
1352 0 : _cleanup_(table_unrefp) Table *table = NULL;
1353 : TableCell *cell;
1354 : int r;
1355 :
1356 0 : assert(rtnl);
1357 :
1358 0 : (void) sd_network_get_operational_state(&operational_state);
1359 0 : operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
1360 :
1361 0 : table = table_new("dot", "key", "value");
1362 0 : if (!table)
1363 0 : return -ENOMEM;
1364 :
1365 0 : assert_se(cell = table_get_cell(table, 0, 0));
1366 0 : (void) table_set_ellipsize_percent(table, cell, 100);
1367 :
1368 0 : assert_se(cell = table_get_cell(table, 0, 1));
1369 0 : (void) table_set_align_percent(table, cell, 100);
1370 0 : (void) table_set_ellipsize_percent(table, cell, 100);
1371 :
1372 0 : table_set_header(table, false);
1373 :
1374 0 : r = table_add_many(table,
1375 : TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
1376 : TABLE_SET_COLOR, on_color_operational,
1377 : TABLE_STRING, "State:",
1378 : TABLE_STRING, strna(operational_state),
1379 : TABLE_SET_COLOR, on_color_operational);
1380 :
1381 0 : r = dump_addresses(rtnl, table, 0);
1382 0 : if (r < 0)
1383 0 : return r;
1384 0 : r = dump_gateways(rtnl, hwdb, table, 0);
1385 0 : if (r < 0)
1386 0 : return r;
1387 :
1388 0 : (void) sd_network_get_dns(&dns);
1389 0 : r = dump_list(table, "DNS:", dns);
1390 0 : if (r < 0)
1391 0 : return r;
1392 :
1393 0 : (void) sd_network_get_search_domains(&search_domains);
1394 0 : r = dump_list(table, "Search Domains:", search_domains);
1395 0 : if (r < 0)
1396 0 : return r;
1397 :
1398 0 : (void) sd_network_get_route_domains(&route_domains);
1399 0 : r = dump_list(table, "Route Domains:", route_domains);
1400 0 : if (r < 0)
1401 0 : return r;
1402 :
1403 0 : (void) sd_network_get_ntp(&ntp);
1404 0 : r = dump_list(table, "NTP:", ntp);
1405 0 : if (r < 0)
1406 0 : return r;
1407 :
1408 0 : return table_print(table, NULL);
1409 : }
1410 :
1411 0 : static int link_status(int argc, char *argv[], void *userdata) {
1412 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1413 0 : _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1414 0 : _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
1415 0 : _cleanup_free_ LinkInfo *links = NULL;
1416 : int r, c, i;
1417 :
1418 0 : (void) pager_open(arg_pager_flags);
1419 :
1420 0 : r = sd_bus_open_system(&bus);
1421 0 : if (r < 0)
1422 0 : return log_error_errno(r, "Failed to connect system bus: %m");
1423 :
1424 0 : r = sd_netlink_open(&rtnl);
1425 0 : if (r < 0)
1426 0 : return log_error_errno(r, "Failed to connect to netlink: %m");
1427 :
1428 0 : r = sd_hwdb_new(&hwdb);
1429 0 : if (r < 0)
1430 0 : log_debug_errno(r, "Failed to open hardware database: %m");
1431 :
1432 0 : if (arg_all)
1433 0 : c = acquire_link_info(bus, rtnl, NULL, &links);
1434 0 : else if (argc <= 1)
1435 0 : return system_status(rtnl, hwdb);
1436 : else
1437 0 : c = acquire_link_info(bus, rtnl, argv + 1, &links);
1438 0 : if (c < 0)
1439 0 : return c;
1440 :
1441 0 : for (i = 0; i < c; i++) {
1442 0 : if (i > 0)
1443 0 : fputc('\n', stdout);
1444 :
1445 0 : link_status_one(rtnl, hwdb, links + i);
1446 : }
1447 :
1448 0 : return 0;
1449 : }
1450 :
1451 0 : static char *lldp_capabilities_to_string(uint16_t x) {
1452 : static const char characters[] = {
1453 : 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
1454 : };
1455 : char *ret;
1456 : unsigned i;
1457 :
1458 0 : ret = new(char, ELEMENTSOF(characters) + 1);
1459 0 : if (!ret)
1460 0 : return NULL;
1461 :
1462 0 : for (i = 0; i < ELEMENTSOF(characters); i++)
1463 0 : ret[i] = (x & (1U << i)) ? characters[i] : '.';
1464 :
1465 0 : ret[i] = 0;
1466 0 : return ret;
1467 : }
1468 :
1469 0 : static void lldp_capabilities_legend(uint16_t x) {
1470 0 : unsigned w, i, cols = columns();
1471 : static const char* const table[] = {
1472 : "o - Other",
1473 : "p - Repeater",
1474 : "b - Bridge",
1475 : "w - WLAN Access Point",
1476 : "r - Router",
1477 : "t - Telephone",
1478 : "d - DOCSIS cable device",
1479 : "a - Station",
1480 : "c - Customer VLAN",
1481 : "s - Service VLAN",
1482 : "m - Two-port MAC Relay (TPMR)",
1483 : };
1484 :
1485 0 : if (x == 0)
1486 0 : return;
1487 :
1488 0 : printf("\nCapability Flags:\n");
1489 0 : for (w = 0, i = 0; i < ELEMENTSOF(table); i++)
1490 0 : if (x & (1U << i) || arg_all) {
1491 : bool newline;
1492 :
1493 0 : newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols;
1494 0 : if (newline)
1495 0 : w = 0;
1496 0 : w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]);
1497 : }
1498 0 : puts("");
1499 : }
1500 :
1501 0 : static int link_lldp_status(int argc, char *argv[], void *userdata) {
1502 0 : _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1503 0 : _cleanup_free_ LinkInfo *links = NULL;
1504 0 : _cleanup_(table_unrefp) Table *table = NULL;
1505 0 : int i, r, c, m = 0;
1506 0 : uint16_t all = 0;
1507 : TableCell *cell;
1508 :
1509 0 : r = sd_netlink_open(&rtnl);
1510 0 : if (r < 0)
1511 0 : return log_error_errno(r, "Failed to connect to netlink: %m");
1512 :
1513 0 : c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
1514 0 : if (c < 0)
1515 0 : return c;
1516 :
1517 0 : (void) pager_open(arg_pager_flags);
1518 :
1519 0 : table = table_new("link",
1520 : "chassis id",
1521 : "system name",
1522 : "caps",
1523 : "port id",
1524 : "port description");
1525 0 : if (!table)
1526 0 : return -ENOMEM;
1527 :
1528 0 : table_set_header(table, arg_legend);
1529 :
1530 0 : assert_se(cell = table_get_cell(table, 0, 0));
1531 0 : table_set_minimum_width(table, cell, 16);
1532 :
1533 0 : assert_se(cell = table_get_cell(table, 0, 1));
1534 0 : table_set_minimum_width(table, cell, 17);
1535 :
1536 0 : assert_se(cell = table_get_cell(table, 0, 2));
1537 0 : table_set_minimum_width(table, cell, 16);
1538 :
1539 0 : assert_se(cell = table_get_cell(table, 0, 3));
1540 0 : table_set_minimum_width(table, cell, 11);
1541 :
1542 0 : assert_se(cell = table_get_cell(table, 0, 4));
1543 0 : table_set_minimum_width(table, cell, 17);
1544 :
1545 0 : assert_se(cell = table_get_cell(table, 0, 5));
1546 0 : table_set_minimum_width(table, cell, 16);
1547 :
1548 0 : for (i = 0; i < c; i++) {
1549 0 : _cleanup_fclose_ FILE *f = NULL;
1550 :
1551 0 : r = open_lldp_neighbors(links[i].ifindex, &f);
1552 0 : if (r == -ENOENT)
1553 0 : continue;
1554 0 : if (r < 0) {
1555 0 : log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
1556 0 : continue;
1557 : }
1558 :
1559 0 : for (;;) {
1560 0 : _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL, *capabilities = NULL;
1561 0 : const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
1562 0 : _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1563 : uint16_t cc;
1564 :
1565 0 : r = next_lldp_neighbor(f, &n);
1566 0 : if (r < 0) {
1567 0 : log_warning_errno(r, "Failed to read neighbor data: %m");
1568 0 : break;
1569 : }
1570 0 : if (r == 0)
1571 0 : break;
1572 :
1573 0 : (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
1574 0 : (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
1575 0 : (void) sd_lldp_neighbor_get_system_name(n, &system_name);
1576 0 : (void) sd_lldp_neighbor_get_port_description(n, &port_description);
1577 :
1578 0 : if (chassis_id) {
1579 0 : cid = ellipsize(chassis_id, 17, 100);
1580 0 : if (cid)
1581 0 : chassis_id = cid;
1582 : }
1583 :
1584 0 : if (port_id) {
1585 0 : pid = ellipsize(port_id, 17, 100);
1586 0 : if (pid)
1587 0 : port_id = pid;
1588 : }
1589 :
1590 0 : if (system_name) {
1591 0 : sname = ellipsize(system_name, 16, 100);
1592 0 : if (sname)
1593 0 : system_name = sname;
1594 : }
1595 :
1596 0 : if (port_description) {
1597 0 : pdesc = ellipsize(port_description, 16, 100);
1598 0 : if (pdesc)
1599 0 : port_description = pdesc;
1600 : }
1601 :
1602 0 : if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
1603 0 : capabilities = lldp_capabilities_to_string(cc);
1604 0 : all |= cc;
1605 : }
1606 :
1607 0 : r = table_add_many(table,
1608 : TABLE_STRING, links[i].name,
1609 : TABLE_STRING, strna(chassis_id),
1610 : TABLE_STRING, strna(system_name),
1611 : TABLE_STRING, strna(capabilities),
1612 : TABLE_STRING, strna(port_id),
1613 : TABLE_STRING, strna(port_description));
1614 0 : if (r < 0)
1615 0 : return r;
1616 :
1617 0 : m++;
1618 : }
1619 : }
1620 :
1621 0 : r = table_print(table, NULL);
1622 0 : if (r < 0)
1623 0 : return r;
1624 :
1625 0 : if (arg_legend) {
1626 0 : lldp_capabilities_legend(all);
1627 0 : printf("\n%i neighbors listed.\n", m);
1628 : }
1629 :
1630 0 : return 0;
1631 : }
1632 :
1633 0 : static int link_delete_send_message(sd_netlink *rtnl, int index) {
1634 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
1635 : int r;
1636 :
1637 0 : assert(rtnl);
1638 :
1639 0 : r = sd_rtnl_message_new_link(rtnl, &req, RTM_DELLINK, index);
1640 0 : if (r < 0)
1641 0 : return rtnl_log_create_error(r);
1642 :
1643 0 : r = sd_netlink_call(rtnl, req, 0, NULL);
1644 0 : if (r < 0)
1645 0 : return r;
1646 :
1647 0 : return 0;
1648 : }
1649 :
1650 0 : static int link_delete(int argc, char *argv[], void *userdata) {
1651 0 : _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1652 0 : _cleanup_set_free_ Set *indexes = NULL;
1653 : int index, r, i;
1654 : Iterator j;
1655 : void *p;
1656 :
1657 0 : r = sd_netlink_open(&rtnl);
1658 0 : if (r < 0)
1659 0 : return log_error_errno(r, "Failed to connect to netlink: %m");
1660 :
1661 0 : indexes = set_new(NULL);
1662 0 : if (!indexes)
1663 0 : return log_oom();
1664 :
1665 0 : for (i = 1; i < argc; i++) {
1666 0 : r = parse_ifindex_or_ifname(argv[i], &index);
1667 0 : if (r < 0)
1668 0 : return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
1669 :
1670 0 : r = set_put(indexes, INT_TO_PTR(index));
1671 0 : if (r < 0)
1672 0 : return log_oom();
1673 : }
1674 :
1675 0 : SET_FOREACH(p, indexes, j) {
1676 0 : r = link_delete_send_message(rtnl, PTR_TO_INT(p));
1677 0 : if (r < 0) {
1678 : char ifname[IF_NAMESIZE + 1];
1679 :
1680 0 : if (format_ifname(index, ifname))
1681 0 : return log_error_errno(r, "Failed to delete interface %s: %m", ifname);
1682 : else
1683 0 : return log_error_errno(r, "Failed to delete interface %d: %m", index);
1684 : }
1685 : }
1686 :
1687 0 : return r;
1688 : }
1689 :
1690 3 : static int help(void) {
1691 3 : _cleanup_free_ char *link = NULL;
1692 : int r;
1693 :
1694 3 : r = terminal_urlify_man("networkctl", "1", &link);
1695 3 : if (r < 0)
1696 0 : return log_oom();
1697 :
1698 3 : printf("%s [OPTIONS...]\n\n"
1699 : "Query and control the networking subsystem.\n\n"
1700 : " -h --help Show this help\n"
1701 : " --version Show package version\n"
1702 : " --no-pager Do not pipe output into a pager\n"
1703 : " --no-legend Do not show the headers and footers\n"
1704 : " -a --all Show status for all links\n"
1705 : " -s --stats Show detailed link statics\n"
1706 : "\nCommands:\n"
1707 : " list [PATTERN...] List links\n"
1708 : " status [PATTERN...] Show link status\n"
1709 : " lldp [PATTERN...] Show LLDP neighbors\n"
1710 : " label Show current address label entries in the kernel\n"
1711 : " delete DEVICES Delete virtual netdevs\n"
1712 : "\nSee the %s for details.\n"
1713 : , program_invocation_short_name
1714 : , link
1715 : );
1716 :
1717 3 : return 0;
1718 : }
1719 :
1720 4 : static int parse_argv(int argc, char *argv[]) {
1721 :
1722 : enum {
1723 : ARG_VERSION = 0x100,
1724 : ARG_NO_PAGER,
1725 : ARG_NO_LEGEND,
1726 : };
1727 :
1728 : static const struct option options[] = {
1729 : { "help", no_argument, NULL, 'h' },
1730 : { "version", no_argument, NULL, ARG_VERSION },
1731 : { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1732 : { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1733 : { "all", no_argument, NULL, 'a' },
1734 : { "stats", no_argument, NULL, 's' },
1735 : {}
1736 : };
1737 :
1738 : int c;
1739 :
1740 4 : assert(argc >= 0);
1741 4 : assert(argv);
1742 :
1743 4 : while ((c = getopt_long(argc, argv, "has", options, NULL)) >= 0) {
1744 :
1745 4 : switch (c) {
1746 :
1747 3 : case 'h':
1748 3 : return help();
1749 :
1750 0 : case ARG_VERSION:
1751 0 : return version();
1752 :
1753 0 : case ARG_NO_PAGER:
1754 0 : arg_pager_flags |= PAGER_DISABLE;
1755 0 : break;
1756 :
1757 0 : case ARG_NO_LEGEND:
1758 0 : arg_legend = false;
1759 0 : break;
1760 :
1761 0 : case 'a':
1762 0 : arg_all = true;
1763 0 : break;
1764 :
1765 0 : case 's':
1766 0 : arg_stats = true;
1767 0 : break;
1768 :
1769 1 : case '?':
1770 1 : return -EINVAL;
1771 :
1772 0 : default:
1773 0 : assert_not_reached("Unhandled option");
1774 : }
1775 : }
1776 :
1777 0 : return 1;
1778 : }
1779 :
1780 0 : static int networkctl_main(int argc, char *argv[]) {
1781 : static const Verb verbs[] = {
1782 : { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
1783 : { "status", VERB_ANY, VERB_ANY, 0, link_status },
1784 : { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
1785 : { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
1786 : { "delete", 2, VERB_ANY, 0, link_delete },
1787 : {}
1788 : };
1789 :
1790 0 : return dispatch_verb(argc, argv, verbs, NULL);
1791 : }
1792 :
1793 0 : static void warn_networkd_missing(void) {
1794 :
1795 0 : if (access("/run/systemd/netif/state", F_OK) >= 0)
1796 0 : return;
1797 :
1798 0 : fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1799 : }
1800 :
1801 4 : static int run(int argc, char* argv[]) {
1802 : int r;
1803 :
1804 4 : log_show_color(true);
1805 4 : log_parse_environment();
1806 4 : log_open();
1807 :
1808 4 : r = parse_argv(argc, argv);
1809 4 : if (r <= 0)
1810 4 : return r;
1811 :
1812 0 : warn_networkd_missing();
1813 :
1814 0 : return networkctl_main(argc, argv);
1815 : }
1816 :
1817 4 : DEFINE_MAIN_FUNCTION(run);
|