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