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