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