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