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