Branch data 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 : 12 : static int help(void) {
1691 : 12 : _cleanup_free_ char *link = NULL;
1692 : : int r;
1693 : :
1694 : 12 : r = terminal_urlify_man("networkctl", "1", &link);
1695 [ - + ]: 12 : if (r < 0)
1696 : 0 : return log_oom();
1697 : :
1698 : 12 : 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 : 12 : return 0;
1718 : : }
1719 : :
1720 : 16 : 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 [ - + ]: 16 : assert(argc >= 0);
1741 [ - + ]: 16 : assert(argv);
1742 : :
1743 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "has", options, NULL)) >= 0) {
1744 : :
1745 [ + - - - : 16 : switch (c) {
- - + - ]
1746 : :
1747 : 12 : case 'h':
1748 : 12 : 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 : 4 : case '?':
1770 : 4 : 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 : 16 : static int run(int argc, char* argv[]) {
1802 : : int r;
1803 : :
1804 : 16 : log_show_color(true);
1805 : 16 : log_parse_environment();
1806 : 16 : log_open();
1807 : :
1808 : 16 : r = parse_argv(argc, argv);
1809 [ + - ]: 16 : if (r <= 0)
1810 : 16 : return r;
1811 : :
1812 : 0 : warn_networkd_missing();
1813 : :
1814 : 0 : return networkctl_main(argc, argv);
1815 : : }
1816 : :
1817 : 16 : DEFINE_MAIN_FUNCTION(run);
|