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