]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[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
ee8c4568 22#include <getopt.h>
1693a943 23#include <net/if.h>
3f6fd1ba 24#include <stdbool.h>
ee8c4568 25
914d6c09 26#include "sd-device.h"
3f6fd1ba
LP
27#include "sd-hwdb.h"
28#include "sd-netlink.h"
29#include "sd-network.h"
ee8c4568 30
3f6fd1ba 31#include "arphrd-list.h"
914d6c09 32#include "device-util.h"
3f6fd1ba 33#include "ether-addr-util.h"
81fd1dd3 34#include "hwdb-util.h"
3f6fd1ba 35#include "lldp.h"
ee8c4568 36#include "local-addresses.h"
3f6fd1ba
LP
37#include "netlink-util.h"
38#include "pager.h"
6bedfcbb 39#include "parse-util.h"
db73295a 40#include "socket-util.h"
07630cea 41#include "string-util.h"
3f6fd1ba 42#include "strv.h"
288a74cc 43#include "terminal-util.h"
3f6fd1ba
LP
44#include "util.h"
45#include "verbs.h"
ee8c4568
LP
46
47static bool arg_no_pager = false;
48static bool arg_legend = true;
9085f64a 49static bool arg_all = false;
ee8c4568
LP
50
51static void pager_open_if_enabled(void) {
52
53 if (arg_no_pager)
54 return;
55
56 pager_open(false);
57}
58
914d6c09 59static int link_get_type_string(int iftype, sd_device *d, char **ret) {
ee8c4568
LP
60 const char *t;
61 char *p;
62
914d6c09
TG
63 assert(ret);
64
ee8c4568 65 if (iftype == ARPHRD_ETHER && d) {
732b7f39 66 const char *devtype = NULL, *id = NULL;
ee8c4568
LP
67 /* WLANs have iftype ARPHRD_ETHER, but we want
68 * to show a more useful type string for
69 * them */
70
914d6c09
TG
71 (void)sd_device_get_devtype(d, &devtype);
72
ee8c4568
LP
73 if (streq_ptr(devtype, "wlan"))
74 id = "wlan";
75 else if (streq_ptr(devtype, "wwan"))
76 id = "wwan";
77
78 if (id) {
79 p = strdup(id);
80 if (!p)
81 return -ENOMEM;
82
83 *ret = p;
84 return 1;
85 }
86 }
87
88 t = arphrd_to_name(iftype);
89 if (!t) {
90 *ret = NULL;
91 return 0;
92 }
93
94 p = strdup(t);
95 if (!p)
96 return -ENOMEM;
97
98 ascii_strlower(p);
99 *ret = p;
100
101 return 0;
102}
103
6d0c65ff
LP
104typedef struct LinkInfo {
105 const char *name;
106 int ifindex;
107 unsigned iftype;
108} LinkInfo;
109
110static int link_info_compare(const void *a, const void *b) {
111 const LinkInfo *x = a, *y = b;
112
113 return x->ifindex - y->ifindex;
114}
115
1c4baffc 116static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) {
6d0c65ff
LP
117 _cleanup_free_ LinkInfo *links = NULL;
118 size_t size = 0, c = 0;
1c4baffc 119 sd_netlink_message *i;
6d0c65ff
LP
120 int r;
121
1c4baffc 122 for (i = m; i; i = sd_netlink_message_next(i)) {
6d0c65ff
LP
123 const char *name;
124 unsigned iftype;
125 uint16_t type;
126 int ifindex;
127
1c4baffc 128 r = sd_netlink_message_get_type(i, &type);
6d0c65ff
LP
129 if (r < 0)
130 return r;
131
132 if (type != RTM_NEWLINK)
133 continue;
134
135 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
136 if (r < 0)
137 return r;
138
1c4baffc 139 r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
6d0c65ff
LP
140 if (r < 0)
141 return r;
142
143 r = sd_rtnl_message_link_get_type(i, &iftype);
144 if (r < 0)
145 return r;
146
147 if (!GREEDY_REALLOC(links, size, c+1))
148 return -ENOMEM;
149
150 links[c].name = name;
151 links[c].ifindex = ifindex;
152 links[c].iftype = iftype;
153 c++;
154 }
155
a6a4f528 156 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
157
158 *ret = links;
159 links = NULL;
160
161 return (int) c;
162}
163
d57c365b
LP
164static void operational_state_to_color(const char *state, const char **on, const char **off) {
165 assert(on);
166 assert(off);
167
168 if (streq_ptr(state, "routable")) {
169 *on = ansi_highlight_green();
1fc464f6 170 *off = ansi_normal();
d57c365b
LP
171 } else if (streq_ptr(state, "degraded")) {
172 *on = ansi_highlight_yellow();
1fc464f6 173 *off = ansi_normal();
d57c365b
LP
174 } else
175 *on = *off = "";
176}
177
178static void setup_state_to_color(const char *state, const char **on, const char **off) {
179 assert(on);
180 assert(off);
181
182 if (streq_ptr(state, "configured")) {
183 *on = ansi_highlight_green();
1fc464f6 184 *off = ansi_normal();
d57c365b
LP
185 } else if (streq_ptr(state, "configuring")) {
186 *on = ansi_highlight_yellow();
1fc464f6 187 *off = ansi_normal();
d57c365b
LP
188 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
189 *on = ansi_highlight_red();
1fc464f6 190 *off = ansi_normal();
d57c365b
LP
191 } else
192 *on = *off = "";
193}
194
266b5389 195static int list_links(int argc, char *argv[], void *userdata) {
1c4baffc
TG
196 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
197 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
6d0c65ff
LP
198 _cleanup_free_ LinkInfo *links = NULL;
199 int r, c, i;
ee8c4568
LP
200
201 pager_open_if_enabled();
202
1c4baffc 203 r = sd_netlink_open(&rtnl);
f647962d
MS
204 if (r < 0)
205 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568 206
ee8c4568
LP
207 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
208 if (r < 0)
209 return rtnl_log_create_error(r);
210
1c4baffc 211 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
212 if (r < 0)
213 return rtnl_log_create_error(r);
214
1c4baffc 215 r = sd_netlink_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;
914d6c09 228 _cleanup_device_unref_ sd_device *d = NULL;
d57c365b
LP
229 const char *on_color_operational, *off_color_operational,
230 *on_color_setup, *off_color_setup;
7d6884b6 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);
914d6c09 241 (void)sd_device_new_from_device_id(&d, devid);
ee8c4568 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 290static int get_gateway_description(
1c4baffc 291 sd_netlink *rtnl,
81fd1dd3 292 sd_hwdb *hwdb,
69fb1176
LP
293 int ifindex,
294 int family,
295 union in_addr_union *gateway,
296 char **gateway_description) {
1c4baffc
TG
297 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
298 sd_netlink_message *m;
c09da729
TG
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
1c4baffc 311 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
312 if (r < 0)
313 return r;
314
1c4baffc 315 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
316 if (r < 0)
317 return r;
318
1c4baffc 319 for (m = reply; m; m = sd_netlink_message_next(m)) {
c09da729
TG
320 union in_addr_union gw = {};
321 struct ether_addr mac = {};
322 uint16_t type;
323 int ifi, fam;
324
1c4baffc 325 r = sd_netlink_message_get_errno(m);
c09da729
TG
326 if (r < 0) {
327 log_error_errno(r, "got error: %m");
328 continue;
329 }
330
1c4baffc 331 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
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:
1c4baffc 364 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
365 if (r < 0)
366 continue;
367
368 break;
369 case AF_INET6:
1c4baffc 370 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
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
1c4baffc 382 r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
c09da729
TG
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 396static int dump_gateways(
1c4baffc 397 sd_netlink *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 445static int dump_addresses(
1c4baffc 446 sd_netlink *rtnl,
69fb1176
LP
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 496static int link_status_one(
1c4baffc 497 sd_netlink *rtnl,
81fd1dd3 498 sd_hwdb *hwdb,
69fb1176 499 const char *name) {
2301cb9f 500 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
64d6c229 501 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
1c4baffc 502 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
914d6c09 503 _cleanup_device_unref_ sd_device *d = NULL;
9085f64a 504 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 505 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 506 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
507 const char *on_color_operational, *off_color_operational,
508 *on_color_setup, *off_color_setup;
0d4ad91d
AR
509 _cleanup_strv_free_ char **carrier_bound_to = NULL;
510 _cleanup_strv_free_ char **carrier_bound_by = NULL;
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);
9085f64a
LP
518 assert(name);
519
520 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
521 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
522 else {
523 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
524 if (r < 0)
525 return rtnl_log_create_error(r);
526
1c4baffc 527 r = sd_netlink_message_append_string(req, IFLA_IFNAME, name);
9085f64a
LP
528 }
529
530 if (r < 0)
531 return rtnl_log_create_error(r);
532
1c4baffc 533 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
534 if (r < 0)
535 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
536
537 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
538 if (r < 0)
539 return rtnl_log_parse_error(r);
540
1c4baffc 541 r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name);
9085f64a
LP
542 if (r < 0)
543 return rtnl_log_parse_error(r);
544
545 r = sd_rtnl_message_link_get_type(reply, &iftype);
546 if (r < 0)
547 return rtnl_log_parse_error(r);
548
1c4baffc 549 have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
9085f64a
LP
550
551 if (have_mac) {
552 const uint8_t *p;
553 bool all_zeroes = true;
554
555 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
556 if (*p != 0) {
557 all_zeroes = false;
558 break;
559 }
560
561 if (all_zeroes)
562 have_mac = false;
563 }
564
1c4baffc 565 sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
9085f64a 566
d6731e4c 567 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
568 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
569
570 sd_network_link_get_setup_state(ifindex, &setup_state);
571 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 572
d6731e4c 573 sd_network_link_get_dns(ifindex, &dns);
2301cb9f 574 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
575 r = sd_network_link_get_wildcard_domain(ifindex);
576 if (r > 0) {
577 char *wildcard;
578
579 wildcard = strdup("*");
1405434b
LP
580 if (!wildcard)
581 return log_oom();
582
583 if (strv_consume(&domains, wildcard) < 0)
584 return log_oom();
67272d15 585 }
9085f64a
LP
586
587 sprintf(devid, "n%i", ifindex);
914d6c09
TG
588
589 (void)sd_device_new_from_device_id(&d, devid);
590
9085f64a 591 if (d) {
914d6c09
TG
592 (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
593 (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
594 (void)sd_device_get_property_value(d, "ID_PATH", &path);
9085f64a 595
914d6c09
TG
596 r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
597 if (r < 0)
598 (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
9085f64a 599
914d6c09
TG
600 r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
601 if (r < 0)
602 (void)sd_device_get_property_value(d, "ID_MODEL", &model);
9085f64a
LP
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
0d4ad91d
AR
609 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
610 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
611
d57c365b 612 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 613
0d4ad91d
AR
614 printf(" Link File: %s\n"
615 " Network File: %s\n"
616 " Type: %s\n"
617 " State: %s%s%s (%s%s%s)\n",
af5effc4 618 strna(link),
373d9f17 619 strna(network),
9085f64a 620 strna(t),
d57c365b
LP
621 on_color_operational, strna(operational_state), off_color_operational,
622 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
623
624 if (path)
0d4ad91d 625 printf(" Path: %s\n", path);
9085f64a 626 if (driver)
0d4ad91d 627 printf(" Driver: %s\n", driver);
9085f64a 628 if (vendor)
0d4ad91d 629 printf(" Vendor: %s\n", vendor);
9085f64a 630 if (model)
0d4ad91d 631 printf(" Model: %s\n", model);
9085f64a 632
db73295a 633 if (have_mac) {
888943fc 634 _cleanup_free_ char *description = NULL;
db73295a 635 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc
LP
636
637 ieee_oui(hwdb, &e, &description);
638
639 if (description)
0d4ad91d 640 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
888943fc 641 else
0d4ad91d 642 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
db73295a 643 }
9085f64a
LP
644
645 if (mtu > 0)
0d4ad91d 646 printf(" MTU: %u\n", mtu);
9085f64a 647
0d4ad91d
AR
648 dump_addresses(rtnl, " Address: ", ifindex);
649 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
9085f64a
LP
650
651 if (!strv_isempty(dns))
0d4ad91d 652 dump_list(" DNS: ", dns);
2301cb9f 653 if (!strv_isempty(domains))
0d4ad91d 654 dump_list(" Domain: ", domains);
8eb9058d
LP
655
656 (void) sd_network_link_get_ntp(ifindex, &ntp);
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 665
64d6c229
TA
666 (void) sd_network_link_get_timezone(ifindex, &tz);
667 if (tz)
668 printf(" Time Zone: %s", tz);
8eb9058d 669
9085f64a
LP
670 return 0;
671}
672
266b5389 673static int link_status(int argc, char *argv[], void *userdata) {
81fd1dd3 674 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
1c4baffc 675 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
ee8c4568
LP
676 char **name;
677 int r;
678
1c4baffc 679 r = sd_netlink_open(&rtnl);
f647962d
MS
680 if (r < 0)
681 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 682
81fd1dd3
TG
683 r = sd_hwdb_new(&hwdb);
684 if (r < 0)
685 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 686
266b5389 687 if (argc <= 1 && !arg_all) {
ee8c4568 688 _cleanup_free_ char *operational_state = NULL;
c627729f 689 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
e92da1e5 690 const char *on_color_operational, *off_color_operational;
ee8c4568 691
03cc0fd1 692 sd_network_get_operational_state(&operational_state);
e92da1e5 693 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 694
b1acce80
LP
695 printf("%s%s%s State: %s%s%s\n",
696 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
697 on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 698
1693a943 699 dump_addresses(rtnl, " Address: ", 0);
69fb1176
LP
700 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
701
03cc0fd1
LP
702 sd_network_get_dns(&dns);
703 if (!strv_isempty(dns))
704 dump_list(" DNS: ", dns);
705
c627729f
LP
706 sd_network_get_domains(&domains);
707 if (!strv_isempty(domains))
708 dump_list(" Domain: ", domains);
709
ddb7f7fc 710 sd_network_get_ntp(&ntp);
03cc0fd1
LP
711 if (!strv_isempty(ntp))
712 dump_list(" NTP: ", ntp);
ee8c4568 713
ee8c4568
LP
714 return 0;
715 }
716
717 pager_open_if_enabled();
718
9085f64a 719 if (arg_all) {
1c4baffc 720 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
6d0c65ff
LP
721 _cleanup_free_ LinkInfo *links = NULL;
722 int c, i;
ee8c4568 723
9085f64a
LP
724 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
725 if (r < 0)
726 return rtnl_log_create_error(r);
ee8c4568 727
1c4baffc 728 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
729 if (r < 0)
730 return rtnl_log_create_error(r);
731
1c4baffc 732 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
733 if (r < 0)
734 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 735
6d0c65ff
LP
736 c = decode_and_sort_links(reply, &links);
737 if (c < 0)
738 return rtnl_log_parse_error(c);
ee8c4568 739
6d0c65ff
LP
740 for (i = 0; i < c; i++) {
741 if (i > 0)
9085f64a 742 fputc('\n', stdout);
ee8c4568 743
914d6c09 744 link_status_one(rtnl, hwdb, links[i].name);
ee8c4568 745 }
69fb1176 746 } else {
266b5389
TG
747 STRV_FOREACH(name, argv + 1) {
748 if (name != argv + 1)
69fb1176 749 fputc('\n', stdout);
ee8c4568 750
914d6c09 751 link_status_one(rtnl, hwdb, *name);
69fb1176 752 }
ee8c4568
LP
753 }
754
755 return 0;
756}
757
49699bac
SS
758const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
759LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
760
761static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
762 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
763 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
764 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
765 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
766 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
767 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
768 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
769 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
770 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
771 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
772 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
773 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
774};
775
776DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
777
778static char *lldp_system_caps(uint16_t cap) {
779 _cleanup_free_ char *s = NULL, *t = NULL;
780 char *capability;
781
782 t = strdup("[ ");
783 if (!t)
784 return NULL;
785
786 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
787 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
788 if (!s)
789 return NULL;
790
791 free(t);
792 t = s;
793 }
794
795 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
796 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
797 if (!s)
798 return NULL;
799
800 free(t);
801 t = s;
802 }
803
804 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
805 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
806 if (!s)
807 return NULL;
808
809 free(t);
810 t = s;
811 }
812
813 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
814 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
815 if (!s)
816 return NULL;
817
818 free(t);
819 t = s;
820 }
821
822 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
920b52e4 823 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
49699bac
SS
824 if (!s)
825 return NULL;
826
827 free(t);
828 t = s;
829 }
830
831 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
832 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
833 if (!s)
834 return NULL;
835
836 free(t);
837 t = s;
838 }
839
840 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
841 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
842 if (!s)
843 return NULL;
844
845 free(t);
846 t = s;
847 }
848
849 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
850 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
851 if (!s)
852 return NULL;
853
854 free(t);
855 t = s;
856 }
857
858 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
859 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
860 if (!s)
861 return NULL;
862
863 free(t);
864 t = s;
865 }
866
867 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
868 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
869 if (!s)
870 return NULL;
871
872 free(t);
873 t = s;
874 }
875
876 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
877 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
878 if (!s)
879 return NULL;
880
881 free(t);
882 }
883
884 if (!s) {
885 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
886 if (!s)
887 return NULL;
888
889 free(t);
890 }
891
892 t = strappend(s, "]");
fbee1d85 893 if (!t)
49699bac
SS
894 return NULL;
895
896 free(s);
897 capability = t;
898
899 s = NULL;
900 t = NULL;
901
902 return capability;
903}
904
905static int link_lldp_status(int argc, char *argv[], void *userdata) {
1c4baffc
TG
906 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
907 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
49699bac
SS
908 _cleanup_free_ LinkInfo *links = NULL;
909 const char *state, *word;
910
ba52f15a 911 double ttl = -1;
49699bac 912 uint32_t capability;
49699bac
SS
913 int i, r, c, j;
914 size_t ll;
915 char **s;
916
917 pager_open_if_enabled();
918
1c4baffc 919 r = sd_netlink_open(&rtnl);
49699bac
SS
920 if (r < 0)
921 return log_error_errno(r, "Failed to connect to netlink: %m");
922
923 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
924 if (r < 0)
925 return rtnl_log_create_error(r);
926
1c4baffc 927 r = sd_netlink_message_request_dump(req, true);
49699bac
SS
928 if (r < 0)
929 return rtnl_log_create_error(r);
930
1c4baffc 931 r = sd_netlink_call(rtnl, req, 0, &reply);
49699bac
SS
932 if (r < 0)
933 return log_error_errno(r, "Failed to enumerate links: %m");
934
935 c = decode_and_sort_links(reply, &links);
936 if (c < 0)
937 return rtnl_log_parse_error(c);
938
19727828
TG
939 if (arg_legend)
940 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
49699bac
SS
941
942 for (i = j = 0; i < c; i++) {
943 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
944 _cleanup_strv_free_ char **l = NULL;
945
946 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
947 if (r < 0)
948 continue;
949
950 l = strv_split_newlines(lldp);
951 if (!l)
952 return -ENOMEM;
953
954 STRV_FOREACH(s, l) {
955 FOREACH_WORD_QUOTED(word, ll, *s, state) {
956 _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
957
958 t = strndup(word, ll);
959 if (!t)
960 return -ENOMEM;
961
962 r = split_pair(t, "=", &a, &b);
963 if (r < 0)
964 continue;
965
966 if (streq(a, "_Chassis")) {
b4e3d5e1
ZJS
967 r = free_and_strdup(&chassis, b);
968 if (r < 0)
969 return r;
49699bac
SS
970
971 } else if (streq(a, "_Port")) {
b4e3d5e1
ZJS
972 r = free_and_strdup(&port, b);
973 if (r < 0)
974 return r;
49699bac
SS
975
976 } else if (streq(a, "_TTL")) {
a7f7d1bd 977 long long unsigned x = 0;
ba52f15a 978 usec_t time;
49699bac 979
ba52f15a
ZJS
980 r = safe_atollu(b, &x);
981 if (r < 0 || (usec_t) x != x)
982 return log_warning_errno(r < 0 ? r : ERANGE,
983 "Failed to parse TTL \"%s\": %m", b);
49699bac 984
27ec691b 985 time = now(clock_boottime_or_monotonic());
ba52f15a
ZJS
986 if (x < time)
987 continue;
49699bac 988
ba52f15a 989 ttl = (double) (x - time) / USEC_PER_SEC;
49699bac
SS
990
991 } else if (streq(a, "_CAP")) {
992 sscanf(b, "%x", &capability);
993
994 cap = lldp_system_caps(capability);
995 }
996
997 }
998
ba52f15a
ZJS
999 if (ttl >= 0) {
1000 printf("%10s %24s %16s %16f %16s\n",
1001 links[i].name,
1002 strna(chassis), strna(port),
1003 ttl, cap);
49699bac
SS
1004 j++;
1005 }
1006 }
1007 }
1008
19727828
TG
1009 if (arg_legend) {
1010 printf("\nCapability Codes:\n"
1011 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1012 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1013 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1014
1015 printf("Total entries displayed: %d\n", j);
1016 }
49699bac
SS
1017
1018 return 0;
1019}
1020
ee8c4568
LP
1021static void help(void) {
1022 printf("%s [OPTIONS...]\n\n"
1023 "Query and control the networking subsystem.\n\n"
1024 " -h --help Show this help\n"
9085f64a
LP
1025 " --version Show package version\n"
1026 " --no-pager Do not pipe output into a pager\n"
1027 " --no-legend Do not show the headers and footers\n"
1028 " -a --all Show status for all links\n\n"
ee8c4568
LP
1029 "Commands:\n"
1030 " list List links\n"
d9000fd3 1031 " status [LINK...] Show link status\n"
49699bac 1032 " lldp Show lldp information\n"
ee8c4568
LP
1033 , program_invocation_short_name);
1034}
1035
1036static int parse_argv(int argc, char *argv[]) {
1037
1038 enum {
1039 ARG_VERSION = 0x100,
1040 ARG_NO_PAGER,
1041 ARG_NO_LEGEND,
1042 };
1043
1044 static const struct option options[] = {
1045 { "help", no_argument, NULL, 'h' },
1046 { "version", no_argument, NULL, ARG_VERSION },
1047 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1048 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1049 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1050 {}
1051 };
1052
1053 int c;
1054
1055 assert(argc >= 0);
1056 assert(argv);
1057
9085f64a 1058 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1059
1060 switch (c) {
1061
1062 case 'h':
1063 help();
1064 return 0;
1065
1066 case ARG_VERSION:
3f6fd1ba 1067 return version();
ee8c4568
LP
1068
1069 case ARG_NO_PAGER:
1070 arg_no_pager = true;
1071 break;
1072
1073 case ARG_NO_LEGEND:
1074 arg_legend = false;
1075 break;
1076
9085f64a
LP
1077 case 'a':
1078 arg_all = true;
1079 break;
1080
ee8c4568
LP
1081 case '?':
1082 return -EINVAL;
1083
1084 default:
1085 assert_not_reached("Unhandled option");
1086 }
1087 }
1088
1089 return 1;
1090}
1091
1092static int networkctl_main(int argc, char *argv[]) {
266b5389
TG
1093 const Verb verbs[] = {
1094 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1095 { "status", 1, VERB_ANY, 0, link_status },
49699bac 1096 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
266b5389 1097 {}
ee8c4568
LP
1098 };
1099
266b5389 1100 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1101}
1102
1103int main(int argc, char* argv[]) {
1104 int r;
1105
1106 log_parse_environment();
1107 log_open();
1108
1109 r = parse_argv(argc, argv);
1110 if (r <= 0)
1111 goto finish;
1112
1113 r = networkctl_main(argc, argv);
1114
1115finish:
1116 pager_close();
1117
1118 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1119}