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