]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
network: add malloc-assertion in test
[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;
9085f64a
LP
511 struct ether_addr e;
512 unsigned iftype;
513 int r, ifindex;
514 bool have_mac;
515 uint32_t mtu;
516
517 assert(rtnl);
518 assert(udev);
519 assert(name);
520
521 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
522 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
523 else {
524 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
525 if (r < 0)
526 return rtnl_log_create_error(r);
527
528 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
529 }
530
531 if (r < 0)
532 return rtnl_log_create_error(r);
533
534 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
535 if (r < 0)
536 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
537
538 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
539 if (r < 0)
540 return rtnl_log_parse_error(r);
541
542 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
543 if (r < 0)
544 return rtnl_log_parse_error(r);
545
546 r = sd_rtnl_message_link_get_type(reply, &iftype);
547 if (r < 0)
548 return rtnl_log_parse_error(r);
549
550 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
551
552 if (have_mac) {
553 const uint8_t *p;
554 bool all_zeroes = true;
555
556 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
557 if (*p != 0) {
558 all_zeroes = false;
559 break;
560 }
561
562 if (all_zeroes)
563 have_mac = false;
564 }
565
566 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
567
d6731e4c 568 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
569 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
570
571 sd_network_link_get_setup_state(ifindex, &setup_state);
572 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 573
d6731e4c
TG
574 sd_network_link_get_dns(ifindex, &dns);
575 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 576 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
577 r = sd_network_link_get_wildcard_domain(ifindex);
578 if (r > 0) {
579 char *wildcard;
580
581 wildcard = strdup("*");
1405434b
LP
582 if (!wildcard)
583 return log_oom();
584
585 if (strv_consume(&domains, wildcard) < 0)
586 return log_oom();
67272d15 587 }
9085f64a
LP
588
589 sprintf(devid, "n%i", ifindex);
590 d = udev_device_new_from_device_id(udev, devid);
9085f64a 591 if (d) {
af5effc4 592 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
9085f64a
LP
593 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
594 path = udev_device_get_property_value(d, "ID_PATH");
595
596 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
597 if (!vendor)
598 vendor = udev_device_get_property_value(d, "ID_VENDOR");
599
600 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
601 if (!model)
602 model = udev_device_get_property_value(d, "ID_MODEL");
603 }
604
b1acce80
LP
605 link_get_type_string(iftype, d, &t);
606
373d9f17 607 sd_network_link_get_network_file(ifindex, &network);
df3fb561 608
d57c365b 609 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 610
af5effc4
TG
611 printf(" Link File: %s\n"
612 "Network File: %s\n"
373d9f17 613 " Type: %s\n"
d57c365b 614 " State: %s%s%s (%s%s%s)\n",
af5effc4 615 strna(link),
373d9f17 616 strna(network),
9085f64a 617 strna(t),
d57c365b
LP
618 on_color_operational, strna(operational_state), off_color_operational,
619 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
620
621 if (path)
622 printf(" Path: %s\n", path);
623 if (driver)
624 printf(" Driver: %s\n", driver);
625 if (vendor)
626 printf(" Vendor: %s\n", vendor);
627 if (model)
628 printf(" Model: %s\n", model);
629
db73295a 630 if (have_mac) {
888943fc 631 _cleanup_free_ char *description = NULL;
db73295a 632 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc
LP
633
634 ieee_oui(hwdb, &e, &description);
635
636 if (description)
637 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
638 else
639 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
db73295a 640 }
9085f64a
LP
641
642 if (mtu > 0)
643 printf(" MTU: %u\n", mtu);
644
645 dump_addresses(rtnl, " Address: ", ifindex);
888943fc 646 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
9085f64a
LP
647
648 if (!strv_isempty(dns))
649 dump_list(" DNS: ", dns);
2301cb9f 650 if (!strv_isempty(domains))
c627729f 651 dump_list(" Domain: ", domains);
9085f64a
LP
652 if (!strv_isempty(ntp))
653 dump_list(" NTP: ", ntp);
654
655 return 0;
656}
657
266b5389 658static int link_status(int argc, char *argv[], void *userdata) {
81fd1dd3 659 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
ee8c4568
LP
660 _cleanup_udev_unref_ struct udev *udev = NULL;
661 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
662 char **name;
663 int r;
664
f7d68aa8 665 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
666 if (r < 0)
667 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8
LP
668
669 udev = udev_new();
4a62c710
MS
670 if (!udev)
671 return log_error_errno(errno, "Failed to connect to udev: %m");
f7d68aa8 672
81fd1dd3
TG
673 r = sd_hwdb_new(&hwdb);
674 if (r < 0)
675 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 676
266b5389 677 if (argc <= 1 && !arg_all) {
ee8c4568 678 _cleanup_free_ char *operational_state = NULL;
c627729f 679 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
e92da1e5 680 const char *on_color_operational, *off_color_operational;
ee8c4568 681
03cc0fd1 682 sd_network_get_operational_state(&operational_state);
e92da1e5 683 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 684
b1acce80
LP
685 printf("%s%s%s State: %s%s%s\n",
686 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
687 on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 688
1693a943 689 dump_addresses(rtnl, " Address: ", 0);
69fb1176
LP
690 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
691
03cc0fd1
LP
692 sd_network_get_dns(&dns);
693 if (!strv_isempty(dns))
694 dump_list(" DNS: ", dns);
695
c627729f
LP
696 sd_network_get_domains(&domains);
697 if (!strv_isempty(domains))
698 dump_list(" Domain: ", domains);
699
ddb7f7fc 700 sd_network_get_ntp(&ntp);
03cc0fd1
LP
701 if (!strv_isempty(ntp))
702 dump_list(" NTP: ", ntp);
ee8c4568 703
ee8c4568
LP
704 return 0;
705 }
706
707 pager_open_if_enabled();
708
9085f64a 709 if (arg_all) {
ee8c4568 710 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
711 _cleanup_free_ LinkInfo *links = NULL;
712 int c, i;
ee8c4568 713
9085f64a
LP
714 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
715 if (r < 0)
716 return rtnl_log_create_error(r);
ee8c4568 717
9085f64a 718 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
719 if (r < 0)
720 return rtnl_log_create_error(r);
721
722 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
723 if (r < 0)
724 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 725
6d0c65ff
LP
726 c = decode_and_sort_links(reply, &links);
727 if (c < 0)
728 return rtnl_log_parse_error(c);
ee8c4568 729
6d0c65ff
LP
730 for (i = 0; i < c; i++) {
731 if (i > 0)
9085f64a 732 fputc('\n', stdout);
ee8c4568 733
69fb1176 734 link_status_one(rtnl, udev, hwdb, links[i].name);
ee8c4568 735 }
69fb1176 736 } else {
266b5389
TG
737 STRV_FOREACH(name, argv + 1) {
738 if (name != argv + 1)
69fb1176 739 fputc('\n', stdout);
ee8c4568 740
69fb1176
LP
741 link_status_one(rtnl, udev, hwdb, *name);
742 }
ee8c4568
LP
743 }
744
745 return 0;
746}
747
49699bac
SS
748const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
749LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
750
751static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
752 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
753 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
754 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
755 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
756 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
757 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
758 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
759 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
760 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
761 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
762 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
763 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
764};
765
766DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
767
768static char *lldp_system_caps(uint16_t cap) {
769 _cleanup_free_ char *s = NULL, *t = NULL;
770 char *capability;
771
772 t = strdup("[ ");
773 if (!t)
774 return NULL;
775
776 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
777 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
778 if (!s)
779 return NULL;
780
781 free(t);
782 t = s;
783 }
784
785 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
786 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
787 if (!s)
788 return NULL;
789
790 free(t);
791 t = s;
792 }
793
794 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
795 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
796 if (!s)
797 return NULL;
798
799 free(t);
800 t = s;
801 }
802
803 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
804 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
805 if (!s)
806 return NULL;
807
808 free(t);
809 t = s;
810 }
811
812 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
813 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
814 if (!s)
815 return NULL;
816
817 free(t);
818 t = s;
819 }
820
821 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
822 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
823 if (!s)
824 return NULL;
825
826 free(t);
827 t = s;
828 }
829
830 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
831 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
832 if (!s)
833 return NULL;
834
835 free(t);
836 t = s;
837 }
838
839 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
840 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
841 if (!s)
842 return NULL;
843
844 free(t);
845 t = s;
846 }
847
848 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
849 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
850 if (!s)
851 return NULL;
852
853 free(t);
854 t = s;
855 }
856
857 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
858 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
859 if (!s)
860 return NULL;
861
862 free(t);
863 t = s;
864 }
865
866 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
867 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
868 if (!s)
869 return NULL;
870
871 free(t);
872 }
873
874 if (!s) {
875 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
876 if (!s)
877 return NULL;
878
879 free(t);
880 }
881
882 t = strappend(s, "]");
883 if (!s)
884 return NULL;
885
886 free(s);
887 capability = t;
888
889 s = NULL;
890 t = NULL;
891
892 return capability;
893}
894
895static int link_lldp_status(int argc, char *argv[], void *userdata) {
896 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
897 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
898 _cleanup_free_ LinkInfo *links = NULL;
899 const char *state, *word;
900
901 usec_t time, until, ttl;
902 uint32_t capability;
903 char buf[LINE_MAX];
904 int i, r, c, j;
905 size_t ll;
906 char **s;
907
908 pager_open_if_enabled();
909
910 r = sd_rtnl_open(&rtnl, 0);
911 if (r < 0)
912 return log_error_errno(r, "Failed to connect to netlink: %m");
913
914 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
915 if (r < 0)
916 return rtnl_log_create_error(r);
917
918 r = sd_rtnl_message_request_dump(req, true);
919 if (r < 0)
920 return rtnl_log_create_error(r);
921
922 r = sd_rtnl_call(rtnl, req, 0, &reply);
923 if (r < 0)
924 return log_error_errno(r, "Failed to enumerate links: %m");
925
926 c = decode_and_sort_links(reply, &links);
927 if (c < 0)
928 return rtnl_log_parse_error(c);
929
19727828
TG
930 if (arg_legend)
931 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
49699bac
SS
932
933 for (i = j = 0; i < c; i++) {
934 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
935 _cleanup_strv_free_ char **l = NULL;
936
937 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
938 if (r < 0)
939 continue;
940
941 l = strv_split_newlines(lldp);
942 if (!l)
943 return -ENOMEM;
944
945 STRV_FOREACH(s, l) {
946 FOREACH_WORD_QUOTED(word, ll, *s, state) {
947 _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
948
949 t = strndup(word, ll);
950 if (!t)
951 return -ENOMEM;
952
953 r = split_pair(t, "=", &a, &b);
954 if (r < 0)
955 continue;
956
957 if (streq(a, "_Chassis")) {
958
959 memzero(buf, LINE_MAX);
960
961 chassis = strdup(b);
962 if (!chassis)
963 return -ENOMEM;
964
965 } else if (streq(a, "_Port")) {
966
967 port = strdup(b);
968 if (!port)
969 return -ENOMEM;
970
971 } else if (streq(a, "_TTL")) {
972
973 time = now(CLOCK_BOOTTIME);
974
975 sscanf(b, "%lu", &until);
976
977 ttl = (until - time) / USEC_PER_SEC;
978
979
980 } else if (streq(a, "_CAP")) {
981 sscanf(b, "%x", &capability);
982
983 cap = lldp_system_caps(capability);
984 }
985
986 }
987
988 if (until > time) {
989 printf("%10s %24s %16s %16lu %16s\n", links[i].name, chassis, port, ttl, cap);
990 j++;
991 }
992 }
993 }
994
19727828
TG
995 if (arg_legend) {
996 printf("\nCapability Codes:\n"
997 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
998 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
999 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1000
1001 printf("Total entries displayed: %d\n", j);
1002 }
49699bac
SS
1003
1004 return 0;
1005}
1006
ee8c4568
LP
1007static void help(void) {
1008 printf("%s [OPTIONS...]\n\n"
1009 "Query and control the networking subsystem.\n\n"
1010 " -h --help Show this help\n"
9085f64a
LP
1011 " --version Show package version\n"
1012 " --no-pager Do not pipe output into a pager\n"
1013 " --no-legend Do not show the headers and footers\n"
1014 " -a --all Show status for all links\n\n"
ee8c4568
LP
1015 "Commands:\n"
1016 " list List links\n"
1017 " status LINK Show link status\n"
49699bac 1018 " lldp Show lldp information\n"
ee8c4568
LP
1019 , program_invocation_short_name);
1020}
1021
1022static int parse_argv(int argc, char *argv[]) {
1023
1024 enum {
1025 ARG_VERSION = 0x100,
1026 ARG_NO_PAGER,
1027 ARG_NO_LEGEND,
1028 };
1029
1030 static const struct option options[] = {
1031 { "help", no_argument, NULL, 'h' },
1032 { "version", no_argument, NULL, ARG_VERSION },
1033 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1034 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1035 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1036 {}
1037 };
1038
1039 int c;
1040
1041 assert(argc >= 0);
1042 assert(argv);
1043
9085f64a 1044 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1045
1046 switch (c) {
1047
1048 case 'h':
1049 help();
1050 return 0;
1051
1052 case ARG_VERSION:
1053 puts(PACKAGE_STRING);
1054 puts(SYSTEMD_FEATURES);
1055 return 0;
1056
1057 case ARG_NO_PAGER:
1058 arg_no_pager = true;
1059 break;
1060
1061 case ARG_NO_LEGEND:
1062 arg_legend = false;
1063 break;
1064
9085f64a
LP
1065 case 'a':
1066 arg_all = true;
1067 break;
1068
ee8c4568
LP
1069 case '?':
1070 return -EINVAL;
1071
1072 default:
1073 assert_not_reached("Unhandled option");
1074 }
1075 }
1076
1077 return 1;
1078}
1079
1080static int networkctl_main(int argc, char *argv[]) {
266b5389
TG
1081 const Verb verbs[] = {
1082 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1083 { "status", 1, VERB_ANY, 0, link_status },
49699bac 1084 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
266b5389 1085 {}
ee8c4568
LP
1086 };
1087
266b5389 1088 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1089}
1090
1091int main(int argc, char* argv[]) {
1092 int r;
1093
1094 log_parse_environment();
1095 log_open();
1096
1097 r = parse_argv(argc, argv);
1098 if (r <= 0)
1099 goto finish;
1100
1101 r = networkctl_main(argc, argv);
1102
1103finish:
1104 pager_close();
1105
1106 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1107}