]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
util-lib: split out allocation calls into alloc-util.[ch]
[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 "locale-util.h"
40 #include "netlink-util.h"
41 #include "pager.h"
42 #include "parse-util.h"
43 #include "socket-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_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
201 _cleanup_netlink_unref_ 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_device_unref_ 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 snprintf(modalias, sizeof(modalias), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
280
281 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
282 if (r < 0)
283 return r;
284
285 desc = strdup(description);
286 if (!desc)
287 return -ENOMEM;
288
289 *ret = desc;
290
291 return 0;
292 }
293
294 static int get_gateway_description(
295 sd_netlink *rtnl,
296 sd_hwdb *hwdb,
297 int ifindex,
298 int family,
299 union in_addr_union *gateway,
300 char **gateway_description) {
301 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
302 sd_netlink_message *m;
303 int r;
304
305 assert(rtnl);
306 assert(ifindex >= 0);
307 assert(family == AF_INET || family == AF_INET6);
308 assert(gateway);
309 assert(gateway_description);
310
311 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
312 if (r < 0)
313 return r;
314
315 r = sd_netlink_message_request_dump(req, true);
316 if (r < 0)
317 return r;
318
319 r = sd_netlink_call(rtnl, req, 0, &reply);
320 if (r < 0)
321 return r;
322
323 for (m = reply; m; m = sd_netlink_message_next(m)) {
324 union in_addr_union gw = {};
325 struct ether_addr mac = {};
326 uint16_t type;
327 int ifi, fam;
328
329 r = sd_netlink_message_get_errno(m);
330 if (r < 0) {
331 log_error_errno(r, "got error: %m");
332 continue;
333 }
334
335 r = sd_netlink_message_get_type(m, &type);
336 if (r < 0) {
337 log_error_errno(r, "could not get type: %m");
338 continue;
339 }
340
341 if (type != RTM_NEWNEIGH) {
342 log_error("type is not RTM_NEWNEIGH");
343 continue;
344 }
345
346 r = sd_rtnl_message_neigh_get_family(m, &fam);
347 if (r < 0) {
348 log_error_errno(r, "could not get family: %m");
349 continue;
350 }
351
352 if (fam != family) {
353 log_error("family is not correct");
354 continue;
355 }
356
357 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
358 if (r < 0) {
359 log_error_errno(r, "could not get ifindex: %m");
360 continue;
361 }
362
363 if (ifindex > 0 && ifi != ifindex)
364 continue;
365
366 switch (fam) {
367 case AF_INET:
368 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
369 if (r < 0)
370 continue;
371
372 break;
373 case AF_INET6:
374 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
375 if (r < 0)
376 continue;
377
378 break;
379 default:
380 continue;
381 }
382
383 if (!in_addr_equal(fam, &gw, gateway))
384 continue;
385
386 r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
387 if (r < 0)
388 continue;
389
390 r = ieee_oui(hwdb, &mac, gateway_description);
391 if (r < 0)
392 continue;
393
394 return 0;
395 }
396
397 return -ENODATA;
398 }
399
400 static int dump_gateways(
401 sd_netlink *rtnl,
402 sd_hwdb *hwdb,
403 const char *prefix,
404 int ifindex) {
405 _cleanup_free_ struct local_address *local = NULL;
406 int r, n, i;
407
408 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
409 if (n < 0)
410 return n;
411
412 for (i = 0; i < n; i++) {
413 _cleanup_free_ char *gateway = NULL, *description = NULL;
414
415 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
416 if (r < 0)
417 return r;
418
419 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
420 if (r < 0)
421 log_debug_errno(r, "Could not get description of gateway: %m");
422
423 printf("%*s%s",
424 (int) strlen(prefix),
425 i == 0 ? prefix : "",
426 gateway);
427
428 if (description)
429 printf(" (%s)", description);
430
431 /* Show interface name for the entry if we show
432 * entries for all interfaces */
433 if (ifindex <= 0) {
434 char name[IF_NAMESIZE+1];
435
436 if (if_indextoname(local[i].ifindex, name)) {
437 fputs(" on ", stdout);
438 fputs(name, stdout);
439 } else
440 printf(" on %%%i", local[i].ifindex);
441 }
442
443 fputc('\n', stdout);
444 }
445
446 return 0;
447 }
448
449 static int dump_addresses(
450 sd_netlink *rtnl,
451 const char *prefix,
452 int ifindex) {
453
454 _cleanup_free_ struct local_address *local = NULL;
455 int r, n, i;
456
457 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
458 if (n < 0)
459 return n;
460
461 for (i = 0; i < n; i++) {
462 _cleanup_free_ char *pretty = NULL;
463
464 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
465 if (r < 0)
466 return r;
467
468 printf("%*s%s",
469 (int) strlen(prefix),
470 i == 0 ? prefix : "",
471 pretty);
472
473 if (ifindex <= 0) {
474 char name[IF_NAMESIZE+1];
475
476 if (if_indextoname(local[i].ifindex, name)) {
477 fputs(" on ", stdout);
478 fputs(name, stdout);
479 } else
480 printf(" on %%%i", local[i].ifindex);
481 }
482
483 fputc('\n', stdout);
484 }
485
486 return 0;
487 }
488
489 static void dump_list(const char *prefix, char **l) {
490 char **i;
491
492 STRV_FOREACH(i, l) {
493 printf("%*s%s\n",
494 (int) strlen(prefix),
495 i == l ? prefix : "",
496 *i);
497 }
498 }
499
500 static int link_status_one(
501 sd_netlink *rtnl,
502 sd_hwdb *hwdb,
503 const char *name) {
504 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
505 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
506 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
507 _cleanup_device_unref_ sd_device *d = NULL;
508 char devid[2 + DECIMAL_STR_MAX(int)];
509 _cleanup_free_ char *t = NULL, *network = NULL;
510 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
511 const char *on_color_operational, *off_color_operational,
512 *on_color_setup, *off_color_setup;
513 _cleanup_strv_free_ char **carrier_bound_to = NULL;
514 _cleanup_strv_free_ char **carrier_bound_by = NULL;
515 struct ether_addr e;
516 unsigned iftype;
517 int r, ifindex;
518 bool have_mac;
519 uint32_t mtu;
520
521 assert(rtnl);
522 assert(name);
523
524 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
525 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
526 else {
527 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
528 if (r < 0)
529 return rtnl_log_create_error(r);
530
531 r = sd_netlink_message_append_string(req, IFLA_IFNAME, name);
532 }
533
534 if (r < 0)
535 return rtnl_log_create_error(r);
536
537 r = sd_netlink_call(rtnl, req, 0, &reply);
538 if (r < 0)
539 return log_error_errno(r, "Failed to query link: %m");
540
541 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
542 if (r < 0)
543 return rtnl_log_parse_error(r);
544
545 r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name);
546 if (r < 0)
547 return rtnl_log_parse_error(r);
548
549 r = sd_rtnl_message_link_get_type(reply, &iftype);
550 if (r < 0)
551 return rtnl_log_parse_error(r);
552
553 have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
554
555 if (have_mac) {
556 const uint8_t *p;
557 bool all_zeroes = true;
558
559 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
560 if (*p != 0) {
561 all_zeroes = false;
562 break;
563 }
564
565 if (all_zeroes)
566 have_mac = false;
567 }
568
569 sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
570
571 sd_network_link_get_operational_state(ifindex, &operational_state);
572 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
573
574 sd_network_link_get_setup_state(ifindex, &setup_state);
575 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
576
577 sd_network_link_get_dns(ifindex, &dns);
578 sd_network_link_get_domains(ifindex, &domains);
579 r = sd_network_link_get_wildcard_domain(ifindex);
580 if (r > 0) {
581 char *wildcard;
582
583 wildcard = strdup("*");
584 if (!wildcard)
585 return log_oom();
586
587 if (strv_consume(&domains, wildcard) < 0)
588 return log_oom();
589 }
590
591 sprintf(devid, "n%i", ifindex);
592
593 (void)sd_device_new_from_device_id(&d, devid);
594
595 if (d) {
596 (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
597 (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
598 (void)sd_device_get_property_value(d, "ID_PATH", &path);
599
600 r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
601 if (r < 0)
602 (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
603
604 r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
605 if (r < 0)
606 (void)sd_device_get_property_value(d, "ID_MODEL", &model);
607 }
608
609 link_get_type_string(iftype, d, &t);
610
611 sd_network_link_get_network_file(ifindex, &network);
612
613 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
614 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
615
616 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
617
618 printf(" Link File: %s\n"
619 " Network File: %s\n"
620 " Type: %s\n"
621 " State: %s%s%s (%s%s%s)\n",
622 strna(link),
623 strna(network),
624 strna(t),
625 on_color_operational, strna(operational_state), off_color_operational,
626 on_color_setup, strna(setup_state), off_color_setup);
627
628 if (path)
629 printf(" Path: %s\n", path);
630 if (driver)
631 printf(" Driver: %s\n", driver);
632 if (vendor)
633 printf(" Vendor: %s\n", vendor);
634 if (model)
635 printf(" Model: %s\n", model);
636
637 if (have_mac) {
638 _cleanup_free_ char *description = NULL;
639 char ea[ETHER_ADDR_TO_STRING_MAX];
640
641 ieee_oui(hwdb, &e, &description);
642
643 if (description)
644 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
645 else
646 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
647 }
648
649 if (mtu > 0)
650 printf(" MTU: %u\n", mtu);
651
652 dump_addresses(rtnl, " Address: ", ifindex);
653 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
654
655 if (!strv_isempty(dns))
656 dump_list(" DNS: ", dns);
657 if (!strv_isempty(domains))
658 dump_list(" Domain: ", domains);
659
660 (void) sd_network_link_get_ntp(ifindex, &ntp);
661 if (!strv_isempty(ntp))
662 dump_list(" NTP: ", ntp);
663
664 if (!strv_isempty(carrier_bound_to))
665 dump_list("Carrier Bound To: ", carrier_bound_to);
666
667 if (!strv_isempty(carrier_bound_by))
668 dump_list("Carrier Bound By: ", carrier_bound_by);
669
670 (void) sd_network_link_get_timezone(ifindex, &tz);
671 if (tz)
672 printf(" Time Zone: %s", tz);
673
674 return 0;
675 }
676
677 static int link_status(int argc, char *argv[], void *userdata) {
678 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
679 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
680 char **name;
681 int r;
682
683 r = sd_netlink_open(&rtnl);
684 if (r < 0)
685 return log_error_errno(r, "Failed to connect to netlink: %m");
686
687 r = sd_hwdb_new(&hwdb);
688 if (r < 0)
689 log_debug_errno(r, "Failed to open hardware database: %m");
690
691 if (argc <= 1 && !arg_all) {
692 _cleanup_free_ char *operational_state = NULL;
693 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
694 const char *on_color_operational, *off_color_operational;
695
696 sd_network_get_operational_state(&operational_state);
697 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
698
699 printf("%s%s%s State: %s%s%s\n",
700 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
701 on_color_operational, strna(operational_state), off_color_operational);
702
703 dump_addresses(rtnl, " Address: ", 0);
704 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
705
706 sd_network_get_dns(&dns);
707 if (!strv_isempty(dns))
708 dump_list(" DNS: ", dns);
709
710 sd_network_get_domains(&domains);
711 if (!strv_isempty(domains))
712 dump_list(" Domain: ", domains);
713
714 sd_network_get_ntp(&ntp);
715 if (!strv_isempty(ntp))
716 dump_list(" NTP: ", ntp);
717
718 return 0;
719 }
720
721 pager_open_if_enabled();
722
723 if (arg_all) {
724 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
725 _cleanup_free_ LinkInfo *links = NULL;
726 int c, i;
727
728 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
729 if (r < 0)
730 return rtnl_log_create_error(r);
731
732 r = sd_netlink_message_request_dump(req, true);
733 if (r < 0)
734 return rtnl_log_create_error(r);
735
736 r = sd_netlink_call(rtnl, req, 0, &reply);
737 if (r < 0)
738 return log_error_errno(r, "Failed to enumerate links: %m");
739
740 c = decode_and_sort_links(reply, &links);
741 if (c < 0)
742 return rtnl_log_parse_error(c);
743
744 for (i = 0; i < c; i++) {
745 if (i > 0)
746 fputc('\n', stdout);
747
748 link_status_one(rtnl, hwdb, links[i].name);
749 }
750 } else {
751 STRV_FOREACH(name, argv + 1) {
752 if (name != argv + 1)
753 fputc('\n', stdout);
754
755 link_status_one(rtnl, hwdb, *name);
756 }
757 }
758
759 return 0;
760 }
761
762 const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
763 LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
764
765 static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
766 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
767 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
768 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
769 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
770 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
771 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
772 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
773 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
774 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
775 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
776 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
777 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
778 };
779
780 DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
781
782 static char *lldp_system_caps(uint16_t cap) {
783 _cleanup_free_ char *s = NULL, *t = NULL;
784 char *capability;
785
786 t = strdup("[ ");
787 if (!t)
788 return NULL;
789
790 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
791 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
792 if (!s)
793 return NULL;
794
795 free(t);
796 t = s;
797 }
798
799 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
800 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
801 if (!s)
802 return NULL;
803
804 free(t);
805 t = s;
806 }
807
808 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
809 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
810 if (!s)
811 return NULL;
812
813 free(t);
814 t = s;
815 }
816
817 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
818 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
819 if (!s)
820 return NULL;
821
822 free(t);
823 t = s;
824 }
825
826 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
827 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
828 if (!s)
829 return NULL;
830
831 free(t);
832 t = s;
833 }
834
835 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
836 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
837 if (!s)
838 return NULL;
839
840 free(t);
841 t = s;
842 }
843
844 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
845 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
846 if (!s)
847 return NULL;
848
849 free(t);
850 t = s;
851 }
852
853 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
854 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
855 if (!s)
856 return NULL;
857
858 free(t);
859 t = s;
860 }
861
862 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
863 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
864 if (!s)
865 return NULL;
866
867 free(t);
868 t = s;
869 }
870
871 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
872 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
873 if (!s)
874 return NULL;
875
876 free(t);
877 t = s;
878 }
879
880 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
881 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
882 if (!s)
883 return NULL;
884
885 free(t);
886 }
887
888 if (!s) {
889 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
890 if (!s)
891 return NULL;
892
893 free(t);
894 }
895
896 t = strappend(s, "]");
897 if (!t)
898 return NULL;
899
900 free(s);
901 capability = t;
902
903 s = NULL;
904 t = NULL;
905
906 return capability;
907 }
908
909 static int link_lldp_status(int argc, char *argv[], void *userdata) {
910 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
911 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
912 _cleanup_free_ LinkInfo *links = NULL;
913 const char *state, *word;
914
915 double ttl = -1;
916 uint32_t capability;
917 int i, r, c, j;
918 size_t ll;
919 char **s;
920
921 pager_open_if_enabled();
922
923 r = sd_netlink_open(&rtnl);
924 if (r < 0)
925 return log_error_errno(r, "Failed to connect to netlink: %m");
926
927 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
928 if (r < 0)
929 return rtnl_log_create_error(r);
930
931 r = sd_netlink_message_request_dump(req, true);
932 if (r < 0)
933 return rtnl_log_create_error(r);
934
935 r = sd_netlink_call(rtnl, req, 0, &reply);
936 if (r < 0)
937 return log_error_errno(r, "Failed to enumerate links: %m");
938
939 c = decode_and_sort_links(reply, &links);
940 if (c < 0)
941 return rtnl_log_parse_error(c);
942
943 if (arg_legend)
944 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
945
946 for (i = j = 0; i < c; i++) {
947 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
948 _cleanup_strv_free_ char **l = NULL;
949
950 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
951 if (r < 0)
952 continue;
953
954 l = strv_split_newlines(lldp);
955 if (!l)
956 return -ENOMEM;
957
958 STRV_FOREACH(s, l) {
959 FOREACH_WORD_QUOTED(word, ll, *s, state) {
960 _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
961
962 t = strndup(word, ll);
963 if (!t)
964 return -ENOMEM;
965
966 r = split_pair(t, "=", &a, &b);
967 if (r < 0)
968 continue;
969
970 if (streq(a, "_Chassis")) {
971 r = free_and_strdup(&chassis, b);
972 if (r < 0)
973 return r;
974
975 } else if (streq(a, "_Port")) {
976 r = free_and_strdup(&port, b);
977 if (r < 0)
978 return r;
979
980 } else if (streq(a, "_TTL")) {
981 long long unsigned x = 0;
982 usec_t time;
983
984 r = safe_atollu(b, &x);
985 if (r < 0 || (usec_t) x != x)
986 return log_warning_errno(r < 0 ? r : ERANGE,
987 "Failed to parse TTL \"%s\": %m", b);
988
989 time = now(clock_boottime_or_monotonic());
990 if (x < time)
991 continue;
992
993 ttl = (double) (x - time) / USEC_PER_SEC;
994
995 } else if (streq(a, "_CAP")) {
996 sscanf(b, "%x", &capability);
997
998 cap = lldp_system_caps(capability);
999 }
1000
1001 }
1002
1003 if (ttl >= 0) {
1004 printf("%10s %24s %16s %16f %16s\n",
1005 links[i].name,
1006 strna(chassis), strna(port),
1007 ttl, cap);
1008 j++;
1009 }
1010 }
1011 }
1012
1013 if (arg_legend) {
1014 printf("\nCapability Codes:\n"
1015 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1016 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1017 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1018
1019 printf("Total entries displayed: %d\n", j);
1020 }
1021
1022 return 0;
1023 }
1024
1025 static void help(void) {
1026 printf("%s [OPTIONS...]\n\n"
1027 "Query and control the networking subsystem.\n\n"
1028 " -h --help Show this help\n"
1029 " --version Show package version\n"
1030 " --no-pager Do not pipe output into a pager\n"
1031 " --no-legend Do not show the headers and footers\n"
1032 " -a --all Show status for all links\n\n"
1033 "Commands:\n"
1034 " list List links\n"
1035 " status [LINK...] Show link status\n"
1036 " lldp Show lldp information\n"
1037 , program_invocation_short_name);
1038 }
1039
1040 static int parse_argv(int argc, char *argv[]) {
1041
1042 enum {
1043 ARG_VERSION = 0x100,
1044 ARG_NO_PAGER,
1045 ARG_NO_LEGEND,
1046 };
1047
1048 static const struct option options[] = {
1049 { "help", no_argument, NULL, 'h' },
1050 { "version", no_argument, NULL, ARG_VERSION },
1051 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1052 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1053 { "all", no_argument, NULL, 'a' },
1054 {}
1055 };
1056
1057 int c;
1058
1059 assert(argc >= 0);
1060 assert(argv);
1061
1062 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
1063
1064 switch (c) {
1065
1066 case 'h':
1067 help();
1068 return 0;
1069
1070 case ARG_VERSION:
1071 return version();
1072
1073 case ARG_NO_PAGER:
1074 arg_no_pager = true;
1075 break;
1076
1077 case ARG_NO_LEGEND:
1078 arg_legend = false;
1079 break;
1080
1081 case 'a':
1082 arg_all = true;
1083 break;
1084
1085 case '?':
1086 return -EINVAL;
1087
1088 default:
1089 assert_not_reached("Unhandled option");
1090 }
1091 }
1092
1093 return 1;
1094 }
1095
1096 static int networkctl_main(int argc, char *argv[]) {
1097 const Verb verbs[] = {
1098 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1099 { "status", 1, VERB_ANY, 0, link_status },
1100 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
1101 {}
1102 };
1103
1104 return dispatch_verb(argc, argv, verbs, NULL);
1105 }
1106
1107 int main(int argc, char* argv[]) {
1108 int r;
1109
1110 log_parse_environment();
1111 log_open();
1112
1113 r = parse_argv(argc, argv);
1114 if (r <= 0)
1115 goto finish;
1116
1117 r = networkctl_main(argc, argv);
1118
1119 finish:
1120 pager_close();
1121
1122 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1123 }