]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
networkctl: split out system status stuff into its own function
[thirdparty/systemd.git] / src / network / networkctl.c
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
20 #include <getopt.h>
21 #include <net/if.h>
22 #include <stdbool.h>
23
24 #include "sd-device.h"
25 #include "sd-hwdb.h"
26 #include "sd-lldp.h"
27 #include "sd-netlink.h"
28 #include "sd-network.h"
29
30 #include "alloc-util.h"
31 #include "arphrd-list.h"
32 #include "device-util.h"
33 #include "ether-addr-util.h"
34 #include "fd-util.h"
35 #include "hwdb-util.h"
36 #include "lldp.h"
37 #include "local-addresses.h"
38 #include "locale-util.h"
39 #include "netlink-util.h"
40 #include "pager.h"
41 #include "parse-util.h"
42 #include "socket-util.h"
43 #include "sparse-endian.h"
44 #include "stdio-util.h"
45 #include "string-table.h"
46 #include "string-util.h"
47 #include "strv.h"
48 #include "terminal-util.h"
49 #include "util.h"
50 #include "verbs.h"
51
52 static bool arg_no_pager = false;
53 static bool arg_legend = true;
54 static bool arg_all = false;
55
56 static void pager_open_if_enabled(void) {
57
58 if (arg_no_pager)
59 return;
60
61 pager_open(false);
62 }
63
64 static int link_get_type_string(unsigned short iftype, sd_device *d, char **ret) {
65 const char *t;
66 char *p;
67
68 assert(ret);
69
70 if (iftype == ARPHRD_ETHER && d) {
71 const char *devtype = NULL, *id = NULL;
72 /* WLANs have iftype ARPHRD_ETHER, but we want
73 * to show a more useful type string for
74 * them */
75
76 (void)sd_device_get_devtype(d, &devtype);
77
78 if (streq_ptr(devtype, "wlan"))
79 id = "wlan";
80 else if (streq_ptr(devtype, "wwan"))
81 id = "wwan";
82
83 if (id) {
84 p = strdup(id);
85 if (!p)
86 return -ENOMEM;
87
88 *ret = p;
89 return 1;
90 }
91 }
92
93 t = arphrd_to_name(iftype);
94 if (!t) {
95 *ret = NULL;
96 return 0;
97 }
98
99 p = strdup(t);
100 if (!p)
101 return -ENOMEM;
102
103 ascii_strlower(p);
104 *ret = p;
105
106 return 0;
107 }
108
109 typedef struct LinkInfo {
110 const char *name;
111 int ifindex;
112 unsigned short iftype;
113 } LinkInfo;
114
115 static int link_info_compare(const void *a, const void *b) {
116 const LinkInfo *x = a, *y = b;
117
118 return x->ifindex - y->ifindex;
119 }
120
121 static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) {
122 _cleanup_free_ LinkInfo *links = NULL;
123 size_t size = 0, c = 0;
124 sd_netlink_message *i;
125 int r;
126
127 for (i = m; i; i = sd_netlink_message_next(i)) {
128 const char *name;
129 unsigned short iftype;
130 uint16_t type;
131 int ifindex;
132
133 r = sd_netlink_message_get_type(i, &type);
134 if (r < 0)
135 return r;
136
137 if (type != RTM_NEWLINK)
138 continue;
139
140 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
141 if (r < 0)
142 return r;
143
144 r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
145 if (r < 0)
146 return r;
147
148 r = sd_rtnl_message_link_get_type(i, &iftype);
149 if (r < 0)
150 return r;
151
152 if (!GREEDY_REALLOC(links, size, c+1))
153 return -ENOMEM;
154
155 links[c].name = name;
156 links[c].ifindex = ifindex;
157 links[c].iftype = iftype;
158 c++;
159 }
160
161 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
162
163 *ret = links;
164 links = NULL;
165
166 return (int) c;
167 }
168
169 static void operational_state_to_color(const char *state, const char **on, const char **off) {
170 assert(on);
171 assert(off);
172
173 if (streq_ptr(state, "routable")) {
174 *on = ansi_highlight_green();
175 *off = ansi_normal();
176 } else if (streq_ptr(state, "degraded")) {
177 *on = ansi_highlight_yellow();
178 *off = ansi_normal();
179 } else
180 *on = *off = "";
181 }
182
183 static void setup_state_to_color(const char *state, const char **on, const char **off) {
184 assert(on);
185 assert(off);
186
187 if (streq_ptr(state, "configured")) {
188 *on = ansi_highlight_green();
189 *off = ansi_normal();
190 } else if (streq_ptr(state, "configuring")) {
191 *on = ansi_highlight_yellow();
192 *off = ansi_normal();
193 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
194 *on = ansi_highlight_red();
195 *off = ansi_normal();
196 } else
197 *on = *off = "";
198 }
199
200 static int list_links(int argc, char *argv[], void *userdata) {
201 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
202 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
203 _cleanup_free_ LinkInfo *links = NULL;
204 int r, c, i;
205
206 pager_open_if_enabled();
207
208 r = sd_netlink_open(&rtnl);
209 if (r < 0)
210 return log_error_errno(r, "Failed to connect to netlink: %m");
211
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_request_dump(req, true);
217 if (r < 0)
218 return rtnl_log_create_error(r);
219
220 r = sd_netlink_call(rtnl, req, 0, &reply);
221 if (r < 0)
222 return log_error_errno(r, "Failed to enumerate links: %m");
223
224 if (arg_legend)
225 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
226
227 c = decode_and_sort_links(reply, &links);
228 if (c < 0)
229 return rtnl_log_parse_error(c);
230
231 for (i = 0; i < c; i++) {
232 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
233 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
234 const char *on_color_operational, *off_color_operational,
235 *on_color_setup, *off_color_setup;
236 char devid[2 + DECIMAL_STR_MAX(int)];
237 _cleanup_free_ char *t = NULL;
238
239 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
240 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
241
242 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
243 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
244
245 sprintf(devid, "n%i", links[i].ifindex);
246 (void)sd_device_new_from_device_id(&d, devid);
247
248 link_get_type_string(links[i].iftype, d, &t);
249
250 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
251 links[i].ifindex, links[i].name, strna(t),
252 on_color_operational, strna(operational_state), off_color_operational,
253 on_color_setup, strna(setup_state), off_color_setup);
254 }
255
256 if (arg_legend)
257 printf("\n%i links listed.\n", c);
258
259 return 0;
260 }
261
262 /* IEEE Organizationally Unique Identifier vendor string */
263 static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) {
264 const char *description;
265 char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc;
266 int r;
267
268 assert(ret);
269
270 if (!hwdb)
271 return -EINVAL;
272
273 if (!mac)
274 return -EINVAL;
275
276 /* skip commonly misused 00:00:00 (Xerox) prefix */
277 if (memcmp(mac, "\0\0\0", 3) == 0)
278 return -EINVAL;
279
280 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
281 ETHER_ADDR_FORMAT_VAL(*mac));
282
283 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
284 if (r < 0)
285 return r;
286
287 desc = strdup(description);
288 if (!desc)
289 return -ENOMEM;
290
291 *ret = desc;
292
293 return 0;
294 }
295
296 static int get_gateway_description(
297 sd_netlink *rtnl,
298 sd_hwdb *hwdb,
299 int ifindex,
300 int family,
301 union in_addr_union *gateway,
302 char **gateway_description) {
303 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
304 sd_netlink_message *m;
305 int r;
306
307 assert(rtnl);
308 assert(ifindex >= 0);
309 assert(family == AF_INET || family == AF_INET6);
310 assert(gateway);
311 assert(gateway_description);
312
313 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
314 if (r < 0)
315 return r;
316
317 r = sd_netlink_message_request_dump(req, true);
318 if (r < 0)
319 return r;
320
321 r = sd_netlink_call(rtnl, req, 0, &reply);
322 if (r < 0)
323 return r;
324
325 for (m = reply; m; m = sd_netlink_message_next(m)) {
326 union in_addr_union gw = {};
327 struct ether_addr mac = {};
328 uint16_t type;
329 int ifi, fam;
330
331 r = sd_netlink_message_get_errno(m);
332 if (r < 0) {
333 log_error_errno(r, "got error: %m");
334 continue;
335 }
336
337 r = sd_netlink_message_get_type(m, &type);
338 if (r < 0) {
339 log_error_errno(r, "could not get type: %m");
340 continue;
341 }
342
343 if (type != RTM_NEWNEIGH) {
344 log_error("type is not RTM_NEWNEIGH");
345 continue;
346 }
347
348 r = sd_rtnl_message_neigh_get_family(m, &fam);
349 if (r < 0) {
350 log_error_errno(r, "could not get family: %m");
351 continue;
352 }
353
354 if (fam != family) {
355 log_error("family is not correct");
356 continue;
357 }
358
359 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
360 if (r < 0) {
361 log_error_errno(r, "could not get ifindex: %m");
362 continue;
363 }
364
365 if (ifindex > 0 && ifi != ifindex)
366 continue;
367
368 switch (fam) {
369 case AF_INET:
370 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
371 if (r < 0)
372 continue;
373
374 break;
375 case AF_INET6:
376 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
377 if (r < 0)
378 continue;
379
380 break;
381 default:
382 continue;
383 }
384
385 if (!in_addr_equal(fam, &gw, gateway))
386 continue;
387
388 r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
389 if (r < 0)
390 continue;
391
392 r = ieee_oui(hwdb, &mac, gateway_description);
393 if (r < 0)
394 continue;
395
396 return 0;
397 }
398
399 return -ENODATA;
400 }
401
402 static int dump_gateways(
403 sd_netlink *rtnl,
404 sd_hwdb *hwdb,
405 const char *prefix,
406 int ifindex) {
407 _cleanup_free_ struct local_address *local = NULL;
408 int r, n, i;
409
410 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
411 if (n < 0)
412 return n;
413
414 for (i = 0; i < n; i++) {
415 _cleanup_free_ char *gateway = NULL, *description = NULL;
416
417 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
418 if (r < 0)
419 return r;
420
421 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
422 if (r < 0)
423 log_debug_errno(r, "Could not get description of gateway: %m");
424
425 printf("%*s%s",
426 (int) strlen(prefix),
427 i == 0 ? prefix : "",
428 gateway);
429
430 if (description)
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);
446 }
447
448 return 0;
449 }
450
451 static int dump_addresses(
452 sd_netlink *rtnl,
453 const char *prefix,
454 int ifindex) {
455
456 _cleanup_free_ struct local_address *local = NULL;
457 int r, n, i;
458
459 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
460 if (n < 0)
461 return n;
462
463 for (i = 0; i < n; i++) {
464 _cleanup_free_ char *pretty = NULL;
465
466 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
467 if (r < 0)
468 return r;
469
470 printf("%*s%s",
471 (int) strlen(prefix),
472 i == 0 ? prefix : "",
473 pretty);
474
475 if (ifindex <= 0) {
476 char name[IF_NAMESIZE+1];
477
478 if (if_indextoname(local[i].ifindex, name)) {
479 fputs(" on ", stdout);
480 fputs(name, stdout);
481 } else
482 printf(" on %%%i", local[i].ifindex);
483 }
484
485 fputc('\n', stdout);
486 }
487
488 return 0;
489 }
490
491 static void dump_list(const char *prefix, char **l) {
492 char **i;
493
494 if (strv_isempty(l))
495 return;
496
497 STRV_FOREACH(i, l) {
498 printf("%*s%s\n",
499 (int) strlen(prefix),
500 i == l ? prefix : "",
501 *i);
502 }
503 }
504
505 static int link_status_one(
506 sd_netlink *rtnl,
507 sd_hwdb *hwdb,
508 const char *name) {
509 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
510 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
511 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
512 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
513 char devid[2 + DECIMAL_STR_MAX(int)];
514 _cleanup_free_ char *t = NULL, *network = NULL;
515 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
516 const char *on_color_operational, *off_color_operational,
517 *on_color_setup, *off_color_setup;
518 _cleanup_strv_free_ char **carrier_bound_to = NULL;
519 _cleanup_strv_free_ char **carrier_bound_by = NULL;
520 struct ether_addr e;
521 unsigned short iftype;
522 int r, ifindex;
523 bool have_mac;
524 uint32_t mtu;
525
526 assert(rtnl);
527 assert(name);
528
529 if (parse_ifindex(name, &ifindex) >= 0)
530 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
531 else {
532 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
533 if (r < 0)
534 return rtnl_log_create_error(r);
535
536 r = sd_netlink_message_append_string(req, IFLA_IFNAME, name);
537 }
538
539 if (r < 0)
540 return rtnl_log_create_error(r);
541
542 r = sd_netlink_call(rtnl, req, 0, &reply);
543 if (r < 0)
544 return log_error_errno(r, "Failed to query link: %m");
545
546 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
547 if (r < 0)
548 return rtnl_log_parse_error(r);
549
550 r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name);
551 if (r < 0)
552 return rtnl_log_parse_error(r);
553
554 r = sd_rtnl_message_link_get_type(reply, &iftype);
555 if (r < 0)
556 return rtnl_log_parse_error(r);
557
558 have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
559 if (have_mac) {
560 const uint8_t *p;
561 bool all_zeroes = true;
562
563 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
564 if (*p != 0) {
565 all_zeroes = false;
566 break;
567 }
568
569 if (all_zeroes)
570 have_mac = false;
571 }
572
573 (void) sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
574
575 (void) sd_network_link_get_operational_state(ifindex, &operational_state);
576 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
577
578 (void) sd_network_link_get_setup_state(ifindex, &setup_state);
579 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
580
581 (void) sd_network_link_get_dns(ifindex, &dns);
582 (void) sd_network_link_get_search_domains(ifindex, &search_domains);
583 (void) sd_network_link_get_route_domains(ifindex, &route_domains);
584 (void) sd_network_link_get_ntp(ifindex, &ntp);
585
586 sprintf(devid, "n%i", ifindex);
587
588 (void) sd_device_new_from_device_id(&d, devid);
589
590 if (d) {
591 (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
592 (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
593 (void) sd_device_get_property_value(d, "ID_PATH", &path);
594
595 r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
596 if (r < 0)
597 (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
598
599 r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
600 if (r < 0)
601 (void) sd_device_get_property_value(d, "ID_MODEL", &model);
602 }
603
604 link_get_type_string(iftype, d, &t);
605
606 sd_network_link_get_network_file(ifindex, &network);
607
608 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
609 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
610
611 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
612
613 printf(" Link File: %s\n"
614 " Network File: %s\n"
615 " Type: %s\n"
616 " State: %s%s%s (%s%s%s)\n",
617 strna(link),
618 strna(network),
619 strna(t),
620 on_color_operational, strna(operational_state), off_color_operational,
621 on_color_setup, strna(setup_state), off_color_setup);
622
623 if (path)
624 printf(" Path: %s\n", path);
625 if (driver)
626 printf(" Driver: %s\n", driver);
627 if (vendor)
628 printf(" Vendor: %s\n", vendor);
629 if (model)
630 printf(" Model: %s\n", model);
631
632 if (have_mac) {
633 _cleanup_free_ char *description = NULL;
634 char ea[ETHER_ADDR_TO_STRING_MAX];
635
636 ieee_oui(hwdb, &e, &description);
637
638 if (description)
639 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
640 else
641 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
642 }
643
644 if (mtu > 0)
645 printf(" MTU: %u\n", mtu);
646
647 dump_addresses(rtnl, " Address: ", ifindex);
648 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
649
650 dump_list(" DNS: ", dns);
651 dump_list(" Search Domains: ", search_domains);
652 dump_list(" Route Domains: ", route_domains);
653
654 dump_list(" NTP: ", ntp);
655
656 dump_list("Carrier Bound To: ", carrier_bound_to);
657 dump_list("Carrier Bound By: ", carrier_bound_by);
658
659 (void) sd_network_link_get_timezone(ifindex, &tz);
660 if (tz)
661 printf(" Time Zone: %s", tz);
662
663 return 0;
664 }
665
666 static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
667 _cleanup_free_ char *operational_state = NULL;
668 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
669 const char *on_color_operational, *off_color_operational;
670
671 assert(rtnl);
672
673 sd_network_get_operational_state(&operational_state);
674 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
675
676 printf("%s%s%s State: %s%s%s\n",
677 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
678 on_color_operational, strna(operational_state), off_color_operational);
679
680 dump_addresses(rtnl, " Address: ", 0);
681 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
682
683 sd_network_get_dns(&dns);
684 dump_list(" DNS: ", dns);
685
686 sd_network_get_search_domains(&search_domains);
687 dump_list("Search Domains: ", search_domains);
688
689 sd_network_get_route_domains(&route_domains);
690 dump_list(" Route Domains: ", route_domains);
691
692 sd_network_get_ntp(&ntp);
693 dump_list(" NTP: ", ntp);
694
695 return 0;
696 }
697
698 static int link_status(int argc, char *argv[], void *userdata) {
699 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
700 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
701 char **name;
702 int r;
703
704 pager_open_if_enabled();
705
706 r = sd_netlink_open(&rtnl);
707 if (r < 0)
708 return log_error_errno(r, "Failed to connect to netlink: %m");
709
710 r = sd_hwdb_new(&hwdb);
711 if (r < 0)
712 log_debug_errno(r, "Failed to open hardware database: %m");
713
714 if (argc <= 1 && !arg_all)
715 return system_status(rtnl, hwdb);
716
717 if (arg_all) {
718 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
719 _cleanup_free_ LinkInfo *links = NULL;
720 int c, i;
721
722 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
723 if (r < 0)
724 return rtnl_log_create_error(r);
725
726 r = sd_netlink_message_request_dump(req, true);
727 if (r < 0)
728 return rtnl_log_create_error(r);
729
730 r = sd_netlink_call(rtnl, req, 0, &reply);
731 if (r < 0)
732 return log_error_errno(r, "Failed to enumerate links: %m");
733
734 c = decode_and_sort_links(reply, &links);
735 if (c < 0)
736 return rtnl_log_parse_error(c);
737
738 for (i = 0; i < c; i++) {
739 if (i > 0)
740 fputc('\n', stdout);
741
742 link_status_one(rtnl, hwdb, links[i].name);
743 }
744 } else {
745 STRV_FOREACH(name, argv + 1) {
746 if (name != argv + 1)
747 fputc('\n', stdout);
748
749 link_status_one(rtnl, hwdb, *name);
750 }
751 }
752
753 return 0;
754 }
755
756 static char *lldp_capabilities_to_string(uint16_t x) {
757 static const char characters[] = {
758 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
759 };
760 char *ret;
761 unsigned i;
762
763 ret = new(char, ELEMENTSOF(characters) + 1);
764 if (!ret)
765 return NULL;
766
767 for (i = 0; i < ELEMENTSOF(characters); i++)
768 ret[i] = (x & (1U << i)) ? characters[i] : '.';
769
770 ret[i] = 0;
771 return ret;
772 }
773
774 static int link_lldp_status(int argc, char *argv[], void *userdata) {
775 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
776 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
777 _cleanup_free_ LinkInfo *links = NULL;
778 int i, r, c, j;
779
780 pager_open_if_enabled();
781
782 r = sd_netlink_open(&rtnl);
783 if (r < 0)
784 return log_error_errno(r, "Failed to connect to netlink: %m");
785
786 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
787 if (r < 0)
788 return rtnl_log_create_error(r);
789
790 r = sd_netlink_message_request_dump(req, true);
791 if (r < 0)
792 return rtnl_log_create_error(r);
793
794 r = sd_netlink_call(rtnl, req, 0, &reply);
795 if (r < 0)
796 return log_error_errno(r, "Failed to enumerate links: %m");
797
798 c = decode_and_sort_links(reply, &links);
799 if (c < 0)
800 return rtnl_log_parse_error(c);
801
802 if (arg_legend)
803 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
804 "LINK",
805 "CHASSIS ID",
806 "SYSTEM NAME",
807 "CAPS",
808 "PORT ID",
809 "PORT DESCRIPTION");
810
811 for (i = j = 0; i < c; i++) {
812 _cleanup_fclose_ FILE *f = NULL;
813 _cleanup_free_ char *p = NULL;
814
815 if (asprintf(&p, "/run/systemd/netif/lldp/%i", links[i].ifindex) < 0)
816 return log_oom();
817
818 f = fopen(p, "re");
819 if (!f) {
820 if (errno == ENOENT)
821 continue;
822
823 log_warning_errno(errno, "Failed to open %s, ignoring: %m", p);
824 continue;
825 }
826
827 for (;;) {
828 const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL;
829 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
830 _cleanup_free_ void *raw = NULL;
831 uint16_t cc;
832 le64_t u;
833 size_t l;
834
835 l = fread(&u, 1, sizeof(u), f);
836 if (l == 0 && feof(f)) /* EOF */
837 break;
838 if (l != sizeof(u)) {
839 log_warning("Premature end of file, ignoring.");
840 break;
841 }
842
843 raw = new(uint8_t, le64toh(u));
844 if (!raw)
845 return log_oom();
846
847 if (fread(raw, 1, le64toh(u), f) != le64toh(u)) {
848 log_warning("Premature end of file, ignoring.");
849 break;
850 }
851
852 r = sd_lldp_neighbor_from_raw(&n, raw, le64toh(u));
853 if (r < 0) {
854 log_warning_errno(r, "Failed to parse LLDP data, ignoring: %m");
855 break;
856 }
857
858 (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
859 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
860 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
861 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
862
863 if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0)
864 capabilities = lldp_capabilities_to_string(cc);
865
866 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
867 links[i].name,
868 strna(chassis_id),
869 strna(system_name),
870 strna(capabilities),
871 strna(port_id),
872 strna(port_description));
873 }
874 }
875
876 if (arg_legend)
877 printf("\nCapabilities:\n"
878 "o - Other; p - Repeater; b - Bridge; w - WLAN Access Point; r - Router;\n"
879 "t - Telephone; d - DOCSIS cable device; a - Station; c - Customer VLAN;\n"
880 "s - Service VLAN, m - Two-port MAC Relay (TPMR)\n\n"
881 "Total entries displayed: %i\n", j);
882
883 return 0;
884 }
885
886 static void help(void) {
887 printf("%s [OPTIONS...]\n\n"
888 "Query and control the networking subsystem.\n\n"
889 " -h --help Show this help\n"
890 " --version Show package version\n"
891 " --no-pager Do not pipe output into a pager\n"
892 " --no-legend Do not show the headers and footers\n"
893 " -a --all Show status for all links\n\n"
894 "Commands:\n"
895 " list List links\n"
896 " status [LINK...] Show link status\n"
897 " lldp Show lldp neighbors\n"
898 , program_invocation_short_name);
899 }
900
901 static int parse_argv(int argc, char *argv[]) {
902
903 enum {
904 ARG_VERSION = 0x100,
905 ARG_NO_PAGER,
906 ARG_NO_LEGEND,
907 };
908
909 static const struct option options[] = {
910 { "help", no_argument, NULL, 'h' },
911 { "version", no_argument, NULL, ARG_VERSION },
912 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
913 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
914 { "all", no_argument, NULL, 'a' },
915 {}
916 };
917
918 int c;
919
920 assert(argc >= 0);
921 assert(argv);
922
923 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
924
925 switch (c) {
926
927 case 'h':
928 help();
929 return 0;
930
931 case ARG_VERSION:
932 return version();
933
934 case ARG_NO_PAGER:
935 arg_no_pager = true;
936 break;
937
938 case ARG_NO_LEGEND:
939 arg_legend = false;
940 break;
941
942 case 'a':
943 arg_all = true;
944 break;
945
946 case '?':
947 return -EINVAL;
948
949 default:
950 assert_not_reached("Unhandled option");
951 }
952 }
953
954 return 1;
955 }
956
957 static int networkctl_main(int argc, char *argv[]) {
958 const Verb verbs[] = {
959 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
960 { "status", 1, VERB_ANY, 0, link_status },
961 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
962 {}
963 };
964
965 return dispatch_verb(argc, argv, verbs, NULL);
966 }
967
968 int main(int argc, char* argv[]) {
969 int r;
970
971 log_parse_environment();
972 log_open();
973
974 r = parse_argv(argc, argv);
975 if (r <= 0)
976 goto finish;
977
978 r = networkctl_main(argc, argv);
979
980 finish:
981 pager_close();
982
983 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
984 }