]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / network / networkctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <getopt.h>
23 #include <net/if.h>
24 #include <stdbool.h>
25
26 #include "sd-device.h"
27 #include "sd-hwdb.h"
28 #include "sd-netlink.h"
29 #include "sd-network.h"
30
31 #include "arphrd-list.h"
32 #include "device-util.h"
33 #include "ether-addr-util.h"
34 #include "hwdb-util.h"
35 #include "lldp.h"
36 #include "local-addresses.h"
37 #include "netlink-util.h"
38 #include "pager.h"
39 #include "socket-util.h"
40 #include "string-util.h"
41 #include "strv.h"
42 #include "terminal-util.h"
43 #include "util.h"
44 #include "verbs.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_normal();
170 } else if (streq_ptr(state, "degraded")) {
171 *on = ansi_highlight_yellow();
172 *off = ansi_normal();
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_normal();
184 } else if (streq_ptr(state, "configuring")) {
185 *on = ansi_highlight_yellow();
186 *off = ansi_normal();
187 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
188 *on = ansi_highlight_red();
189 *off = ansi_normal();
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, *tz = 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_domains(ifindex, &domains);
574 r = sd_network_link_get_wildcard_domain(ifindex);
575 if (r > 0) {
576 char *wildcard;
577
578 wildcard = strdup("*");
579 if (!wildcard)
580 return log_oom();
581
582 if (strv_consume(&domains, wildcard) < 0)
583 return log_oom();
584 }
585
586 sprintf(devid, "n%i", ifindex);
587
588 (void)sd_device_new_from_device_id(&d, devid);
589
590 if (d) {
591 (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
592 (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
593 (void)sd_device_get_property_value(d, "ID_PATH", &path);
594
595 r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
596 if (r < 0)
597 (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
598
599 r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
600 if (r < 0)
601 (void)sd_device_get_property_value(d, "ID_MODEL", &model);
602 }
603
604 link_get_type_string(iftype, d, &t);
605
606 sd_network_link_get_network_file(ifindex, &network);
607
608 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
609 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
610
611 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
612
613 printf(" Link File: %s\n"
614 " Network File: %s\n"
615 " Type: %s\n"
616 " State: %s%s%s (%s%s%s)\n",
617 strna(link),
618 strna(network),
619 strna(t),
620 on_color_operational, strna(operational_state), off_color_operational,
621 on_color_setup, strna(setup_state), off_color_setup);
622
623 if (path)
624 printf(" Path: %s\n", path);
625 if (driver)
626 printf(" Driver: %s\n", driver);
627 if (vendor)
628 printf(" Vendor: %s\n", vendor);
629 if (model)
630 printf(" Model: %s\n", model);
631
632 if (have_mac) {
633 _cleanup_free_ char *description = NULL;
634 char ea[ETHER_ADDR_TO_STRING_MAX];
635
636 ieee_oui(hwdb, &e, &description);
637
638 if (description)
639 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
640 else
641 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
642 }
643
644 if (mtu > 0)
645 printf(" MTU: %u\n", mtu);
646
647 dump_addresses(rtnl, " Address: ", ifindex);
648 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
649
650 if (!strv_isempty(dns))
651 dump_list(" DNS: ", dns);
652 if (!strv_isempty(domains))
653 dump_list(" Domain: ", domains);
654
655 (void) sd_network_link_get_ntp(ifindex, &ntp);
656 if (!strv_isempty(ntp))
657 dump_list(" NTP: ", ntp);
658
659 if (!strv_isempty(carrier_bound_to))
660 dump_list("Carrier Bound To: ", carrier_bound_to);
661
662 if (!strv_isempty(carrier_bound_by))
663 dump_list("Carrier Bound By: ", carrier_bound_by);
664
665 (void) sd_network_link_get_timezone(ifindex, &tz);
666 if (tz)
667 printf(" Time Zone: %s", tz);
668
669 return 0;
670 }
671
672 static int link_status(int argc, char *argv[], void *userdata) {
673 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
674 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
675 char **name;
676 int r;
677
678 r = sd_netlink_open(&rtnl);
679 if (r < 0)
680 return log_error_errno(r, "Failed to connect to netlink: %m");
681
682 r = sd_hwdb_new(&hwdb);
683 if (r < 0)
684 log_debug_errno(r, "Failed to open hardware database: %m");
685
686 if (argc <= 1 && !arg_all) {
687 _cleanup_free_ char *operational_state = NULL;
688 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
689 const char *on_color_operational, *off_color_operational;
690
691 sd_network_get_operational_state(&operational_state);
692 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
693
694 printf("%s%s%s State: %s%s%s\n",
695 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
696 on_color_operational, strna(operational_state), off_color_operational);
697
698 dump_addresses(rtnl, " Address: ", 0);
699 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
700
701 sd_network_get_dns(&dns);
702 if (!strv_isempty(dns))
703 dump_list(" DNS: ", dns);
704
705 sd_network_get_domains(&domains);
706 if (!strv_isempty(domains))
707 dump_list(" Domain: ", domains);
708
709 sd_network_get_ntp(&ntp);
710 if (!strv_isempty(ntp))
711 dump_list(" NTP: ", ntp);
712
713 return 0;
714 }
715
716 pager_open_if_enabled();
717
718 if (arg_all) {
719 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
720 _cleanup_free_ LinkInfo *links = NULL;
721 int c, i;
722
723 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
724 if (r < 0)
725 return rtnl_log_create_error(r);
726
727 r = sd_netlink_message_request_dump(req, true);
728 if (r < 0)
729 return rtnl_log_create_error(r);
730
731 r = sd_netlink_call(rtnl, req, 0, &reply);
732 if (r < 0)
733 return log_error_errno(r, "Failed to enumerate links: %m");
734
735 c = decode_and_sort_links(reply, &links);
736 if (c < 0)
737 return rtnl_log_parse_error(c);
738
739 for (i = 0; i < c; i++) {
740 if (i > 0)
741 fputc('\n', stdout);
742
743 link_status_one(rtnl, hwdb, links[i].name);
744 }
745 } else {
746 STRV_FOREACH(name, argv + 1) {
747 if (name != argv + 1)
748 fputc('\n', stdout);
749
750 link_status_one(rtnl, hwdb, *name);
751 }
752 }
753
754 return 0;
755 }
756
757 const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
758 LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
759
760 static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
761 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
762 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
763 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
764 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
765 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
766 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
767 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
768 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
769 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
770 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
771 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
772 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
773 };
774
775 DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
776
777 static char *lldp_system_caps(uint16_t cap) {
778 _cleanup_free_ char *s = NULL, *t = NULL;
779 char *capability;
780
781 t = strdup("[ ");
782 if (!t)
783 return NULL;
784
785 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
786 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
787 if (!s)
788 return NULL;
789
790 free(t);
791 t = s;
792 }
793
794 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
795 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
796 if (!s)
797 return NULL;
798
799 free(t);
800 t = s;
801 }
802
803 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
804 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
805 if (!s)
806 return NULL;
807
808 free(t);
809 t = s;
810 }
811
812 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
813 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
814 if (!s)
815 return NULL;
816
817 free(t);
818 t = s;
819 }
820
821 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
822 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
823 if (!s)
824 return NULL;
825
826 free(t);
827 t = s;
828 }
829
830 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
831 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
832 if (!s)
833 return NULL;
834
835 free(t);
836 t = s;
837 }
838
839 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
840 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
841 if (!s)
842 return NULL;
843
844 free(t);
845 t = s;
846 }
847
848 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
849 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
850 if (!s)
851 return NULL;
852
853 free(t);
854 t = s;
855 }
856
857 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
858 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
859 if (!s)
860 return NULL;
861
862 free(t);
863 t = s;
864 }
865
866 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
867 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
868 if (!s)
869 return NULL;
870
871 free(t);
872 t = s;
873 }
874
875 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
876 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
877 if (!s)
878 return NULL;
879
880 free(t);
881 }
882
883 if (!s) {
884 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
885 if (!s)
886 return NULL;
887
888 free(t);
889 }
890
891 t = strappend(s, "]");
892 if (!t)
893 return NULL;
894
895 free(s);
896 capability = t;
897
898 s = NULL;
899 t = NULL;
900
901 return capability;
902 }
903
904 static int link_lldp_status(int argc, char *argv[], void *userdata) {
905 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
906 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
907 _cleanup_free_ LinkInfo *links = NULL;
908 const char *state, *word;
909
910 double ttl = -1;
911 uint32_t capability;
912 int i, r, c, j;
913 size_t ll;
914 char **s;
915
916 pager_open_if_enabled();
917
918 r = sd_netlink_open(&rtnl);
919 if (r < 0)
920 return log_error_errno(r, "Failed to connect to netlink: %m");
921
922 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
923 if (r < 0)
924 return rtnl_log_create_error(r);
925
926 r = sd_netlink_message_request_dump(req, true);
927 if (r < 0)
928 return rtnl_log_create_error(r);
929
930 r = sd_netlink_call(rtnl, req, 0, &reply);
931 if (r < 0)
932 return log_error_errno(r, "Failed to enumerate links: %m");
933
934 c = decode_and_sort_links(reply, &links);
935 if (c < 0)
936 return rtnl_log_parse_error(c);
937
938 if (arg_legend)
939 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
940
941 for (i = j = 0; i < c; i++) {
942 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
943 _cleanup_strv_free_ char **l = NULL;
944
945 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
946 if (r < 0)
947 continue;
948
949 l = strv_split_newlines(lldp);
950 if (!l)
951 return -ENOMEM;
952
953 STRV_FOREACH(s, l) {
954 FOREACH_WORD_QUOTED(word, ll, *s, state) {
955 _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
956
957 t = strndup(word, ll);
958 if (!t)
959 return -ENOMEM;
960
961 r = split_pair(t, "=", &a, &b);
962 if (r < 0)
963 continue;
964
965 if (streq(a, "_Chassis")) {
966 r = free_and_strdup(&chassis, b);
967 if (r < 0)
968 return r;
969
970 } else if (streq(a, "_Port")) {
971 r = free_and_strdup(&port, b);
972 if (r < 0)
973 return r;
974
975 } else if (streq(a, "_TTL")) {
976 long long unsigned x = 0;
977 usec_t time;
978
979 r = safe_atollu(b, &x);
980 if (r < 0 || (usec_t) x != x)
981 return log_warning_errno(r < 0 ? r : ERANGE,
982 "Failed to parse TTL \"%s\": %m", b);
983
984 time = now(clock_boottime_or_monotonic());
985 if (x < time)
986 continue;
987
988 ttl = (double) (x - time) / USEC_PER_SEC;
989
990 } else if (streq(a, "_CAP")) {
991 sscanf(b, "%x", &capability);
992
993 cap = lldp_system_caps(capability);
994 }
995
996 }
997
998 if (ttl >= 0) {
999 printf("%10s %24s %16s %16f %16s\n",
1000 links[i].name,
1001 strna(chassis), strna(port),
1002 ttl, cap);
1003 j++;
1004 }
1005 }
1006 }
1007
1008 if (arg_legend) {
1009 printf("\nCapability Codes:\n"
1010 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1011 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1012 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1013
1014 printf("Total entries displayed: %d\n", j);
1015 }
1016
1017 return 0;
1018 }
1019
1020 static void help(void) {
1021 printf("%s [OPTIONS...]\n\n"
1022 "Query and control the networking subsystem.\n\n"
1023 " -h --help Show this help\n"
1024 " --version Show package version\n"
1025 " --no-pager Do not pipe output into a pager\n"
1026 " --no-legend Do not show the headers and footers\n"
1027 " -a --all Show status for all links\n\n"
1028 "Commands:\n"
1029 " list List links\n"
1030 " status [LINK...] Show link status\n"
1031 " lldp Show lldp information\n"
1032 , program_invocation_short_name);
1033 }
1034
1035 static int parse_argv(int argc, char *argv[]) {
1036
1037 enum {
1038 ARG_VERSION = 0x100,
1039 ARG_NO_PAGER,
1040 ARG_NO_LEGEND,
1041 };
1042
1043 static const struct option options[] = {
1044 { "help", no_argument, NULL, 'h' },
1045 { "version", no_argument, NULL, ARG_VERSION },
1046 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1047 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1048 { "all", no_argument, NULL, 'a' },
1049 {}
1050 };
1051
1052 int c;
1053
1054 assert(argc >= 0);
1055 assert(argv);
1056
1057 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
1058
1059 switch (c) {
1060
1061 case 'h':
1062 help();
1063 return 0;
1064
1065 case ARG_VERSION:
1066 return version();
1067
1068 case ARG_NO_PAGER:
1069 arg_no_pager = true;
1070 break;
1071
1072 case ARG_NO_LEGEND:
1073 arg_legend = false;
1074 break;
1075
1076 case 'a':
1077 arg_all = true;
1078 break;
1079
1080 case '?':
1081 return -EINVAL;
1082
1083 default:
1084 assert_not_reached("Unhandled option");
1085 }
1086 }
1087
1088 return 1;
1089 }
1090
1091 static int networkctl_main(int argc, char *argv[]) {
1092 const Verb verbs[] = {
1093 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1094 { "status", 1, VERB_ANY, 0, link_status },
1095 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
1096 {}
1097 };
1098
1099 return dispatch_verb(argc, argv, verbs, NULL);
1100 }
1101
1102 int main(int argc, char* argv[]) {
1103 int r;
1104
1105 log_parse_environment();
1106 log_open();
1107
1108 r = parse_argv(argc, argv);
1109 if (r <= 0)
1110 goto finish;
1111
1112 r = networkctl_main(argc, argv);
1113
1114 finish:
1115 pager_close();
1116
1117 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1118 }