]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
Merge pull request #1668 from ssahani/net1
[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"
db73295a 39#include "socket-util.h"
07630cea 40#include "string-util.h"
3f6fd1ba 41#include "strv.h"
288a74cc 42#include "terminal-util.h"
3f6fd1ba
LP
43#include "util.h"
44#include "verbs.h"
ee8c4568
LP
45
46static bool arg_no_pager = false;
47static bool arg_legend = true;
9085f64a 48static bool arg_all = false;
ee8c4568
LP
49
50static void pager_open_if_enabled(void) {
51
52 if (arg_no_pager)
53 return;
54
55 pager_open(false);
56}
57
914d6c09 58static int link_get_type_string(int iftype, sd_device *d, char **ret) {
ee8c4568
LP
59 const char *t;
60 char *p;
61
914d6c09
TG
62 assert(ret);
63
ee8c4568 64 if (iftype == ARPHRD_ETHER && d) {
732b7f39 65 const char *devtype = NULL, *id = NULL;
ee8c4568
LP
66 /* WLANs have iftype ARPHRD_ETHER, but we want
67 * to show a more useful type string for
68 * them */
69
914d6c09
TG
70 (void)sd_device_get_devtype(d, &devtype);
71
ee8c4568
LP
72 if (streq_ptr(devtype, "wlan"))
73 id = "wlan";
74 else if (streq_ptr(devtype, "wwan"))
75 id = "wwan";
76
77 if (id) {
78 p = strdup(id);
79 if (!p)
80 return -ENOMEM;
81
82 *ret = p;
83 return 1;
84 }
85 }
86
87 t = arphrd_to_name(iftype);
88 if (!t) {
89 *ret = NULL;
90 return 0;
91 }
92
93 p = strdup(t);
94 if (!p)
95 return -ENOMEM;
96
97 ascii_strlower(p);
98 *ret = p;
99
100 return 0;
101}
102
6d0c65ff
LP
103typedef struct LinkInfo {
104 const char *name;
105 int ifindex;
106 unsigned iftype;
107} LinkInfo;
108
109static int link_info_compare(const void *a, const void *b) {
110 const LinkInfo *x = a, *y = b;
111
112 return x->ifindex - y->ifindex;
113}
114
1c4baffc 115static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) {
6d0c65ff
LP
116 _cleanup_free_ LinkInfo *links = NULL;
117 size_t size = 0, c = 0;
1c4baffc 118 sd_netlink_message *i;
6d0c65ff
LP
119 int r;
120
1c4baffc 121 for (i = m; i; i = sd_netlink_message_next(i)) {
6d0c65ff
LP
122 const char *name;
123 unsigned iftype;
124 uint16_t type;
125 int ifindex;
126
1c4baffc 127 r = sd_netlink_message_get_type(i, &type);
6d0c65ff
LP
128 if (r < 0)
129 return r;
130
131 if (type != RTM_NEWLINK)
132 continue;
133
134 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
135 if (r < 0)
136 return r;
137
1c4baffc 138 r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
6d0c65ff
LP
139 if (r < 0)
140 return r;
141
142 r = sd_rtnl_message_link_get_type(i, &iftype);
143 if (r < 0)
144 return r;
145
146 if (!GREEDY_REALLOC(links, size, c+1))
147 return -ENOMEM;
148
149 links[c].name = name;
150 links[c].ifindex = ifindex;
151 links[c].iftype = iftype;
152 c++;
153 }
154
a6a4f528 155 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
156
157 *ret = links;
158 links = NULL;
159
160 return (int) c;
161}
162
d57c365b
LP
163static void operational_state_to_color(const char *state, const char **on, const char **off) {
164 assert(on);
165 assert(off);
166
167 if (streq_ptr(state, "routable")) {
168 *on = ansi_highlight_green();
1fc464f6 169 *off = ansi_normal();
d57c365b
LP
170 } else if (streq_ptr(state, "degraded")) {
171 *on = ansi_highlight_yellow();
1fc464f6 172 *off = ansi_normal();
d57c365b
LP
173 } else
174 *on = *off = "";
175}
176
177static void setup_state_to_color(const char *state, const char **on, const char **off) {
178 assert(on);
179 assert(off);
180
181 if (streq_ptr(state, "configured")) {
182 *on = ansi_highlight_green();
1fc464f6 183 *off = ansi_normal();
d57c365b
LP
184 } else if (streq_ptr(state, "configuring")) {
185 *on = ansi_highlight_yellow();
1fc464f6 186 *off = ansi_normal();
d57c365b
LP
187 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
188 *on = ansi_highlight_red();
1fc464f6 189 *off = ansi_normal();
d57c365b
LP
190 } else
191 *on = *off = "";
192}
193
266b5389 194static int list_links(int argc, char *argv[], void *userdata) {
1c4baffc
TG
195 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
196 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
6d0c65ff
LP
197 _cleanup_free_ LinkInfo *links = NULL;
198 int r, c, i;
ee8c4568
LP
199
200 pager_open_if_enabled();
201
1c4baffc 202 r = sd_netlink_open(&rtnl);
f647962d
MS
203 if (r < 0)
204 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568 205
ee8c4568
LP
206 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
207 if (r < 0)
208 return rtnl_log_create_error(r);
209
1c4baffc 210 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
211 if (r < 0)
212 return rtnl_log_create_error(r);
213
1c4baffc 214 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
215 if (r < 0)
216 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568
LP
217
218 if (arg_legend)
3e3db0ee 219 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 220
6d0c65ff
LP
221 c = decode_and_sort_links(reply, &links);
222 if (c < 0)
223 return rtnl_log_parse_error(c);
224
225 for (i = 0; i < c; i++) {
ab1525bc 226 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
914d6c09 227 _cleanup_device_unref_ sd_device *d = NULL;
d57c365b
LP
228 const char *on_color_operational, *off_color_operational,
229 *on_color_setup, *off_color_setup;
7d6884b6 230 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 231 _cleanup_free_ char *t = NULL;
ee8c4568 232
d6731e4c 233 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
234 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
235
236 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
237 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
ee8c4568 238
6d0c65ff 239 sprintf(devid, "n%i", links[i].ifindex);
914d6c09 240 (void)sd_device_new_from_device_id(&d, devid);
ee8c4568 241
6d0c65ff 242 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 243
3e3db0ee 244 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
245 links[i].ifindex, links[i].name, strna(t),
246 on_color_operational, strna(operational_state), off_color_operational,
247 on_color_setup, strna(setup_state), off_color_setup);
ee8c4568
LP
248 }
249
250 if (arg_legend)
6d0c65ff 251 printf("\n%i links listed.\n", c);
ee8c4568
LP
252
253 return 0;
254}
255
c09da729 256/* IEEE Organizationally Unique Identifier vendor string */
81fd1dd3
TG
257static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) {
258 const char *description;
259 char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc;
260 int r;
261
262 assert(ret);
c09da729 263
888943fc
LP
264 if (!hwdb)
265 return -EINVAL;
266
81fd1dd3
TG
267 if (!mac)
268 return -EINVAL;
269
c09da729
TG
270 /* skip commonly misused 00:00:00 (Xerox) prefix */
271 if (memcmp(mac, "\0\0\0", 3) == 0)
272 return -EINVAL;
273
81fd1dd3 274 snprintf(modalias, sizeof(modalias), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
c09da729 275
81fd1dd3
TG
276 r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
277 if (r < 0)
278 return r;
c09da729 279
81fd1dd3
TG
280 desc = strdup(description);
281 if (!desc)
282 return -ENOMEM;
c09da729 283
81fd1dd3
TG
284 *ret = desc;
285
286 return 0;
c09da729
TG
287}
288
69fb1176 289static int get_gateway_description(
1c4baffc 290 sd_netlink *rtnl,
81fd1dd3 291 sd_hwdb *hwdb,
69fb1176
LP
292 int ifindex,
293 int family,
294 union in_addr_union *gateway,
295 char **gateway_description) {
1c4baffc
TG
296 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
297 sd_netlink_message *m;
c09da729
TG
298 int r;
299
300 assert(rtnl);
301 assert(ifindex >= 0);
302 assert(family == AF_INET || family == AF_INET6);
303 assert(gateway);
304 assert(gateway_description);
305
306 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
307 if (r < 0)
308 return r;
309
1c4baffc 310 r = sd_netlink_message_request_dump(req, true);
c09da729
TG
311 if (r < 0)
312 return r;
313
1c4baffc 314 r = sd_netlink_call(rtnl, req, 0, &reply);
c09da729
TG
315 if (r < 0)
316 return r;
317
1c4baffc 318 for (m = reply; m; m = sd_netlink_message_next(m)) {
c09da729
TG
319 union in_addr_union gw = {};
320 struct ether_addr mac = {};
321 uint16_t type;
322 int ifi, fam;
323
1c4baffc 324 r = sd_netlink_message_get_errno(m);
c09da729
TG
325 if (r < 0) {
326 log_error_errno(r, "got error: %m");
327 continue;
328 }
329
1c4baffc 330 r = sd_netlink_message_get_type(m, &type);
c09da729
TG
331 if (r < 0) {
332 log_error_errno(r, "could not get type: %m");
333 continue;
334 }
335
336 if (type != RTM_NEWNEIGH) {
337 log_error("type is not RTM_NEWNEIGH");
338 continue;
339 }
340
341 r = sd_rtnl_message_neigh_get_family(m, &fam);
342 if (r < 0) {
343 log_error_errno(r, "could not get family: %m");
344 continue;
345 }
346
347 if (fam != family) {
348 log_error("family is not correct");
349 continue;
350 }
351
352 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
353 if (r < 0) {
144232a8 354 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
355 continue;
356 }
357
358 if (ifindex > 0 && ifi != ifindex)
359 continue;
360
361 switch (fam) {
362 case AF_INET:
1c4baffc 363 r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in);
c09da729
TG
364 if (r < 0)
365 continue;
366
367 break;
368 case AF_INET6:
1c4baffc 369 r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6);
c09da729
TG
370 if (r < 0)
371 continue;
372
373 break;
374 default:
375 continue;
376 }
377
378 if (!in_addr_equal(fam, &gw, gateway))
379 continue;
380
1c4baffc 381 r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
c09da729
TG
382 if (r < 0)
383 continue;
384
385 r = ieee_oui(hwdb, &mac, gateway_description);
386 if (r < 0)
387 continue;
388
389 return 0;
390 }
391
392 return -ENODATA;
393}
394
69fb1176 395static int dump_gateways(
1c4baffc 396 sd_netlink *rtnl,
81fd1dd3 397 sd_hwdb *hwdb,
69fb1176
LP
398 const char *prefix,
399 int ifindex) {
b6a3ca6d
TG
400 _cleanup_free_ struct local_address *local = NULL;
401 int r, n, i;
c09da729 402
b6a3ca6d
TG
403 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
404 if (n < 0)
405 return n;
c09da729 406
b6a3ca6d
TG
407 for (i = 0; i < n; i++) {
408 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 409
b6a3ca6d 410 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 411 if (r < 0)
b6a3ca6d 412 return r;
c09da729 413
69fb1176 414 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
c09da729 415 if (r < 0)
b6a3ca6d 416 log_debug_errno(r, "Could not get description of gateway: %m");
c09da729 417
1693a943
LP
418 printf("%*s%s",
419 (int) strlen(prefix),
420 i == 0 ? prefix : "",
421 gateway);
422
b6a3ca6d 423 if (description)
1693a943
LP
424 printf(" (%s)", description);
425
426 /* Show interface name for the entry if we show
427 * entries for all interfaces */
428 if (ifindex <= 0) {
429 char name[IF_NAMESIZE+1];
430
431 if (if_indextoname(local[i].ifindex, name)) {
432 fputs(" on ", stdout);
433 fputs(name, stdout);
434 } else
435 printf(" on %%%i", local[i].ifindex);
436 }
437
438 fputc('\n', stdout);
c09da729
TG
439 }
440
441 return 0;
442}
443
69fb1176 444static int dump_addresses(
1c4baffc 445 sd_netlink *rtnl,
69fb1176
LP
446 const char *prefix,
447 int ifindex) {
448
ee8c4568
LP
449 _cleanup_free_ struct local_address *local = NULL;
450 int r, n, i;
451
1d050e1e 452 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
453 if (n < 0)
454 return n;
455
456 for (i = 0; i < n; i++) {
457 _cleanup_free_ char *pretty = NULL;
458
459 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
460 if (r < 0)
461 return r;
462
1693a943 463 printf("%*s%s",
ee8c4568
LP
464 (int) strlen(prefix),
465 i == 0 ? prefix : "",
466 pretty);
1693a943
LP
467
468 if (ifindex <= 0) {
469 char name[IF_NAMESIZE+1];
470
471 if (if_indextoname(local[i].ifindex, name)) {
472 fputs(" on ", stdout);
473 fputs(name, stdout);
474 } else
475 printf(" on %%%i", local[i].ifindex);
476 }
477
478 fputc('\n', stdout);
ee8c4568
LP
479 }
480
481 return 0;
482}
483
484static void dump_list(const char *prefix, char **l) {
485 char **i;
486
487 STRV_FOREACH(i, l) {
488 printf("%*s%s\n",
489 (int) strlen(prefix),
490 i == l ? prefix : "",
491 *i);
492 }
493}
494
69fb1176 495static int link_status_one(
1c4baffc 496 sd_netlink *rtnl,
81fd1dd3 497 sd_hwdb *hwdb,
69fb1176 498 const char *name) {
2301cb9f 499 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
64d6c229 500 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
1c4baffc 501 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
914d6c09 502 _cleanup_device_unref_ sd_device *d = NULL;
9085f64a 503 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 504 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 505 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
506 const char *on_color_operational, *off_color_operational,
507 *on_color_setup, *off_color_setup;
0d4ad91d
AR
508 _cleanup_strv_free_ char **carrier_bound_to = NULL;
509 _cleanup_strv_free_ char **carrier_bound_by = NULL;
9085f64a
LP
510 struct ether_addr e;
511 unsigned iftype;
512 int r, ifindex;
513 bool have_mac;
514 uint32_t mtu;
515
516 assert(rtnl);
9085f64a
LP
517 assert(name);
518
519 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
520 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
521 else {
522 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
523 if (r < 0)
524 return rtnl_log_create_error(r);
525
1c4baffc 526 r = sd_netlink_message_append_string(req, IFLA_IFNAME, name);
9085f64a
LP
527 }
528
529 if (r < 0)
530 return rtnl_log_create_error(r);
531
1c4baffc 532 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
533 if (r < 0)
534 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
535
536 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
537 if (r < 0)
538 return rtnl_log_parse_error(r);
539
1c4baffc 540 r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name);
9085f64a
LP
541 if (r < 0)
542 return rtnl_log_parse_error(r);
543
544 r = sd_rtnl_message_link_get_type(reply, &iftype);
545 if (r < 0)
546 return rtnl_log_parse_error(r);
547
1c4baffc 548 have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
9085f64a
LP
549
550 if (have_mac) {
551 const uint8_t *p;
552 bool all_zeroes = true;
553
554 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
555 if (*p != 0) {
556 all_zeroes = false;
557 break;
558 }
559
560 if (all_zeroes)
561 have_mac = false;
562 }
563
1c4baffc 564 sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
9085f64a 565
d6731e4c 566 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
567 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
568
569 sd_network_link_get_setup_state(ifindex, &setup_state);
570 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 571
d6731e4c 572 sd_network_link_get_dns(ifindex, &dns);
2301cb9f 573 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
574 r = sd_network_link_get_wildcard_domain(ifindex);
575 if (r > 0) {
576 char *wildcard;
577
578 wildcard = strdup("*");
1405434b
LP
579 if (!wildcard)
580 return log_oom();
581
582 if (strv_consume(&domains, wildcard) < 0)
583 return log_oom();
67272d15 584 }
9085f64a
LP
585
586 sprintf(devid, "n%i", ifindex);
914d6c09
TG
587
588 (void)sd_device_new_from_device_id(&d, devid);
589
9085f64a 590 if (d) {
914d6c09
TG
591 (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
592 (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
593 (void)sd_device_get_property_value(d, "ID_PATH", &path);
9085f64a 594
914d6c09
TG
595 r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
596 if (r < 0)
597 (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
9085f64a 598
914d6c09
TG
599 r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
600 if (r < 0)
601 (void)sd_device_get_property_value(d, "ID_MODEL", &model);
9085f64a
LP
602 }
603
b1acce80
LP
604 link_get_type_string(iftype, d, &t);
605
373d9f17 606 sd_network_link_get_network_file(ifindex, &network);
df3fb561 607
0d4ad91d
AR
608 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
609 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
610
d57c365b 611 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 612
0d4ad91d
AR
613 printf(" Link File: %s\n"
614 " Network File: %s\n"
615 " Type: %s\n"
616 " State: %s%s%s (%s%s%s)\n",
af5effc4 617 strna(link),
373d9f17 618 strna(network),
9085f64a 619 strna(t),
d57c365b
LP
620 on_color_operational, strna(operational_state), off_color_operational,
621 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
622
623 if (path)
0d4ad91d 624 printf(" Path: %s\n", path);
9085f64a 625 if (driver)
0d4ad91d 626 printf(" Driver: %s\n", driver);
9085f64a 627 if (vendor)
0d4ad91d 628 printf(" Vendor: %s\n", vendor);
9085f64a 629 if (model)
0d4ad91d 630 printf(" Model: %s\n", model);
9085f64a 631
db73295a 632 if (have_mac) {
888943fc 633 _cleanup_free_ char *description = NULL;
db73295a 634 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc
LP
635
636 ieee_oui(hwdb, &e, &description);
637
638 if (description)
0d4ad91d 639 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
888943fc 640 else
0d4ad91d 641 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
db73295a 642 }
9085f64a
LP
643
644 if (mtu > 0)
0d4ad91d 645 printf(" MTU: %u\n", mtu);
9085f64a 646
0d4ad91d
AR
647 dump_addresses(rtnl, " Address: ", ifindex);
648 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
9085f64a
LP
649
650 if (!strv_isempty(dns))
0d4ad91d 651 dump_list(" DNS: ", dns);
2301cb9f 652 if (!strv_isempty(domains))
0d4ad91d 653 dump_list(" Domain: ", domains);
8eb9058d
LP
654
655 (void) sd_network_link_get_ntp(ifindex, &ntp);
9085f64a 656 if (!strv_isempty(ntp))
0d4ad91d
AR
657 dump_list(" NTP: ", ntp);
658
659 if (!strv_isempty(carrier_bound_to))
660 dump_list("Carrier Bound To: ", carrier_bound_to);
661
662 if (!strv_isempty(carrier_bound_by))
663 dump_list("Carrier Bound By: ", carrier_bound_by);
9085f64a 664
64d6c229
TA
665 (void) sd_network_link_get_timezone(ifindex, &tz);
666 if (tz)
667 printf(" Time Zone: %s", tz);
8eb9058d 668
9085f64a
LP
669 return 0;
670}
671
266b5389 672static int link_status(int argc, char *argv[], void *userdata) {
81fd1dd3 673 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
1c4baffc 674 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
ee8c4568
LP
675 char **name;
676 int r;
677
1c4baffc 678 r = sd_netlink_open(&rtnl);
f647962d
MS
679 if (r < 0)
680 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 681
81fd1dd3
TG
682 r = sd_hwdb_new(&hwdb);
683 if (r < 0)
684 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 685
266b5389 686 if (argc <= 1 && !arg_all) {
ee8c4568 687 _cleanup_free_ char *operational_state = NULL;
c627729f 688 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
e92da1e5 689 const char *on_color_operational, *off_color_operational;
ee8c4568 690
03cc0fd1 691 sd_network_get_operational_state(&operational_state);
e92da1e5 692 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 693
b1acce80
LP
694 printf("%s%s%s State: %s%s%s\n",
695 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
696 on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 697
1693a943 698 dump_addresses(rtnl, " Address: ", 0);
69fb1176
LP
699 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
700
03cc0fd1
LP
701 sd_network_get_dns(&dns);
702 if (!strv_isempty(dns))
703 dump_list(" DNS: ", dns);
704
c627729f
LP
705 sd_network_get_domains(&domains);
706 if (!strv_isempty(domains))
707 dump_list(" Domain: ", domains);
708
ddb7f7fc 709 sd_network_get_ntp(&ntp);
03cc0fd1
LP
710 if (!strv_isempty(ntp))
711 dump_list(" NTP: ", ntp);
ee8c4568 712
ee8c4568
LP
713 return 0;
714 }
715
716 pager_open_if_enabled();
717
9085f64a 718 if (arg_all) {
1c4baffc 719 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
6d0c65ff
LP
720 _cleanup_free_ LinkInfo *links = NULL;
721 int c, i;
ee8c4568 722
9085f64a
LP
723 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
724 if (r < 0)
725 return rtnl_log_create_error(r);
ee8c4568 726
1c4baffc 727 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
728 if (r < 0)
729 return rtnl_log_create_error(r);
730
1c4baffc 731 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
732 if (r < 0)
733 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 734
6d0c65ff
LP
735 c = decode_and_sort_links(reply, &links);
736 if (c < 0)
737 return rtnl_log_parse_error(c);
ee8c4568 738
6d0c65ff
LP
739 for (i = 0; i < c; i++) {
740 if (i > 0)
9085f64a 741 fputc('\n', stdout);
ee8c4568 742
914d6c09 743 link_status_one(rtnl, hwdb, links[i].name);
ee8c4568 744 }
69fb1176 745 } else {
266b5389
TG
746 STRV_FOREACH(name, argv + 1) {
747 if (name != argv + 1)
69fb1176 748 fputc('\n', stdout);
ee8c4568 749
914d6c09 750 link_status_one(rtnl, hwdb, *name);
69fb1176 751 }
ee8c4568
LP
752 }
753
754 return 0;
755}
756
49699bac
SS
757const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
758LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
759
760static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
761 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
762 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
763 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
764 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
765 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
766 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
767 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
768 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
769 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
770 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
771 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
772 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
773};
774
775DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
776
777static char *lldp_system_caps(uint16_t cap) {
778 _cleanup_free_ char *s = NULL, *t = NULL;
779 char *capability;
780
781 t = strdup("[ ");
782 if (!t)
783 return NULL;
784
785 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
786 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
787 if (!s)
788 return NULL;
789
790 free(t);
791 t = s;
792 }
793
794 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
795 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
796 if (!s)
797 return NULL;
798
799 free(t);
800 t = s;
801 }
802
803 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
804 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
805 if (!s)
806 return NULL;
807
808 free(t);
809 t = s;
810 }
811
812 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
813 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
814 if (!s)
815 return NULL;
816
817 free(t);
818 t = s;
819 }
820
821 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
920b52e4 822 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
49699bac
SS
823 if (!s)
824 return NULL;
825
826 free(t);
827 t = s;
828 }
829
830 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
831 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
832 if (!s)
833 return NULL;
834
835 free(t);
836 t = s;
837 }
838
839 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
840 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
841 if (!s)
842 return NULL;
843
844 free(t);
845 t = s;
846 }
847
848 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
849 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
850 if (!s)
851 return NULL;
852
853 free(t);
854 t = s;
855 }
856
857 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
858 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
859 if (!s)
860 return NULL;
861
862 free(t);
863 t = s;
864 }
865
866 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
867 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
868 if (!s)
869 return NULL;
870
871 free(t);
872 t = s;
873 }
874
875 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
876 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
877 if (!s)
878 return NULL;
879
880 free(t);
881 }
882
883 if (!s) {
884 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
885 if (!s)
886 return NULL;
887
888 free(t);
889 }
890
891 t = strappend(s, "]");
fbee1d85 892 if (!t)
49699bac
SS
893 return NULL;
894
895 free(s);
896 capability = t;
897
898 s = NULL;
899 t = NULL;
900
901 return capability;
902}
903
904static int link_lldp_status(int argc, char *argv[], void *userdata) {
1c4baffc
TG
905 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
906 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
49699bac
SS
907 _cleanup_free_ LinkInfo *links = NULL;
908 const char *state, *word;
909
ba52f15a 910 double ttl = -1;
49699bac 911 uint32_t capability;
49699bac
SS
912 int i, r, c, j;
913 size_t ll;
914 char **s;
915
916 pager_open_if_enabled();
917
1c4baffc 918 r = sd_netlink_open(&rtnl);
49699bac
SS
919 if (r < 0)
920 return log_error_errno(r, "Failed to connect to netlink: %m");
921
922 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
923 if (r < 0)
924 return rtnl_log_create_error(r);
925
1c4baffc 926 r = sd_netlink_message_request_dump(req, true);
49699bac
SS
927 if (r < 0)
928 return rtnl_log_create_error(r);
929
1c4baffc 930 r = sd_netlink_call(rtnl, req, 0, &reply);
49699bac
SS
931 if (r < 0)
932 return log_error_errno(r, "Failed to enumerate links: %m");
933
934 c = decode_and_sort_links(reply, &links);
935 if (c < 0)
936 return rtnl_log_parse_error(c);
937
19727828
TG
938 if (arg_legend)
939 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
49699bac
SS
940
941 for (i = j = 0; i < c; i++) {
942 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
943 _cleanup_strv_free_ char **l = NULL;
944
945 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
946 if (r < 0)
947 continue;
948
949 l = strv_split_newlines(lldp);
950 if (!l)
951 return -ENOMEM;
952
953 STRV_FOREACH(s, l) {
954 FOREACH_WORD_QUOTED(word, ll, *s, state) {
955 _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
956
957 t = strndup(word, ll);
958 if (!t)
959 return -ENOMEM;
960
961 r = split_pair(t, "=", &a, &b);
962 if (r < 0)
963 continue;
964
965 if (streq(a, "_Chassis")) {
b4e3d5e1
ZJS
966 r = free_and_strdup(&chassis, b);
967 if (r < 0)
968 return r;
49699bac
SS
969
970 } else if (streq(a, "_Port")) {
b4e3d5e1
ZJS
971 r = free_and_strdup(&port, b);
972 if (r < 0)
973 return r;
49699bac
SS
974
975 } else if (streq(a, "_TTL")) {
a7f7d1bd 976 long long unsigned x = 0;
ba52f15a 977 usec_t time;
49699bac 978
ba52f15a
ZJS
979 r = safe_atollu(b, &x);
980 if (r < 0 || (usec_t) x != x)
981 return log_warning_errno(r < 0 ? r : ERANGE,
982 "Failed to parse TTL \"%s\": %m", b);
49699bac 983
27ec691b 984 time = now(clock_boottime_or_monotonic());
ba52f15a
ZJS
985 if (x < time)
986 continue;
49699bac 987
ba52f15a 988 ttl = (double) (x - time) / USEC_PER_SEC;
49699bac
SS
989
990 } else if (streq(a, "_CAP")) {
991 sscanf(b, "%x", &capability);
992
993 cap = lldp_system_caps(capability);
994 }
995
996 }
997
ba52f15a
ZJS
998 if (ttl >= 0) {
999 printf("%10s %24s %16s %16f %16s\n",
1000 links[i].name,
1001 strna(chassis), strna(port),
1002 ttl, cap);
49699bac
SS
1003 j++;
1004 }
1005 }
1006 }
1007
19727828
TG
1008 if (arg_legend) {
1009 printf("\nCapability Codes:\n"
1010 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1011 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1012 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1013
1014 printf("Total entries displayed: %d\n", j);
1015 }
49699bac
SS
1016
1017 return 0;
1018}
1019
ee8c4568
LP
1020static void help(void) {
1021 printf("%s [OPTIONS...]\n\n"
1022 "Query and control the networking subsystem.\n\n"
1023 " -h --help Show this help\n"
9085f64a
LP
1024 " --version Show package version\n"
1025 " --no-pager Do not pipe output into a pager\n"
1026 " --no-legend Do not show the headers and footers\n"
1027 " -a --all Show status for all links\n\n"
ee8c4568
LP
1028 "Commands:\n"
1029 " list List links\n"
d9000fd3 1030 " status [LINK...] Show link status\n"
49699bac 1031 " lldp Show lldp information\n"
ee8c4568
LP
1032 , program_invocation_short_name);
1033}
1034
1035static int parse_argv(int argc, char *argv[]) {
1036
1037 enum {
1038 ARG_VERSION = 0x100,
1039 ARG_NO_PAGER,
1040 ARG_NO_LEGEND,
1041 };
1042
1043 static const struct option options[] = {
1044 { "help", no_argument, NULL, 'h' },
1045 { "version", no_argument, NULL, ARG_VERSION },
1046 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1047 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1048 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1049 {}
1050 };
1051
1052 int c;
1053
1054 assert(argc >= 0);
1055 assert(argv);
1056
9085f64a 1057 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1058
1059 switch (c) {
1060
1061 case 'h':
1062 help();
1063 return 0;
1064
1065 case ARG_VERSION:
3f6fd1ba 1066 return version();
ee8c4568
LP
1067
1068 case ARG_NO_PAGER:
1069 arg_no_pager = true;
1070 break;
1071
1072 case ARG_NO_LEGEND:
1073 arg_legend = false;
1074 break;
1075
9085f64a
LP
1076 case 'a':
1077 arg_all = true;
1078 break;
1079
ee8c4568
LP
1080 case '?':
1081 return -EINVAL;
1082
1083 default:
1084 assert_not_reached("Unhandled option");
1085 }
1086 }
1087
1088 return 1;
1089}
1090
1091static int networkctl_main(int argc, char *argv[]) {
266b5389
TG
1092 const Verb verbs[] = {
1093 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1094 { "status", 1, VERB_ANY, 0, link_status },
49699bac 1095 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
266b5389 1096 {}
ee8c4568
LP
1097 };
1098
266b5389 1099 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1100}
1101
1102int main(int argc, char* argv[]) {
1103 int r;
1104
1105 log_parse_environment();
1106 log_open();
1107
1108 r = parse_argv(argc, argv);
1109 if (r <= 0)
1110 goto finish;
1111
1112 r = networkctl_main(argc, argv);
1113
1114finish:
1115 pager_close();
1116
1117 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1118}