]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
libsystemd: add sd-device library
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
ee8c4568
LP
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>
1693a943 24#include <net/if.h>
ee8c4568
LP
25
26#include "sd-network.h"
27#include "sd-rtnl.h"
81fd1dd3 28#include "sd-hwdb.h"
ee8c4568
LP
29#include "libudev.h"
30
81fd1dd3 31#include "strv.h"
ee8c4568
LP
32#include "build.h"
33#include "util.h"
34#include "pager.h"
49699bac 35#include "lldp.h"
ee8c4568
LP
36#include "rtnl-util.h"
37#include "udev-util.h"
81fd1dd3 38#include "hwdb-util.h"
ee8c4568
LP
39#include "arphrd-list.h"
40#include "local-addresses.h"
db73295a 41#include "socket-util.h"
d8500c53 42#include "ether-addr-util.h"
266b5389 43#include "verbs.h"
ee8c4568
LP
44
45static bool arg_no_pager = false;
46static bool arg_legend = true;
9085f64a 47static bool arg_all = false;
ee8c4568
LP
48
49static void pager_open_if_enabled(void) {
50
51 if (arg_no_pager)
52 return;
53
54 pager_open(false);
55}
56
57static 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
6d0c65ff
LP
99typedef struct LinkInfo {
100 const char *name;
101 int ifindex;
102 unsigned iftype;
103} LinkInfo;
104
105static 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
111static 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
a6a4f528 151 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
152
153 *ret = links;
154 links = NULL;
155
156 return (int) c;
157}
158
d57c365b
LP
159static 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
173static 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
266b5389 190static int list_links(int argc, char *argv[], void *userdata) {
ee8c4568
LP
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;
6d0c65ff
LP
194 _cleanup_free_ LinkInfo *links = NULL;
195 int r, c, i;
ee8c4568
LP
196
197 pager_open_if_enabled();
198
199 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
200 if (r < 0)
201 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568
LP
202
203 udev = udev_new();
4a62c710
MS
204 if (!udev)
205 return log_error_errno(errno, "Failed to connect to udev: %m");
ee8c4568
LP
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);
f647962d
MS
216 if (r < 0)
217 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568
LP
218
219 if (arg_legend)
3e3db0ee 220 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 221
6d0c65ff
LP
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++) {
ab1525bc 227 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
ee8c4568 228 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
d57c365b
LP
229 const char *on_color_operational, *off_color_operational,
230 *on_color_setup, *off_color_setup;
df3fb561 231 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 232 _cleanup_free_ char *t = NULL;
ee8c4568 233
d6731e4c 234 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
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);
ee8c4568 239
6d0c65ff 240 sprintf(devid, "n%i", links[i].ifindex);
ee8c4568
LP
241 d = udev_device_new_from_device_id(udev, devid);
242
6d0c65ff 243 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 244
3e3db0ee 245 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
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);
ee8c4568
LP
249 }
250
251 if (arg_legend)
6d0c65ff 252 printf("\n%i links listed.\n", c);
ee8c4568
LP
253
254 return 0;
255}
256
c09da729 257/* IEEE Organizationally Unique Identifier vendor string */
81fd1dd3
TG
258static 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);
c09da729 264
888943fc
LP
265 if (!hwdb)
266 return -EINVAL;
267
81fd1dd3
TG
268 if (!mac)
269 return -EINVAL;
270
c09da729
TG
271 /* skip commonly misused 00:00:00 (Xerox) prefix */
272 if (memcmp(mac, "\0\0\0", 3) == 0)
273 return -EINVAL;
274
81fd1dd3 275 snprintf(modalias, sizeof(modalias), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 276
81fd1dd3
TG
277 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
278 if (r < 0)
279 return r;
c09da729 280
81fd1dd3
TG
281 desc = strdup(description);
282 if (!desc)
283 return -ENOMEM;
c09da729 284
81fd1dd3
TG
285 *ret = desc;
286
287 return 0;
c09da729
TG
288}
289
69fb1176
LP
290static int get_gateway_description(
291 sd_rtnl *rtnl,
81fd1dd3 292 sd_hwdb *hwdb,
69fb1176
LP
293 int ifindex,
294 int family,
295 union in_addr_union *gateway,
296 char **gateway_description) {
c09da729
TG
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) {
144232a8 355 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
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
69fb1176
LP
396static int dump_gateways(
397 sd_rtnl *rtnl,
81fd1dd3 398 sd_hwdb *hwdb,
69fb1176
LP
399 const char *prefix,
400 int ifindex) {
b6a3ca6d
TG
401 _cleanup_free_ struct local_address *local = NULL;
402 int r, n, i;
c09da729 403
b6a3ca6d
TG
404 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
405 if (n < 0)
406 return n;
c09da729 407
b6a3ca6d
TG
408 for (i = 0; i < n; i++) {
409 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 410
b6a3ca6d 411 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 412 if (r < 0)
b6a3ca6d 413 return r;
c09da729 414
69fb1176 415 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
c09da729 416 if (r < 0)
b6a3ca6d 417 log_debug_errno(r, "Could not get description of gateway: %m");
c09da729 418
1693a943
LP
419 printf("%*s%s",
420 (int) strlen(prefix),
421 i == 0 ? prefix : "",
422 gateway);
423
b6a3ca6d 424 if (description)
1693a943
LP
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);
c09da729
TG
440 }
441
442 return 0;
443}
444
69fb1176
LP
445static int dump_addresses(
446 sd_rtnl *rtnl,
447 const char *prefix,
448 int ifindex) {
449
ee8c4568
LP
450 _cleanup_free_ struct local_address *local = NULL;
451 int r, n, i;
452
1d050e1e 453 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
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
1693a943 464 printf("%*s%s",
ee8c4568
LP
465 (int) strlen(prefix),
466 i == 0 ? prefix : "",
467 pretty);
1693a943
LP
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);
ee8c4568
LP
480 }
481
482 return 0;
483}
484
485static 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
69fb1176
LP
496static int link_status_one(
497 sd_rtnl *rtnl,
498 struct udev *udev,
81fd1dd3 499 sd_hwdb *hwdb,
69fb1176
LP
500 const char *name) {
501
2301cb9f 502 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
6c03d27d 503 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
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)];
373d9f17 507 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 508 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
509 const char *on_color_operational, *off_color_operational,
510 *on_color_setup, *off_color_setup;
0d4ad91d
AR
511 _cleanup_strv_free_ char **carrier_bound_to = NULL;
512 _cleanup_strv_free_ char **carrier_bound_by = NULL;
9085f64a
LP
513 struct ether_addr e;
514 unsigned iftype;
515 int r, ifindex;
516 bool have_mac;
517 uint32_t mtu;
518
519 assert(rtnl);
520 assert(udev);
521 assert(name);
522
523 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
524 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
525 else {
526 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
527 if (r < 0)
528 return rtnl_log_create_error(r);
529
530 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
531 }
532
533 if (r < 0)
534 return rtnl_log_create_error(r);
535
536 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
537 if (r < 0)
538 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
539
540 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
541 if (r < 0)
542 return rtnl_log_parse_error(r);
543
544 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
545 if (r < 0)
546 return rtnl_log_parse_error(r);
547
548 r = sd_rtnl_message_link_get_type(reply, &iftype);
549 if (r < 0)
550 return rtnl_log_parse_error(r);
551
552 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
553
554 if (have_mac) {
555 const uint8_t *p;
556 bool all_zeroes = true;
557
558 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
559 if (*p != 0) {
560 all_zeroes = false;
561 break;
562 }
563
564 if (all_zeroes)
565 have_mac = false;
566 }
567
568 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
569
d6731e4c 570 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
571 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
572
573 sd_network_link_get_setup_state(ifindex, &setup_state);
574 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 575
d6731e4c
TG
576 sd_network_link_get_dns(ifindex, &dns);
577 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 578 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
579 r = sd_network_link_get_wildcard_domain(ifindex);
580 if (r > 0) {
581 char *wildcard;
582
583 wildcard = strdup("*");
1405434b
LP
584 if (!wildcard)
585 return log_oom();
586
587 if (strv_consume(&domains, wildcard) < 0)
588 return log_oom();
67272d15 589 }
9085f64a
LP
590
591 sprintf(devid, "n%i", ifindex);
592 d = udev_device_new_from_device_id(udev, devid);
9085f64a 593 if (d) {
af5effc4 594 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
9085f64a
LP
595 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
596 path = udev_device_get_property_value(d, "ID_PATH");
597
598 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
599 if (!vendor)
600 vendor = udev_device_get_property_value(d, "ID_VENDOR");
601
602 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
603 if (!model)
604 model = udev_device_get_property_value(d, "ID_MODEL");
605 }
606
b1acce80
LP
607 link_get_type_string(iftype, d, &t);
608
373d9f17 609 sd_network_link_get_network_file(ifindex, &network);
df3fb561 610
0d4ad91d
AR
611 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
612 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
613
d57c365b 614 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 615
0d4ad91d
AR
616 printf(" Link File: %s\n"
617 " Network File: %s\n"
618 " Type: %s\n"
619 " State: %s%s%s (%s%s%s)\n",
af5effc4 620 strna(link),
373d9f17 621 strna(network),
9085f64a 622 strna(t),
d57c365b
LP
623 on_color_operational, strna(operational_state), off_color_operational,
624 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
625
626 if (path)
0d4ad91d 627 printf(" Path: %s\n", path);
9085f64a 628 if (driver)
0d4ad91d 629 printf(" Driver: %s\n", driver);
9085f64a 630 if (vendor)
0d4ad91d 631 printf(" Vendor: %s\n", vendor);
9085f64a 632 if (model)
0d4ad91d 633 printf(" Model: %s\n", model);
9085f64a 634
db73295a 635 if (have_mac) {
888943fc 636 _cleanup_free_ char *description = NULL;
db73295a 637 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc
LP
638
639 ieee_oui(hwdb, &e, &description);
640
641 if (description)
0d4ad91d 642 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
888943fc 643 else
0d4ad91d 644 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
db73295a 645 }
9085f64a
LP
646
647 if (mtu > 0)
0d4ad91d 648 printf(" MTU: %u\n", mtu);
9085f64a 649
0d4ad91d
AR
650 dump_addresses(rtnl, " Address: ", ifindex);
651 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
9085f64a
LP
652
653 if (!strv_isempty(dns))
0d4ad91d 654 dump_list(" DNS: ", dns);
2301cb9f 655 if (!strv_isempty(domains))
0d4ad91d 656 dump_list(" Domain: ", domains);
9085f64a 657 if (!strv_isempty(ntp))
0d4ad91d
AR
658 dump_list(" NTP: ", ntp);
659
660 if (!strv_isempty(carrier_bound_to))
661 dump_list("Carrier Bound To: ", carrier_bound_to);
662
663 if (!strv_isempty(carrier_bound_by))
664 dump_list("Carrier Bound By: ", carrier_bound_by);
9085f64a
LP
665
666 return 0;
667}
668
266b5389 669static int link_status(int argc, char *argv[], void *userdata) {
81fd1dd3 670 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
ee8c4568
LP
671 _cleanup_udev_unref_ struct udev *udev = NULL;
672 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
673 char **name;
674 int r;
675
f7d68aa8 676 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
677 if (r < 0)
678 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8
LP
679
680 udev = udev_new();
4a62c710
MS
681 if (!udev)
682 return log_error_errno(errno, "Failed to connect to udev: %m");
f7d68aa8 683
81fd1dd3
TG
684 r = sd_hwdb_new(&hwdb);
685 if (r < 0)
686 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 687
266b5389 688 if (argc <= 1 && !arg_all) {
ee8c4568 689 _cleanup_free_ char *operational_state = NULL;
c627729f 690 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
e92da1e5 691 const char *on_color_operational, *off_color_operational;
ee8c4568 692
03cc0fd1 693 sd_network_get_operational_state(&operational_state);
e92da1e5 694 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 695
b1acce80
LP
696 printf("%s%s%s State: %s%s%s\n",
697 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
698 on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 699
1693a943 700 dump_addresses(rtnl, " Address: ", 0);
69fb1176
LP
701 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
702
03cc0fd1
LP
703 sd_network_get_dns(&dns);
704 if (!strv_isempty(dns))
705 dump_list(" DNS: ", dns);
706
c627729f
LP
707 sd_network_get_domains(&domains);
708 if (!strv_isempty(domains))
709 dump_list(" Domain: ", domains);
710
ddb7f7fc 711 sd_network_get_ntp(&ntp);
03cc0fd1
LP
712 if (!strv_isempty(ntp))
713 dump_list(" NTP: ", ntp);
ee8c4568 714
ee8c4568
LP
715 return 0;
716 }
717
718 pager_open_if_enabled();
719
9085f64a 720 if (arg_all) {
ee8c4568 721 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
722 _cleanup_free_ LinkInfo *links = NULL;
723 int c, i;
ee8c4568 724
9085f64a
LP
725 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
726 if (r < 0)
727 return rtnl_log_create_error(r);
ee8c4568 728
9085f64a 729 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
730 if (r < 0)
731 return rtnl_log_create_error(r);
732
733 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
734 if (r < 0)
735 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 736
6d0c65ff
LP
737 c = decode_and_sort_links(reply, &links);
738 if (c < 0)
739 return rtnl_log_parse_error(c);
ee8c4568 740
6d0c65ff
LP
741 for (i = 0; i < c; i++) {
742 if (i > 0)
9085f64a 743 fputc('\n', stdout);
ee8c4568 744
69fb1176 745 link_status_one(rtnl, udev, hwdb, links[i].name);
ee8c4568 746 }
69fb1176 747 } else {
266b5389
TG
748 STRV_FOREACH(name, argv + 1) {
749 if (name != argv + 1)
69fb1176 750 fputc('\n', stdout);
ee8c4568 751
69fb1176
LP
752 link_status_one(rtnl, udev, hwdb, *name);
753 }
ee8c4568
LP
754 }
755
756 return 0;
757}
758
49699bac
SS
759const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
760LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
761
762static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
763 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
764 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
765 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
766 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
767 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
768 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
769 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
770 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
771 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
772 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
773 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
774 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
775};
776
777DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
778
779static char *lldp_system_caps(uint16_t cap) {
780 _cleanup_free_ char *s = NULL, *t = NULL;
781 char *capability;
782
783 t = strdup("[ ");
784 if (!t)
785 return NULL;
786
787 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
788 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
789 if (!s)
790 return NULL;
791
792 free(t);
793 t = s;
794 }
795
796 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
797 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
798 if (!s)
799 return NULL;
800
801 free(t);
802 t = s;
803 }
804
805 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
806 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
807 if (!s)
808 return NULL;
809
810 free(t);
811 t = s;
812 }
813
814 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
815 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
816 if (!s)
817 return NULL;
818
819 free(t);
820 t = s;
821 }
822
823 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
824 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
825 if (!s)
826 return NULL;
827
828 free(t);
829 t = s;
830 }
831
832 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
833 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
834 if (!s)
835 return NULL;
836
837 free(t);
838 t = s;
839 }
840
841 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
842 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
843 if (!s)
844 return NULL;
845
846 free(t);
847 t = s;
848 }
849
850 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
851 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
852 if (!s)
853 return NULL;
854
855 free(t);
856 t = s;
857 }
858
859 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
860 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
861 if (!s)
862 return NULL;
863
864 free(t);
865 t = s;
866 }
867
868 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
869 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
870 if (!s)
871 return NULL;
872
873 free(t);
874 t = s;
875 }
876
877 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
878 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
879 if (!s)
880 return NULL;
881
882 free(t);
883 }
884
885 if (!s) {
886 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
887 if (!s)
888 return NULL;
889
890 free(t);
891 }
892
893 t = strappend(s, "]");
fbee1d85 894 if (!t)
49699bac
SS
895 return NULL;
896
897 free(s);
898 capability = t;
899
900 s = NULL;
901 t = NULL;
902
903 return capability;
904}
905
906static int link_lldp_status(int argc, char *argv[], void *userdata) {
907 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
908 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
909 _cleanup_free_ LinkInfo *links = NULL;
910 const char *state, *word;
911
ba52f15a 912 double ttl = -1;
49699bac 913 uint32_t capability;
49699bac
SS
914 int i, r, c, j;
915 size_t ll;
916 char **s;
917
918 pager_open_if_enabled();
919
920 r = sd_rtnl_open(&rtnl, 0);
921 if (r < 0)
922 return log_error_errno(r, "Failed to connect to netlink: %m");
923
924 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
925 if (r < 0)
926 return rtnl_log_create_error(r);
927
928 r = sd_rtnl_message_request_dump(req, true);
929 if (r < 0)
930 return rtnl_log_create_error(r);
931
932 r = sd_rtnl_call(rtnl, req, 0, &reply);
933 if (r < 0)
934 return log_error_errno(r, "Failed to enumerate links: %m");
935
936 c = decode_and_sort_links(reply, &links);
937 if (c < 0)
938 return rtnl_log_parse_error(c);
939
19727828
TG
940 if (arg_legend)
941 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
49699bac
SS
942
943 for (i = j = 0; i < c; i++) {
944 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
945 _cleanup_strv_free_ char **l = NULL;
946
947 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
948 if (r < 0)
949 continue;
950
951 l = strv_split_newlines(lldp);
952 if (!l)
953 return -ENOMEM;
954
955 STRV_FOREACH(s, l) {
956 FOREACH_WORD_QUOTED(word, ll, *s, state) {
957 _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
958
959 t = strndup(word, ll);
960 if (!t)
961 return -ENOMEM;
962
963 r = split_pair(t, "=", &a, &b);
964 if (r < 0)
965 continue;
966
967 if (streq(a, "_Chassis")) {
b4e3d5e1
ZJS
968 r = free_and_strdup(&chassis, b);
969 if (r < 0)
970 return r;
49699bac
SS
971
972 } else if (streq(a, "_Port")) {
b4e3d5e1
ZJS
973 r = free_and_strdup(&port, b);
974 if (r < 0)
975 return r;
49699bac
SS
976
977 } else if (streq(a, "_TTL")) {
a7f7d1bd 978 long long unsigned x = 0;
ba52f15a 979 usec_t time;
49699bac 980
ba52f15a
ZJS
981 r = safe_atollu(b, &x);
982 if (r < 0 || (usec_t) x != x)
983 return log_warning_errno(r < 0 ? r : ERANGE,
984 "Failed to parse TTL \"%s\": %m", b);
49699bac 985
ba52f15a
ZJS
986 time = now(CLOCK_BOOTTIME);
987 if (x < time)
988 continue;
49699bac 989
ba52f15a 990 ttl = (double) (x - time) / USEC_PER_SEC;
49699bac
SS
991
992 } else if (streq(a, "_CAP")) {
993 sscanf(b, "%x", &capability);
994
995 cap = lldp_system_caps(capability);
996 }
997
998 }
999
ba52f15a
ZJS
1000 if (ttl >= 0) {
1001 printf("%10s %24s %16s %16f %16s\n",
1002 links[i].name,
1003 strna(chassis), strna(port),
1004 ttl, cap);
49699bac
SS
1005 j++;
1006 }
1007 }
1008 }
1009
19727828
TG
1010 if (arg_legend) {
1011 printf("\nCapability Codes:\n"
1012 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1013 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1014 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1015
1016 printf("Total entries displayed: %d\n", j);
1017 }
49699bac
SS
1018
1019 return 0;
1020}
1021
ee8c4568
LP
1022static void help(void) {
1023 printf("%s [OPTIONS...]\n\n"
1024 "Query and control the networking subsystem.\n\n"
1025 " -h --help Show this help\n"
9085f64a
LP
1026 " --version Show package version\n"
1027 " --no-pager Do not pipe output into a pager\n"
1028 " --no-legend Do not show the headers and footers\n"
1029 " -a --all Show status for all links\n\n"
ee8c4568
LP
1030 "Commands:\n"
1031 " list List links\n"
d9000fd3 1032 " status [LINK...] Show link status\n"
49699bac 1033 " lldp Show lldp information\n"
ee8c4568
LP
1034 , program_invocation_short_name);
1035}
1036
1037static int parse_argv(int argc, char *argv[]) {
1038
1039 enum {
1040 ARG_VERSION = 0x100,
1041 ARG_NO_PAGER,
1042 ARG_NO_LEGEND,
1043 };
1044
1045 static const struct option options[] = {
1046 { "help", no_argument, NULL, 'h' },
1047 { "version", no_argument, NULL, ARG_VERSION },
1048 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1049 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1050 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1051 {}
1052 };
1053
1054 int c;
1055
1056 assert(argc >= 0);
1057 assert(argv);
1058
9085f64a 1059 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1060
1061 switch (c) {
1062
1063 case 'h':
1064 help();
1065 return 0;
1066
1067 case ARG_VERSION:
1068 puts(PACKAGE_STRING);
1069 puts(SYSTEMD_FEATURES);
1070 return 0;
1071
1072 case ARG_NO_PAGER:
1073 arg_no_pager = true;
1074 break;
1075
1076 case ARG_NO_LEGEND:
1077 arg_legend = false;
1078 break;
1079
9085f64a
LP
1080 case 'a':
1081 arg_all = true;
1082 break;
1083
ee8c4568
LP
1084 case '?':
1085 return -EINVAL;
1086
1087 default:
1088 assert_not_reached("Unhandled option");
1089 }
1090 }
1091
1092 return 1;
1093}
1094
1095static int networkctl_main(int argc, char *argv[]) {
266b5389
TG
1096 const Verb verbs[] = {
1097 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1098 { "status", 1, VERB_ANY, 0, link_status },
49699bac 1099 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
266b5389 1100 {}
ee8c4568
LP
1101 };
1102
266b5389 1103 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1104}
1105
1106int main(int argc, char* argv[]) {
1107 int r;
1108
1109 log_parse_environment();
1110 log_open();
1111
1112 r = parse_argv(argc, argv);
1113 if (r <= 0)
1114 goto finish;
1115
1116 r = networkctl_main(argc, argv);
1117
1118finish:
1119 pager_close();
1120
1121 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1122}