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