]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
core: exclude .slice units from "systemctl isolate"
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
ee8c4568
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2014 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
ee8c4568 20#include <getopt.h>
1693a943 21#include <net/if.h>
3f6fd1ba 22#include <stdbool.h>
ee8c4568 23
914d6c09 24#include "sd-device.h"
3f6fd1ba
LP
25#include "sd-hwdb.h"
26#include "sd-netlink.h"
27#include "sd-network.h"
ee8c4568 28
b5efdb8a 29#include "alloc-util.h"
3f6fd1ba 30#include "arphrd-list.h"
914d6c09 31#include "device-util.h"
3f6fd1ba 32#include "ether-addr-util.h"
81fd1dd3 33#include "hwdb-util.h"
3f6fd1ba 34#include "lldp.h"
ee8c4568 35#include "local-addresses.h"
8752c575 36#include "locale-util.h"
3f6fd1ba
LP
37#include "netlink-util.h"
38#include "pager.h"
6bedfcbb 39#include "parse-util.h"
db73295a 40#include "socket-util.h"
d054f0a4 41#include "stdio-util.h"
8b43440b 42#include "string-table.h"
8752c575 43#include "string-util.h"
3f6fd1ba 44#include "strv.h"
288a74cc 45#include "terminal-util.h"
3f6fd1ba
LP
46#include "util.h"
47#include "verbs.h"
ee8c4568
LP
48
49static bool arg_no_pager = false;
50static bool arg_legend = true;
9085f64a 51static bool arg_all = false;
ee8c4568
LP
52
53static void pager_open_if_enabled(void) {
54
55 if (arg_no_pager)
56 return;
57
58 pager_open(false);
59}
60
1c4a6088 61static int link_get_type_string(unsigned short iftype, sd_device *d, char **ret) {
ee8c4568
LP
62 const char *t;
63 char *p;
64
914d6c09
TG
65 assert(ret);
66
ee8c4568 67 if (iftype == ARPHRD_ETHER && d) {
732b7f39 68 const char *devtype = NULL, *id = NULL;
ee8c4568
LP
69 /* WLANs have iftype ARPHRD_ETHER, but we want
70 * to show a more useful type string for
71 * them */
72
914d6c09
TG
73 (void)sd_device_get_devtype(d, &devtype);
74
ee8c4568
LP
75 if (streq_ptr(devtype, "wlan"))
76 id = "wlan";
77 else if (streq_ptr(devtype, "wwan"))
78 id = "wwan";
79
80 if (id) {
81 p = strdup(id);
82 if (!p)
83 return -ENOMEM;
84
85 *ret = p;
86 return 1;
87 }
88 }
89
90 t = arphrd_to_name(iftype);
91 if (!t) {
92 *ret = NULL;
93 return 0;
94 }
95
96 p = strdup(t);
97 if (!p)
98 return -ENOMEM;
99
100 ascii_strlower(p);
101 *ret = p;
102
103 return 0;
104}
105
6d0c65ff
LP
106typedef struct LinkInfo {
107 const char *name;
108 int ifindex;
1c4a6088 109 unsigned short iftype;
6d0c65ff
LP
110} LinkInfo;
111
112static int link_info_compare(const void *a, const void *b) {
113 const LinkInfo *x = a, *y = b;
114
115 return x->ifindex - y->ifindex;
116}
117
1c4baffc 118static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) {
6d0c65ff
LP
119 _cleanup_free_ LinkInfo *links = NULL;
120 size_t size = 0, c = 0;
1c4baffc 121 sd_netlink_message *i;
6d0c65ff
LP
122 int r;
123
1c4baffc 124 for (i = m; i; i = sd_netlink_message_next(i)) {
6d0c65ff 125 const char *name;
1c4a6088 126 unsigned short iftype;
6d0c65ff
LP
127 uint16_t type;
128 int ifindex;
129
1c4baffc 130 r = sd_netlink_message_get_type(i, &type);
6d0c65ff
LP
131 if (r < 0)
132 return r;
133
134 if (type != RTM_NEWLINK)
135 continue;
136
137 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
138 if (r < 0)
139 return r;
140
1c4baffc 141 r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
6d0c65ff
LP
142 if (r < 0)
143 return r;
144
145 r = sd_rtnl_message_link_get_type(i, &iftype);
146 if (r < 0)
147 return r;
148
149 if (!GREEDY_REALLOC(links, size, c+1))
150 return -ENOMEM;
151
152 links[c].name = name;
153 links[c].ifindex = ifindex;
154 links[c].iftype = iftype;
155 c++;
156 }
157
a6a4f528 158 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
159
160 *ret = links;
161 links = NULL;
162
163 return (int) c;
164}
165
d57c365b
LP
166static void operational_state_to_color(const char *state, const char **on, const char **off) {
167 assert(on);
168 assert(off);
169
170 if (streq_ptr(state, "routable")) {
171 *on = ansi_highlight_green();
1fc464f6 172 *off = ansi_normal();
d57c365b
LP
173 } else if (streq_ptr(state, "degraded")) {
174 *on = ansi_highlight_yellow();
1fc464f6 175 *off = ansi_normal();
d57c365b
LP
176 } else
177 *on = *off = "";
178}
179
180static void setup_state_to_color(const char *state, const char **on, const char **off) {
181 assert(on);
182 assert(off);
183
184 if (streq_ptr(state, "configured")) {
185 *on = ansi_highlight_green();
1fc464f6 186 *off = ansi_normal();
d57c365b
LP
187 } else if (streq_ptr(state, "configuring")) {
188 *on = ansi_highlight_yellow();
1fc464f6 189 *off = ansi_normal();
d57c365b
LP
190 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
191 *on = ansi_highlight_red();
1fc464f6 192 *off = ansi_normal();
d57c365b
LP
193 } else
194 *on = *off = "";
195}
196
266b5389 197static int list_links(int argc, char *argv[], void *userdata) {
4afd3348
LP
198 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
199 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
6d0c65ff
LP
200 _cleanup_free_ LinkInfo *links = NULL;
201 int r, c, i;
ee8c4568
LP
202
203 pager_open_if_enabled();
204
1c4baffc 205 r = sd_netlink_open(&rtnl);
f647962d
MS
206 if (r < 0)
207 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568 208
ee8c4568
LP
209 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
210 if (r < 0)
211 return rtnl_log_create_error(r);
212
1c4baffc 213 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
214 if (r < 0)
215 return rtnl_log_create_error(r);
216
1c4baffc 217 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
218 if (r < 0)
219 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568
LP
220
221 if (arg_legend)
3e3db0ee 222 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 223
6d0c65ff
LP
224 c = decode_and_sort_links(reply, &links);
225 if (c < 0)
226 return rtnl_log_parse_error(c);
227
228 for (i = 0; i < c; i++) {
ab1525bc 229 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
4afd3348 230 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
d57c365b
LP
231 const char *on_color_operational, *off_color_operational,
232 *on_color_setup, *off_color_setup;
7d6884b6 233 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 234 _cleanup_free_ char *t = NULL;
ee8c4568 235
d6731e4c 236 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
237 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
238
239 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
240 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
ee8c4568 241
6d0c65ff 242 sprintf(devid, "n%i", links[i].ifindex);
914d6c09 243 (void)sd_device_new_from_device_id(&d, devid);
ee8c4568 244
6d0c65ff 245 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 246
3e3db0ee 247 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
248 links[i].ifindex, links[i].name, strna(t),
249 on_color_operational, strna(operational_state), off_color_operational,
250 on_color_setup, strna(setup_state), off_color_setup);
ee8c4568
LP
251 }
252
253 if (arg_legend)
6d0c65ff 254 printf("\n%i links listed.\n", c);
ee8c4568
LP
255
256 return 0;
257}
258
c09da729 259/* IEEE Organizationally Unique Identifier vendor string */
81fd1dd3
TG
260static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) {
261 const char *description;
262 char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc;
263 int r;
264
265 assert(ret);
c09da729 266
888943fc
LP
267 if (!hwdb)
268 return -EINVAL;
269
81fd1dd3
TG
270 if (!mac)
271 return -EINVAL;
272
c09da729
TG
273 /* skip commonly misused 00:00:00 (Xerox) prefix */
274 if (memcmp(mac, "\0\0\0", 3) == 0)
275 return -EINVAL;
276
d054f0a4
DM
277 xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
278 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) {
4afd3348 300 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
1c4baffc 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
dce83649
LP
491 if (strv_isempty(l))
492 return;
493
ee8c4568
LP
494 STRV_FOREACH(i, l) {
495 printf("%*s%s\n",
496 (int) strlen(prefix),
497 i == l ? prefix : "",
498 *i);
499 }
500}
501
69fb1176 502static int link_status_one(
1c4baffc 503 sd_netlink *rtnl,
81fd1dd3 504 sd_hwdb *hwdb,
69fb1176 505 const char *name) {
3df9bec5 506 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
64d6c229 507 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
4afd3348
LP
508 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
509 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
9085f64a 510 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 511 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 512 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
513 const char *on_color_operational, *off_color_operational,
514 *on_color_setup, *off_color_setup;
0d4ad91d
AR
515 _cleanup_strv_free_ char **carrier_bound_to = NULL;
516 _cleanup_strv_free_ char **carrier_bound_by = NULL;
9085f64a 517 struct ether_addr e;
1c4a6088 518 unsigned short iftype;
9085f64a
LP
519 int r, ifindex;
520 bool have_mac;
521 uint32_t mtu;
522
523 assert(rtnl);
9085f64a
LP
524 assert(name);
525
6ad623a3 526 if (parse_ifindex(name, &ifindex) >= 0)
9085f64a
LP
527 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
528 else {
529 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
530 if (r < 0)
531 return rtnl_log_create_error(r);
532
1c4baffc 533 r = sd_netlink_message_append_string(req, IFLA_IFNAME, name);
9085f64a
LP
534 }
535
536 if (r < 0)
537 return rtnl_log_create_error(r);
538
1c4baffc 539 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
540 if (r < 0)
541 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
542
543 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
544 if (r < 0)
545 return rtnl_log_parse_error(r);
546
1c4baffc 547 r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name);
9085f64a
LP
548 if (r < 0)
549 return rtnl_log_parse_error(r);
550
551 r = sd_rtnl_message_link_get_type(reply, &iftype);
552 if (r < 0)
553 return rtnl_log_parse_error(r);
554
1c4baffc 555 have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
9085f64a
LP
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
dce83649 570 (void) sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
9085f64a 571
dce83649 572 (void) 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
dce83649 575 (void) sd_network_link_get_setup_state(ifindex, &setup_state);
d57c365b 576 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 577
dce83649
LP
578 (void) sd_network_link_get_dns(ifindex, &dns);
579 (void) sd_network_link_get_search_domains(ifindex, &search_domains);
580 (void) sd_network_link_get_route_domains(ifindex, &route_domains);
581 (void) sd_network_link_get_ntp(ifindex, &ntp);
9085f64a
LP
582
583 sprintf(devid, "n%i", ifindex);
914d6c09 584
dce83649 585 (void) sd_device_new_from_device_id(&d, devid);
914d6c09 586
9085f64a 587 if (d) {
dce83649
LP
588 (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
589 (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
590 (void) sd_device_get_property_value(d, "ID_PATH", &path);
9085f64a 591
914d6c09
TG
592 r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
593 if (r < 0)
dce83649 594 (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
9085f64a 595
914d6c09
TG
596 r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
597 if (r < 0)
dce83649 598 (void) sd_device_get_property_value(d, "ID_MODEL", &model);
9085f64a
LP
599 }
600
b1acce80
LP
601 link_get_type_string(iftype, d, &t);
602
373d9f17 603 sd_network_link_get_network_file(ifindex, &network);
df3fb561 604
0d4ad91d
AR
605 sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
606 sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
607
d57c365b 608 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 609
0d4ad91d
AR
610 printf(" Link File: %s\n"
611 " Network File: %s\n"
612 " Type: %s\n"
613 " State: %s%s%s (%s%s%s)\n",
af5effc4 614 strna(link),
373d9f17 615 strna(network),
9085f64a 616 strna(t),
d57c365b
LP
617 on_color_operational, strna(operational_state), off_color_operational,
618 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
619
620 if (path)
0d4ad91d 621 printf(" Path: %s\n", path);
9085f64a 622 if (driver)
0d4ad91d 623 printf(" Driver: %s\n", driver);
9085f64a 624 if (vendor)
0d4ad91d 625 printf(" Vendor: %s\n", vendor);
9085f64a 626 if (model)
0d4ad91d 627 printf(" Model: %s\n", model);
9085f64a 628
db73295a 629 if (have_mac) {
888943fc 630 _cleanup_free_ char *description = NULL;
db73295a 631 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc
LP
632
633 ieee_oui(hwdb, &e, &description);
634
635 if (description)
0d4ad91d 636 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
888943fc 637 else
0d4ad91d 638 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
db73295a 639 }
9085f64a
LP
640
641 if (mtu > 0)
0d4ad91d 642 printf(" MTU: %u\n", mtu);
9085f64a 643
0d4ad91d
AR
644 dump_addresses(rtnl, " Address: ", ifindex);
645 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
9085f64a 646
dce83649
LP
647 dump_list(" DNS: ", dns);
648 dump_list(" Search Domains: ", search_domains);
649 dump_list(" Route Domains: ", route_domains);
8eb9058d 650
dce83649 651 dump_list(" NTP: ", ntp);
0d4ad91d 652
dce83649
LP
653 dump_list("Carrier Bound To: ", carrier_bound_to);
654 dump_list("Carrier Bound By: ", carrier_bound_by);
9085f64a 655
64d6c229
TA
656 (void) sd_network_link_get_timezone(ifindex, &tz);
657 if (tz)
658 printf(" Time Zone: %s", tz);
8eb9058d 659
9085f64a
LP
660 return 0;
661}
662
266b5389 663static int link_status(int argc, char *argv[], void *userdata) {
4afd3348
LP
664 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
665 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
ee8c4568
LP
666 char **name;
667 int r;
668
1c4baffc 669 r = sd_netlink_open(&rtnl);
f647962d
MS
670 if (r < 0)
671 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8 672
81fd1dd3
TG
673 r = sd_hwdb_new(&hwdb);
674 if (r < 0)
675 log_debug_errno(r, "Failed to open hardware database: %m");
69fb1176 676
266b5389 677 if (argc <= 1 && !arg_all) {
ee8c4568 678 _cleanup_free_ char *operational_state = NULL;
3df9bec5 679 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains;
e92da1e5 680 const char *on_color_operational, *off_color_operational;
ee8c4568 681
03cc0fd1 682 sd_network_get_operational_state(&operational_state);
e92da1e5 683 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 684
3df9bec5 685 printf("%s%s%s State: %s%s%s\n",
b1acce80
LP
686 on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
687 on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 688
3df9bec5
LP
689 dump_addresses(rtnl, " Address: ", 0);
690 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
69fb1176 691
03cc0fd1 692 sd_network_get_dns(&dns);
dce83649 693 dump_list(" DNS: ", dns);
3df9bec5
LP
694
695 sd_network_get_search_domains(&search_domains);
dce83649 696 dump_list("Search Domains: ", search_domains);
3df9bec5
LP
697
698 sd_network_get_route_domains(&route_domains);
dce83649 699 dump_list(" Route Domains: ", route_domains);
c627729f 700
ddb7f7fc 701 sd_network_get_ntp(&ntp);
dce83649 702 dump_list(" NTP: ", ntp);
ee8c4568 703
ee8c4568
LP
704 return 0;
705 }
706
707 pager_open_if_enabled();
708
9085f64a 709 if (arg_all) {
4afd3348 710 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
6d0c65ff
LP
711 _cleanup_free_ LinkInfo *links = NULL;
712 int c, i;
ee8c4568 713
9085f64a
LP
714 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
715 if (r < 0)
716 return rtnl_log_create_error(r);
ee8c4568 717
1c4baffc 718 r = sd_netlink_message_request_dump(req, true);
ee8c4568
LP
719 if (r < 0)
720 return rtnl_log_create_error(r);
721
1c4baffc 722 r = sd_netlink_call(rtnl, req, 0, &reply);
f647962d
MS
723 if (r < 0)
724 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 725
6d0c65ff
LP
726 c = decode_and_sort_links(reply, &links);
727 if (c < 0)
728 return rtnl_log_parse_error(c);
ee8c4568 729
6d0c65ff
LP
730 for (i = 0; i < c; i++) {
731 if (i > 0)
9085f64a 732 fputc('\n', stdout);
ee8c4568 733
914d6c09 734 link_status_one(rtnl, hwdb, links[i].name);
ee8c4568 735 }
69fb1176 736 } else {
266b5389
TG
737 STRV_FOREACH(name, argv + 1) {
738 if (name != argv + 1)
69fb1176 739 fputc('\n', stdout);
ee8c4568 740
914d6c09 741 link_status_one(rtnl, hwdb, *name);
69fb1176 742 }
ee8c4568
LP
743 }
744
745 return 0;
746}
747
49699bac
SS
748const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_;
749LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_;
750
751static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = {
752 [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O",
753 [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P",
754 [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B",
755 [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W",
756 [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R",
757 [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T",
758 [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D",
759 [LLDP_SYSTEM_CAPABILITIES_STATION] = "A",
760 [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C",
761 [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S",
762 [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M",
763 [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A",
764};
765
766DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities);
767
768static char *lldp_system_caps(uint16_t cap) {
769 _cleanup_free_ char *s = NULL, *t = NULL;
770 char *capability;
771
772 t = strdup("[ ");
773 if (!t)
774 return NULL;
775
776 if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) {
777 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL);
778 if (!s)
779 return NULL;
780
781 free(t);
782 t = s;
783 }
784
785 if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) {
786 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL);
787 if (!s)
788 return NULL;
789
790 free(t);
791 t = s;
792 }
793
794 if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) {
795 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL);
796 if (!s)
797 return NULL;
798
799 free(t);
800 t = s;
801 }
802
803 if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) {
804 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL);
805 if (!s)
806 return NULL;
807
808 free(t);
809 t = s;
810 }
811
812 if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) {
920b52e4 813 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL);
49699bac
SS
814 if (!s)
815 return NULL;
816
817 free(t);
818 t = s;
819 }
820
821 if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) {
822 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL);
823 if (!s)
824 return NULL;
825
826 free(t);
827 t = s;
828 }
829
830 if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) {
831 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL);
832 if (!s)
833 return NULL;
834
835 free(t);
836 t = s;
837 }
838
839 if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) {
840 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL);
841 if (!s)
842 return NULL;
843
844 free(t);
845 t = s;
846 }
847
848 if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) {
849 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL);
850 if (!s)
851 return NULL;
852
853 free(t);
854 t = s;
855 }
856
857 if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) {
858 s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL);
859 if (!s)
860 return NULL;
861
862 free(t);
863 t = s;
864 }
865
866 if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) {
867 s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR));
868 if (!s)
869 return NULL;
870
871 free(t);
872 }
873
874 if (!s) {
875 s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX));
876 if (!s)
877 return NULL;
878
879 free(t);
880 }
881
882 t = strappend(s, "]");
fbee1d85 883 if (!t)
49699bac
SS
884 return NULL;
885
886 free(s);
887 capability = t;
888
889 s = NULL;
890 t = NULL;
891
892 return capability;
893}
894
895static int link_lldp_status(int argc, char *argv[], void *userdata) {
4afd3348
LP
896 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
897 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
49699bac 898 _cleanup_free_ LinkInfo *links = NULL;
ba52f15a 899 double ttl = -1;
49699bac 900 uint32_t capability;
49699bac 901 int i, r, c, j;
af40397d 902 const char *p;
49699bac
SS
903 char **s;
904
905 pager_open_if_enabled();
906
1c4baffc 907 r = sd_netlink_open(&rtnl);
49699bac
SS
908 if (r < 0)
909 return log_error_errno(r, "Failed to connect to netlink: %m");
910
911 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
912 if (r < 0)
913 return rtnl_log_create_error(r);
914
1c4baffc 915 r = sd_netlink_message_request_dump(req, true);
49699bac
SS
916 if (r < 0)
917 return rtnl_log_create_error(r);
918
1c4baffc 919 r = sd_netlink_call(rtnl, req, 0, &reply);
49699bac
SS
920 if (r < 0)
921 return log_error_errno(r, "Failed to enumerate links: %m");
922
923 c = decode_and_sort_links(reply, &links);
924 if (c < 0)
925 return rtnl_log_parse_error(c);
926
19727828
TG
927 if (arg_legend)
928 printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability");
49699bac
SS
929
930 for (i = j = 0; i < c; i++) {
931 _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL;
932 _cleanup_strv_free_ char **l = NULL;
933
934 r = sd_network_link_get_lldp(links[i].ifindex, &lldp);
935 if (r < 0)
936 continue;
937
938 l = strv_split_newlines(lldp);
939 if (!l)
940 return -ENOMEM;
941
942 STRV_FOREACH(s, l) {
49699bac 943
af40397d
SS
944 p = *s;
945 for (;;) {
946 _cleanup_free_ char *a = NULL, *b = NULL, *word = NULL;
947
948 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
949 if (r < 0)
950 return log_error_errno(r, "Failed to parse LLDP syntax \"%s\": %m", *s);
951
952 if (r == 0)
953 break;
49699bac 954
af40397d 955 r = split_pair(word, "=", &a, &b);
49699bac
SS
956 if (r < 0)
957 continue;
958
959 if (streq(a, "_Chassis")) {
b4e3d5e1
ZJS
960 r = free_and_strdup(&chassis, b);
961 if (r < 0)
962 return r;
49699bac
SS
963
964 } else if (streq(a, "_Port")) {
b4e3d5e1
ZJS
965 r = free_and_strdup(&port, b);
966 if (r < 0)
967 return r;
49699bac
SS
968
969 } else if (streq(a, "_TTL")) {
a7f7d1bd 970 long long unsigned x = 0;
ba52f15a 971 usec_t time;
49699bac 972
ba52f15a
ZJS
973 r = safe_atollu(b, &x);
974 if (r < 0 || (usec_t) x != x)
975 return log_warning_errno(r < 0 ? r : ERANGE,
976 "Failed to parse TTL \"%s\": %m", b);
49699bac 977
27ec691b 978 time = now(clock_boottime_or_monotonic());
ba52f15a
ZJS
979 if (x < time)
980 continue;
49699bac 981
ba52f15a 982 ttl = (double) (x - time) / USEC_PER_SEC;
49699bac
SS
983
984 } else if (streq(a, "_CAP")) {
985 sscanf(b, "%x", &capability);
986
987 cap = lldp_system_caps(capability);
988 }
989
990 }
991
ba52f15a
ZJS
992 if (ttl >= 0) {
993 printf("%10s %24s %16s %16f %16s\n",
994 links[i].name,
995 strna(chassis), strna(port),
996 ttl, cap);
49699bac
SS
997 j++;
998 }
999 }
1000 }
1001
19727828
TG
1002 if (arg_legend) {
1003 printf("\nCapability Codes:\n"
1004 "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n"
1005 "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n"
1006 "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n");
1007
1008 printf("Total entries displayed: %d\n", j);
1009 }
49699bac
SS
1010
1011 return 0;
1012}
1013
ee8c4568
LP
1014static void help(void) {
1015 printf("%s [OPTIONS...]\n\n"
1016 "Query and control the networking subsystem.\n\n"
1017 " -h --help Show this help\n"
9085f64a
LP
1018 " --version Show package version\n"
1019 " --no-pager Do not pipe output into a pager\n"
1020 " --no-legend Do not show the headers and footers\n"
1021 " -a --all Show status for all links\n\n"
ee8c4568
LP
1022 "Commands:\n"
1023 " list List links\n"
d9000fd3 1024 " status [LINK...] Show link status\n"
49699bac 1025 " lldp Show lldp information\n"
ee8c4568
LP
1026 , program_invocation_short_name);
1027}
1028
1029static int parse_argv(int argc, char *argv[]) {
1030
1031 enum {
1032 ARG_VERSION = 0x100,
1033 ARG_NO_PAGER,
1034 ARG_NO_LEGEND,
1035 };
1036
1037 static const struct option options[] = {
1038 { "help", no_argument, NULL, 'h' },
1039 { "version", no_argument, NULL, ARG_VERSION },
1040 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1041 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 1042 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
1043 {}
1044 };
1045
1046 int c;
1047
1048 assert(argc >= 0);
1049 assert(argv);
1050
9085f64a 1051 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
1052
1053 switch (c) {
1054
1055 case 'h':
1056 help();
1057 return 0;
1058
1059 case ARG_VERSION:
3f6fd1ba 1060 return version();
ee8c4568
LP
1061
1062 case ARG_NO_PAGER:
1063 arg_no_pager = true;
1064 break;
1065
1066 case ARG_NO_LEGEND:
1067 arg_legend = false;
1068 break;
1069
9085f64a
LP
1070 case 'a':
1071 arg_all = true;
1072 break;
1073
ee8c4568
LP
1074 case '?':
1075 return -EINVAL;
1076
1077 default:
1078 assert_not_reached("Unhandled option");
1079 }
1080 }
1081
1082 return 1;
1083}
1084
1085static int networkctl_main(int argc, char *argv[]) {
266b5389
TG
1086 const Verb verbs[] = {
1087 { "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
1088 { "status", 1, VERB_ANY, 0, link_status },
49699bac 1089 { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
266b5389 1090 {}
ee8c4568
LP
1091 };
1092
266b5389 1093 return dispatch_verb(argc, argv, verbs, NULL);
ee8c4568
LP
1094}
1095
1096int main(int argc, char* argv[]) {
1097 int r;
1098
1099 log_parse_environment();
1100 log_open();
1101
1102 r = parse_argv(argc, argv);
1103 if (r <= 0)
1104 goto finish;
1105
1106 r = networkctl_main(argc, argv);
1107
1108finish:
1109 pager_close();
1110
1111 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1112}