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