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