]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
sd-netlink: Add netlink property IFLA_MIN_MTU and IFLA_MAX_MTU,
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
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
914d6c09 11#include "sd-device.h"
3f6fd1ba 12#include "sd-hwdb.h"
34437b4f 13#include "sd-lldp.h"
3f6fd1ba
LP
14#include "sd-netlink.h"
15#include "sd-network.h"
ee8c4568 16
b5efdb8a 17#include "alloc-util.h"
3f6fd1ba 18#include "arphrd-list.h"
914d6c09 19#include "device-util.h"
3f6fd1ba 20#include "ether-addr-util.h"
34437b4f 21#include "fd-util.h"
81fd1dd3 22#include "hwdb-util.h"
ee8c4568 23#include "local-addresses.h"
8752c575 24#include "locale-util.h"
d37b7627 25#include "macro.h"
4e2ca442 26#include "main-func.h"
3f6fd1ba
LP
27#include "netlink-util.h"
28#include "pager.h"
6bedfcbb 29#include "parse-util.h"
294bf0c3 30#include "pretty-print.h"
db73295a 31#include "socket-util.h"
760877e9 32#include "sort-util.h"
34437b4f 33#include "sparse-endian.h"
d054f0a4 34#include "stdio-util.h"
8b43440b 35#include "string-table.h"
8752c575 36#include "string-util.h"
3f6fd1ba 37#include "strv.h"
2388b2f4 38#include "strxcpyx.h"
288a74cc 39#include "terminal-util.h"
3f6fd1ba 40#include "verbs.h"
ee8c4568 41
0221d68a 42static PagerFlags arg_pager_flags = 0;
ee8c4568 43static bool arg_legend = true;
9085f64a 44static bool arg_all = false;
ee8c4568 45
b55822c3 46static char *link_get_type_string(unsigned short iftype, sd_device *d) {
2c740afd 47 const char *t, *devtype;
ee8c4568
LP
48 char *p;
49
2c740afd
YW
50 if (d &&
51 sd_device_get_devtype(d, &devtype) >= 0 &&
52 !isempty(devtype))
53 return strdup(devtype);
ee8c4568
LP
54
55 t = arphrd_to_name(iftype);
b55822c3
JD
56 if (!t)
57 return NULL;
ee8c4568
LP
58
59 p = strdup(t);
60 if (!p)
b55822c3 61 return NULL;
ee8c4568
LP
62
63 ascii_strlower(p);
b55822c3 64 return p;
ee8c4568
LP
65}
66
7e5a080a
LP
67static void operational_state_to_color(const char *state, const char **on, const char **off) {
68 assert(on);
69 assert(off);
70
85323805 71 if (STRPTR_IN_SET(state, "routable", "enslaved")) {
7e5a080a
LP
72 *on = ansi_highlight_green();
73 *off = ansi_normal();
74 } else if (streq_ptr(state, "degraded")) {
75 *on = ansi_highlight_yellow();
76 *off = ansi_normal();
77 } else
78 *on = *off = "";
79}
80
81static void setup_state_to_color(const char *state, const char **on, const char **off) {
82 assert(on);
83 assert(off);
84
85 if (streq_ptr(state, "configured")) {
86 *on = ansi_highlight_green();
87 *off = ansi_normal();
88 } else if (streq_ptr(state, "configuring")) {
89 *on = ansi_highlight_yellow();
90 *off = ansi_normal();
1cf03a4f 91 } else if (STRPTR_IN_SET(state, "failed", "linger")) {
7e5a080a
LP
92 *on = ansi_highlight_red();
93 *off = ansi_normal();
94 } else
95 *on = *off = "";
96}
97
6d0c65ff 98typedef struct LinkInfo {
e997c4b0 99 char name[IFNAMSIZ+1];
6d0c65ff 100 int ifindex;
1c4a6088 101 unsigned short iftype;
b147503e
LP
102 struct ether_addr mac_address;
103 uint32_t mtu;
104
105 bool has_mac_address:1;
106 bool has_mtu:1;
6d0c65ff
LP
107} LinkInfo;
108
93bab288
YW
109static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
110 return CMP(a->ifindex, b->ifindex);
6d0c65ff
LP
111}
112
a6962904 113static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
e997c4b0 114 const char *name;
e997c4b0 115 uint16_t type;
a6962904 116 int ifindex, r;
6d0c65ff 117
e997c4b0
LP
118 assert(m);
119 assert(info);
6d0c65ff 120
e997c4b0
LP
121 r = sd_netlink_message_get_type(m, &type);
122 if (r < 0)
123 return r;
6d0c65ff 124
e997c4b0
LP
125 if (type != RTM_NEWLINK)
126 return 0;
6d0c65ff 127
a6962904 128 r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
e997c4b0
LP
129 if (r < 0)
130 return r;
6d0c65ff 131
e997c4b0
LP
132 r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name);
133 if (r < 0)
134 return r;
6d0c65ff 135
a6962904
YW
136 if (patterns) {
137 char str[DECIMAL_STR_MAX(int)];
138
139 xsprintf(str, "%i", ifindex);
140
141 if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0))
142 return 0;
143 }
144
b147503e 145 r = sd_rtnl_message_link_get_type(m, &info->iftype);
e997c4b0
LP
146 if (r < 0)
147 return r;
6d0c65ff 148
2388b2f4 149 strscpy(info->name, sizeof info->name, name);
a6962904 150 info->ifindex = ifindex;
b147503e
LP
151
152 info->has_mac_address =
153 sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 &&
72e551f4 154 memcmp(&info->mac_address, &ETHER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
b147503e
LP
155
156 info->has_mtu =
4015d106 157 sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) >= 0 &&
b147503e 158 info->mtu > 0;
e997c4b0
LP
159
160 return 1;
161}
162
a6962904 163static int acquire_link_info(sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
4afd3348 164 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
7e5a080a
LP
165 _cleanup_free_ LinkInfo *links = NULL;
166 size_t allocated = 0, c = 0;
167 sd_netlink_message *i;
7d367b45
LP
168 int r;
169
b147503e 170 assert(rtnl);
7d367b45 171 assert(ret);
ee8c4568 172
ee8c4568
LP
173 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
174 if (r < 0)
175 return rtnl_log_create_error(r);
176
1c4baffc 177 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
178 if (r < 0)
179 return rtnl_log_create_error(r);
180
1c4baffc 181 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
182 if (r < 0)
183 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 184
7e5a080a
LP
185 for (i = reply; i; i = sd_netlink_message_next(i)) {
186 if (!GREEDY_REALLOC(links, allocated, c+1))
187 return -ENOMEM;
7d367b45 188
a6962904 189 r = decode_link(i, links + c, patterns);
7e5a080a
LP
190 if (r < 0)
191 return r;
192 if (r > 0)
193 c++;
194 }
195
93bab288 196 typesafe_qsort(links, c, link_info_compare);
7e5a080a 197
1cc6c93a 198 *ret = TAKE_PTR(links);
7e5a080a
LP
199
200 return (int) c;
7d367b45 201}
ee8c4568 202
7d367b45 203static int list_links(int argc, char *argv[], void *userdata) {
b147503e 204 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
7d367b45 205 _cleanup_free_ LinkInfo *links = NULL;
b147503e
LP
206 int c, i, r;
207
208 r = sd_netlink_open(&rtnl);
209 if (r < 0)
210 return log_error_errno(r, "Failed to connect to netlink: %m");
7d367b45 211
a6962904 212 c = acquire_link_info(rtnl, argc > 1 ? argv + 1 : NULL, &links);
6d0c65ff 213 if (c < 0)
7d367b45
LP
214 return c;
215
0221d68a 216 (void) pager_open(arg_pager_flags);
7d367b45
LP
217
218 if (arg_legend)
c9cc0383 219 printf("%3s %-16s %-18s %-16s %-10s\n",
7d367b45
LP
220 "IDX",
221 "LINK",
222 "TYPE",
223 "OPERATIONAL",
224 "SETUP");
6d0c65ff
LP
225
226 for (i = 0; i < c; i++) {
ab1525bc 227 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
4afd3348 228 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
d57c365b
LP
229 const char *on_color_operational, *off_color_operational,
230 *on_color_setup, *off_color_setup;
7d6884b6 231 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 232 _cleanup_free_ char *t = NULL;
ee8c4568 233
4abd866d 234 (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
235 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
236
33d5013d
LP
237 r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
238 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
239 setup_state = strdup("unmanaged");
d57c365b 240 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
ee8c4568 241
691d4da9
LP
242 xsprintf(devid, "n%i", links[i].ifindex);
243 (void) sd_device_new_from_device_id(&d, devid);
ee8c4568 244
b55822c3 245 t = link_get_type_string(links[i].iftype, d);
ee8c4568 246
c9cc0383 247 printf("%3i %-16s %-18s %s%-16s%s %s%-10s%s\n",
d57c365b
LP
248 links[i].ifindex, links[i].name, strna(t),
249 on_color_operational, strna(operational_state), off_color_operational,
250 on_color_setup, strna(setup_state), off_color_setup);
ee8c4568
LP
251 }
252
253 if (arg_legend)
6d0c65ff 254 printf("\n%i links listed.\n", c);
ee8c4568
LP
255
256 return 0;
257}
258
c09da729 259/* IEEE Organizationally Unique Identifier vendor string */
b147503e 260static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) {
81fd1dd3 261 const char *description;
fbd0b64f 262 char modalias[STRLEN("OUI:XXYYXXYYXXYY") + 1], *desc;
81fd1dd3
TG
263 int r;
264
265 assert(ret);
c09da729 266
888943fc
LP
267 if (!hwdb)
268 return -EINVAL;
269
81fd1dd3
TG
270 if (!mac)
271 return -EINVAL;
272
c09da729
TG
273 /* skip commonly misused 00:00:00 (Xerox) prefix */
274 if (memcmp(mac, "\0\0\0", 3) == 0)
275 return -EINVAL;
276
d054f0a4
DM
277 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
278 ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 279
81fd1dd3
TG
280 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
281 if (r < 0)
282 return r;
c09da729 283
81fd1dd3
TG
284 desc = strdup(description);
285 if (!desc)
286 return -ENOMEM;
c09da729 287
81fd1dd3
TG
288 *ret = desc;
289
290 return 0;
c09da729
TG
291}
292
69fb1176 293static int get_gateway_description(
1c4baffc 294 sd_netlink *rtnl,
81fd1dd3 295 sd_hwdb *hwdb,
69fb1176
LP
296 int ifindex,
297 int family,
298 union in_addr_union *gateway,
299 char **gateway_description) {
4afd3348 300 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
1c4baffc 301 sd_netlink_message *m;
c09da729
TG
302 int r;
303
304 assert(rtnl);
305 assert(ifindex >= 0);
4c701096 306 assert(IN_SET(family, AF_INET, AF_INET6));
c09da729
TG
307 assert(gateway);
308 assert(gateway_description);
309
310 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
311 if (r < 0)
312 return r;
313
1c4baffc 314 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
315 if (r < 0)
316 return r;
317
1c4baffc 318 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
319 if (r < 0)
320 return r;
321
1c4baffc 322 for (m = reply; m; m = sd_netlink_message_next(m)) {
67a46833
YW
323 union in_addr_union gw = IN_ADDR_NULL;
324 struct ether_addr mac = ETHER_ADDR_NULL;
c09da729
TG
325 uint16_t type;
326 int ifi, fam;
327
1c4baffc 328 r = sd_netlink_message_get_errno(m);
c09da729
TG
329 if (r < 0) {
330 log_error_errno(r, "got error: %m");
331 continue;
332 }
333
1c4baffc 334 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
335 if (r < 0) {
336 log_error_errno(r, "could not get type: %m");
337 continue;
338 }
339
340 if (type != RTM_NEWNEIGH) {
341 log_error("type is not RTM_NEWNEIGH");
342 continue;
343 }
344
345 r = sd_rtnl_message_neigh_get_family(m, &fam);
346 if (r < 0) {
347 log_error_errno(r, "could not get family: %m");
348 continue;
349 }
350
351 if (fam != family) {
352 log_error("family is not correct");
353 continue;
354 }
355
356 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
357 if (r < 0) {
144232a8 358 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
359 continue;
360 }
361
362 if (ifindex > 0 && ifi != ifindex)
363 continue;
364
365 switch (fam) {
366 case AF_INET:
1c4baffc 367 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
368 if (r < 0)
369 continue;
370
371 break;
372 case AF_INET6:
1c4baffc 373 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
374 if (r < 0)
375 continue;
376
377 break;
378 default:
379 continue;
380 }
381
382 if (!in_addr_equal(fam, &gw, gateway))
383 continue;
384
1c4baffc 385 r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
c09da729
TG
386 if (r < 0)
387 continue;
388
389 r = ieee_oui(hwdb, &mac, gateway_description);
390 if (r < 0)
391 continue;
392
393 return 0;
394 }
395
396 return -ENODATA;
397}
398
69fb1176 399static int dump_gateways(
1c4baffc 400 sd_netlink *rtnl,
81fd1dd3 401 sd_hwdb *hwdb,
69fb1176
LP
402 const char *prefix,
403 int ifindex) {
b6a3ca6d
TG
404 _cleanup_free_ struct local_address *local = NULL;
405 int r, n, i;
c09da729 406
837f57da
LP
407 assert(rtnl);
408 assert(prefix);
409
b6a3ca6d
TG
410 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
411 if (n < 0)
412 return n;
c09da729 413
b6a3ca6d
TG
414 for (i = 0; i < n; i++) {
415 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 416
b6a3ca6d 417 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 418 if (r < 0)
b6a3ca6d 419 return r;
c09da729 420
69fb1176 421 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
c09da729 422 if (r < 0)
b6a3ca6d 423 log_debug_errno(r, "Could not get description of gateway: %m");
c09da729 424
1693a943
LP
425 printf("%*s%s",
426 (int) strlen(prefix),
427 i == 0 ? prefix : "",
428 gateway);
429
b6a3ca6d 430 if (description)
1693a943
LP
431 printf(" (%s)", description);
432
433 /* Show interface name for the entry if we show
434 * entries for all interfaces */
435 if (ifindex <= 0) {
436 char name[IF_NAMESIZE+1];
437
438 if (if_indextoname(local[i].ifindex, name)) {
439 fputs(" on ", stdout);
440 fputs(name, stdout);
441 } else
442 printf(" on %%%i", local[i].ifindex);
443 }
444
445 fputc('\n', stdout);
c09da729
TG
446 }
447
448 return 0;
449}
450
69fb1176 451static int dump_addresses(
1c4baffc 452 sd_netlink *rtnl,
69fb1176
LP
453 const char *prefix,
454 int ifindex) {
455
ee8c4568
LP
456 _cleanup_free_ struct local_address *local = NULL;
457 int r, n, i;
458
837f57da
LP
459 assert(rtnl);
460 assert(prefix);
461
1d050e1e 462 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
463 if (n < 0)
464 return n;
465
466 for (i = 0; i < n; i++) {
467 _cleanup_free_ char *pretty = NULL;
468
469 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
470 if (r < 0)
471 return r;
472
1693a943 473 printf("%*s%s",
ee8c4568
LP
474 (int) strlen(prefix),
475 i == 0 ? prefix : "",
476 pretty);
1693a943
LP
477
478 if (ifindex <= 0) {
479 char name[IF_NAMESIZE+1];
480
481 if (if_indextoname(local[i].ifindex, name)) {
482 fputs(" on ", stdout);
483 fputs(name, stdout);
484 } else
485 printf(" on %%%i", local[i].ifindex);
486 }
487
488 fputc('\n', stdout);
ee8c4568
LP
489 }
490
491 return 0;
492}
493
d37b7627
SS
494static int dump_address_labels(sd_netlink *rtnl) {
495 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
496 sd_netlink_message *m;
497 int r;
498
499 assert(rtnl);
500
501 r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6);
502 if (r < 0)
503 return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m");
504
505 r = sd_netlink_message_request_dump(req, true);
506 if (r < 0)
507 return r;
508
509 r = sd_netlink_call(rtnl, req, 0, &reply);
510 if (r < 0)
511 return r;
512
513 printf("%10s/%s %30s\n", "Prefix", "Prefixlen", "Label");
514
515 for (m = reply; m; m = sd_netlink_message_next(m)) {
516 _cleanup_free_ char *pretty = NULL;
67a46833 517 union in_addr_union prefix = IN_ADDR_NULL;
d37b7627
SS
518 uint8_t prefixlen;
519 uint32_t label;
520
521 r = sd_netlink_message_get_errno(m);
522 if (r < 0) {
523 log_error_errno(r, "got error: %m");
524 continue;
525 }
526
527 r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label);
528 if (r < 0 && r != -ENODATA) {
529 log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m");
530 continue;
531 }
532
533 r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6);
534 if (r < 0)
535 continue;
536
537 r = in_addr_to_string(AF_INET6, &prefix, &pretty);
538 if (r < 0)
539 continue;
540
541 r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen);
542 if (r < 0)
543 continue;
544
545 printf("%10s/%-5u %30u\n", pretty, prefixlen, label);
546 }
547
548 return 0;
549}
550
551static int list_address_labels(int argc, char *argv[], void *userdata) {
552 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
553 int r;
554
555 r = sd_netlink_open(&rtnl);
556 if (r < 0)
557 return log_error_errno(r, "Failed to connect to netlink: %m");
558
559 dump_address_labels(rtnl);
560
561 return 0;
562}
563
837f57da
LP
564static int open_lldp_neighbors(int ifindex, FILE **ret) {
565 _cleanup_free_ char *p = NULL;
566 FILE *f;
567
568 if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0)
569 return -ENOMEM;
570
571 f = fopen(p, "re");
572 if (!f)
573 return -errno;
574
575 *ret = f;
576 return 0;
577}
578
579static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
580 _cleanup_free_ void *raw = NULL;
581 size_t l;
582 le64_t u;
583 int r;
584
585 assert(f);
586 assert(ret);
587
588 l = fread(&u, 1, sizeof(u), f);
589 if (l == 0 && feof(f))
590 return 0;
591 if (l != sizeof(u))
592 return -EBADMSG;
593
d23c3e4c
FB
594 /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
595 if (le64toh(u) >= 4096)
596 return -EBADMSG;
597
837f57da
LP
598 raw = new(uint8_t, le64toh(u));
599 if (!raw)
600 return -ENOMEM;
601
602 if (fread(raw, 1, le64toh(u), f) != le64toh(u))
603 return -EBADMSG;
604
605 r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
606 if (r < 0)
607 return r;
608
609 return 1;
610}
611
612static int dump_lldp_neighbors(const char *prefix, int ifindex) {
613 _cleanup_fclose_ FILE *f = NULL;
614 int r, c = 0;
615
616 assert(prefix);
617 assert(ifindex > 0);
618
619 r = open_lldp_neighbors(ifindex, &f);
620 if (r < 0)
621 return r;
622
623 for (;;) {
624 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
625 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
626
627 r = next_lldp_neighbor(f, &n);
628 if (r < 0)
629 return r;
630 if (r == 0)
631 break;
632
633 printf("%*s",
634 (int) strlen(prefix),
635 c == 0 ? prefix : "");
636
637 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
638 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
639 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
640
641 printf("%s on port %s", strna(system_name), strna(port_id));
642
643 if (!isempty(port_description))
644 printf(" (%s)", port_description);
645
646 putchar('\n');
647
648 c++;
649 }
650
651 return c;
652}
653
b295beea
LP
654static void dump_ifindexes(const char *prefix, const int *ifindexes) {
655 unsigned c;
656
657 assert(prefix);
658
659 if (!ifindexes || ifindexes[0] <= 0)
660 return;
661
662 for (c = 0; ifindexes[c] > 0; c++) {
663 char name[IF_NAMESIZE+1];
664
665 printf("%*s",
666 (int) strlen(prefix),
667 c == 0 ? prefix : "");
668
669 if (if_indextoname(ifindexes[c], name))
670 fputs(name, stdout);
671 else
672 printf("%i", ifindexes[c]);
673
674 fputc('\n', stdout);
675 }
676}
677
ee8c4568
LP
678static void dump_list(const char *prefix, char **l) {
679 char **i;
680
dce83649
LP
681 if (strv_isempty(l))
682 return;
683
ee8c4568
LP
684 STRV_FOREACH(i, l) {
685 printf("%*s%s\n",
686 (int) strlen(prefix),
687 i == l ? prefix : "",
688 *i);
689 }
690}
691
69fb1176 692static int link_status_one(
1c4baffc 693 sd_netlink *rtnl,
81fd1dd3 694 sd_hwdb *hwdb,
b147503e
LP
695 const LinkInfo *info) {
696
3df9bec5 697 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
64d6c229 698 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
4afd3348 699 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
9085f64a 700 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 701 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 702 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
703 const char *on_color_operational, *off_color_operational,
704 *on_color_setup, *off_color_setup;
b295beea 705 _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
b147503e 706 int r;
9085f64a
LP
707
708 assert(rtnl);
b147503e 709 assert(info);
9085f64a 710
b147503e 711 (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
d57c365b
LP
712 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
713
33d5013d
LP
714 r = sd_network_link_get_setup_state(info->ifindex, &setup_state);
715 if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
716 setup_state = strdup("unmanaged");
d57c365b 717 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 718
b147503e
LP
719 (void) sd_network_link_get_dns(info->ifindex, &dns);
720 (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
721 (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
722 (void) sd_network_link_get_ntp(info->ifindex, &ntp);
9085f64a 723
691d4da9 724 xsprintf(devid, "n%i", info->ifindex);
914d6c09 725
dce83649 726 (void) sd_device_new_from_device_id(&d, devid);
914d6c09 727
9085f64a 728 if (d) {
dce83649
LP
729 (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
730 (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
731 (void) sd_device_get_property_value(d, "ID_PATH", &path);
9085f64a 732
2c740afd 733 if (sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
dce83649 734 (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
9085f64a 735
2c740afd 736 if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) < 0)
dce83649 737 (void) sd_device_get_property_value(d, "ID_MODEL", &model);
9085f64a
LP
738 }
739
b55822c3 740 t = link_get_type_string(info->iftype, d);
b1acce80 741
4abd866d 742 (void) sd_network_link_get_network_file(info->ifindex, &network);
df3fb561 743
4abd866d
LP
744 (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
745 (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
0d4ad91d 746
9a6f746f 747 printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off_color_operational, info->ifindex, info->name);
9085f64a 748
0d4ad91d
AR
749 printf(" Link File: %s\n"
750 " Network File: %s\n"
751 " Type: %s\n"
752 " State: %s%s%s (%s%s%s)\n",
af5effc4 753 strna(link),
373d9f17 754 strna(network),
9085f64a 755 strna(t),
d57c365b
LP
756 on_color_operational, strna(operational_state), off_color_operational,
757 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
758
759 if (path)
0d4ad91d 760 printf(" Path: %s\n", path);
9085f64a 761 if (driver)
0d4ad91d 762 printf(" Driver: %s\n", driver);
9085f64a 763 if (vendor)
0d4ad91d 764 printf(" Vendor: %s\n", vendor);
9085f64a 765 if (model)
0d4ad91d 766 printf(" Model: %s\n", model);
9085f64a 767
b147503e 768 if (info->has_mac_address) {
888943fc 769 _cleanup_free_ char *description = NULL;
db73295a 770 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc 771
4abd866d 772 (void) ieee_oui(hwdb, &info->mac_address, &description);
888943fc
LP
773
774 if (description)
b147503e 775 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description);
888943fc 776 else
b147503e 777 printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea));
db73295a 778 }
9085f64a 779
b147503e 780 if (info->has_mtu)
4e964aa0 781 printf(" MTU: %" PRIu32 "\n", info->mtu);
9085f64a 782
4abd866d
LP
783 (void) dump_addresses(rtnl, " Address: ", info->ifindex);
784 (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex);
9085f64a 785
dce83649
LP
786 dump_list(" DNS: ", dns);
787 dump_list(" Search Domains: ", search_domains);
788 dump_list(" Route Domains: ", route_domains);
8eb9058d 789
dce83649 790 dump_list(" NTP: ", ntp);
0d4ad91d 791
b295beea
LP
792 dump_ifindexes("Carrier Bound To: ", carrier_bound_to);
793 dump_ifindexes("Carrier Bound By: ", carrier_bound_by);
9085f64a 794
b147503e 795 (void) sd_network_link_get_timezone(info->ifindex, &tz);
64d6c229 796 if (tz)
f5207c22 797 printf(" Time Zone: %s\n", tz);
8eb9058d 798
837f57da
LP
799 (void) dump_lldp_neighbors(" Connected To: ", info->ifindex);
800
9085f64a
LP
801 return 0;
802}
803
0070333f
LP
804static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
805 _cleanup_free_ char *operational_state = NULL;
806 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
807 const char *on_color_operational, *off_color_operational;
808
809 assert(rtnl);
810
4abd866d 811 (void) sd_network_get_operational_state(&operational_state);
0070333f
LP
812 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
813
814 printf("%s%s%s State: %s%s%s\n",
9a6f746f 815 on_color_operational, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off_color_operational,
0070333f
LP
816 on_color_operational, strna(operational_state), off_color_operational);
817
4abd866d
LP
818 (void) dump_addresses(rtnl, " Address: ", 0);
819 (void) dump_gateways(rtnl, hwdb, " Gateway: ", 0);
0070333f 820
4abd866d 821 (void) sd_network_get_dns(&dns);
0070333f
LP
822 dump_list(" DNS: ", dns);
823
4abd866d 824 (void) sd_network_get_search_domains(&search_domains);
0070333f
LP
825 dump_list("Search Domains: ", search_domains);
826
4abd866d 827 (void) sd_network_get_route_domains(&route_domains);
0070333f
LP
828 dump_list(" Route Domains: ", route_domains);
829
4abd866d 830 (void) sd_network_get_ntp(&ntp);
0070333f
LP
831 dump_list(" NTP: ", ntp);
832
833 return 0;
834}
835
266b5389 836static int link_status(int argc, char *argv[], void *userdata) {
4afd3348 837 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
b147503e
LP
838 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
839 _cleanup_free_ LinkInfo *links = NULL;
840 int r, c, i;
ee8c4568 841
0221d68a 842 (void) pager_open(arg_pager_flags);
0070333f 843
1c4baffc 844 r = sd_netlink_open(&rtnl);
f647962d
MS
845 if (r < 0)
846 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 847
81fd1dd3
TG
848 r = sd_hwdb_new(&hwdb);
849 if (r < 0)
850 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 851
b147503e 852 if (arg_all)
a6962904 853 c = acquire_link_info(rtnl, NULL, &links);
b147503e 854 else if (argc <= 1)
0070333f 855 return system_status(rtnl, hwdb);
b147503e 856 else
a6962904 857 c = acquire_link_info(rtnl, argv + 1, &links);
b147503e
LP
858 if (c < 0)
859 return c;
ee8c4568 860
b147503e
LP
861 for (i = 0; i < c; i++) {
862 if (i > 0)
863 fputc('\n', stdout);
ee8c4568 864
b147503e 865 link_status_one(rtnl, hwdb, links + i);
ee8c4568
LP
866 }
867
868 return 0;
869}
870
34437b4f
LP
871static char *lldp_capabilities_to_string(uint16_t x) {
872 static const char characters[] = {
873 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
874 };
875 char *ret;
876 unsigned i;
49699bac 877
34437b4f
LP
878 ret = new(char, ELEMENTSOF(characters) + 1);
879 if (!ret)
49699bac
SS
880 return NULL;
881
34437b4f
LP
882 for (i = 0; i < ELEMENTSOF(characters); i++)
883 ret[i] = (x & (1U << i)) ? characters[i] : '.';
49699bac 884
34437b4f
LP
885 ret[i] = 0;
886 return ret;
49699bac
SS
887}
888
4c3160f1
ZJS
889static void lldp_capabilities_legend(uint16_t x) {
890 unsigned w, i, cols = columns();
404d53a9 891 static const char* const table[] = {
4c3160f1
ZJS
892 "o - Other",
893 "p - Repeater",
894 "b - Bridge",
895 "w - WLAN Access Point",
896 "r - Router",
897 "t - Telephone",
898 "d - DOCSIS cable device",
899 "a - Station",
900 "c - Customer VLAN",
901 "s - Service VLAN",
902 "m - Two-port MAC Relay (TPMR)",
903 };
904
905 if (x == 0)
906 return;
907
908 printf("\nCapability Flags:\n");
909 for (w = 0, i = 0; i < ELEMENTSOF(table); i++)
910 if (x & (1U << i) || arg_all) {
911 bool newline;
912
913 newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols;
914 if (newline)
915 w = 0;
916 w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]);
917 }
918 puts("");
919}
920
49699bac 921static int link_lldp_status(int argc, char *argv[], void *userdata) {
b147503e 922 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
49699bac 923 _cleanup_free_ LinkInfo *links = NULL;
b147503e 924 int i, r, c, m = 0;
4c3160f1 925 uint16_t all = 0;
b147503e
LP
926
927 r = sd_netlink_open(&rtnl);
928 if (r < 0)
929 return log_error_errno(r, "Failed to connect to netlink: %m");
49699bac 930
a6962904 931 c = acquire_link_info(rtnl, argc > 1 ? argv + 1 : NULL, &links);
49699bac 932 if (c < 0)
7d367b45
LP
933 return c;
934
0221d68a 935 (void) pager_open(arg_pager_flags);
49699bac 936
19727828 937 if (arg_legend)
34437b4f
LP
938 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
939 "LINK",
940 "CHASSIS ID",
941 "SYSTEM NAME",
942 "CAPS",
943 "PORT ID",
944 "PORT DESCRIPTION");
49699bac 945
b147503e 946 for (i = 0; i < c; i++) {
34437b4f 947 _cleanup_fclose_ FILE *f = NULL;
49699bac 948
837f57da
LP
949 r = open_lldp_neighbors(links[i].ifindex, &f);
950 if (r == -ENOENT)
951 continue;
952 if (r < 0) {
953 log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
34437b4f
LP
954 continue;
955 }
49699bac 956
34437b4f 957 for (;;) {
d08191a2 958 _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL;
34437b4f
LP
959 const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL;
960 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
34437b4f 961 uint16_t cc;
49699bac 962
837f57da 963 r = next_lldp_neighbor(f, &n);
34437b4f 964 if (r < 0) {
837f57da 965 log_warning_errno(r, "Failed to read neighbor data: %m");
34437b4f 966 break;
49699bac 967 }
837f57da
LP
968 if (r == 0)
969 break;
34437b4f
LP
970
971 (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
972 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
973 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
974 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
975
d08191a2
LP
976 if (chassis_id) {
977 cid = ellipsize(chassis_id, 17, 100);
978 if (cid)
979 chassis_id = cid;
980 }
981
982 if (port_id) {
983 pid = ellipsize(port_id, 17, 100);
984 if (pid)
985 port_id = pid;
986 }
987
988 if (system_name) {
989 sname = ellipsize(system_name, 16, 100);
990 if (sname)
991 system_name = sname;
992 }
993
994 if (port_description) {
995 pdesc = ellipsize(port_description, 16, 100);
996 if (pdesc)
997 port_description = pdesc;
998 }
999
4c3160f1 1000 if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
34437b4f 1001 capabilities = lldp_capabilities_to_string(cc);
4c3160f1
ZJS
1002 all |= cc;
1003 }
34437b4f
LP
1004
1005 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
1006 links[i].name,
1007 strna(chassis_id),
1008 strna(system_name),
1009 strna(capabilities),
1010 strna(port_id),
1011 strna(port_description));
b147503e
LP
1012
1013 m++;
49699bac
SS
1014 }
1015 }
1016
4c3160f1
ZJS
1017 if (arg_legend) {
1018 lldp_capabilities_legend(all);
1019 printf("\n%i neighbors listed.\n", m);
1020 }
49699bac
SS
1021
1022 return 0;
1023}
1024
37ec0fdd
LP
1025static int help(void) {
1026 _cleanup_free_ char *link = NULL;
1027 int r;
1028
1029 r = terminal_urlify_man("networkctl", "1", &link);
1030 if (r < 0)
1031 return log_oom();
1032
ee8c4568
LP
1033 printf("%s [OPTIONS...]\n\n"
1034 "Query and control the networking subsystem.\n\n"
1035 " -h --help Show this help\n"
9085f64a
LP
1036 " --version Show package version\n"
1037 " --no-pager Do not pipe output into a pager\n"
1038 " --no-legend Do not show the headers and footers\n"
1039 " -a --all Show status for all links\n\n"
ee8c4568 1040 "Commands:\n"
a6962904
YW
1041 " list [PATTERN...] List links\n"
1042 " status [PATTERN...] Show link status\n"
1043 " lldp [PATTERN...] Show LLDP neighbors\n"
d37b7627 1044 " label Show current address label entries in the kernel\n"
37ec0fdd
LP
1045 "\nSee the %s for details.\n"
1046 , program_invocation_short_name
1047 , link
1048 );
1049
1050 return 0;
ee8c4568
LP
1051}
1052
1053static int parse_argv(int argc, char *argv[]) {
1054
1055 enum {
1056 ARG_VERSION = 0x100,
1057 ARG_NO_PAGER,
1058 ARG_NO_LEGEND,
1059 };
1060
1061 static const struct option options[] = {
1062 { "help", no_argument, NULL, 'h' },
1063 { "version", no_argument, NULL, ARG_VERSION },
1064 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1065 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1066 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1067 {}
1068 };
1069
1070 int c;
1071
1072 assert(argc >= 0);
1073 assert(argv);
1074
9085f64a 1075 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1076
1077 switch (c) {
1078
1079 case 'h':
37ec0fdd 1080 return help();
ee8c4568
LP
1081
1082 case ARG_VERSION:
3f6fd1ba 1083 return version();
ee8c4568
LP
1084
1085 case ARG_NO_PAGER:
0221d68a 1086 arg_pager_flags |= PAGER_DISABLE;
ee8c4568
LP
1087 break;
1088
1089 case ARG_NO_LEGEND:
1090 arg_legend = false;
1091 break;
1092
9085f64a
LP
1093 case 'a':
1094 arg_all = true;
1095 break;
1096
ee8c4568
LP
1097 case '?':
1098 return -EINVAL;
1099
1100 default:
1101 assert_not_reached("Unhandled option");
1102 }
1103 }
1104
1105 return 1;
1106}
1107
1108static int networkctl_main(int argc, char *argv[]) {
15c3626e
YW
1109 static const Verb verbs[] = {
1110 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
1111 { "status", VERB_ANY, VERB_ANY, 0, link_status },
1112 { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
1113 { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
266b5389 1114 {}
ee8c4568
LP
1115 };
1116
266b5389 1117 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1118}
1119
58fb3678
LP
1120static void warn_networkd_missing(void) {
1121
1122 if (access("/run/systemd/netif/state", F_OK) >= 0)
1123 return;
1124
1125 fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
1126}
1127
4e2ca442 1128static int run(int argc, char* argv[]) {
ee8c4568
LP
1129 int r;
1130
1a043959 1131 log_show_color(true);
ee8c4568
LP
1132 log_parse_environment();
1133 log_open();
1134
1135 r = parse_argv(argc, argv);
1136 if (r <= 0)
4e2ca442 1137 return r;
ee8c4568 1138
58fb3678
LP
1139 warn_networkd_missing();
1140
4e2ca442 1141 return networkctl_main(argc, argv);
ee8c4568 1142}
4e2ca442
ZJS
1143
1144DEFINE_MAIN_FUNCTION(run);