]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
networkctl: properly format MAC addresses
[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
22#include <stdbool.h>
23#include <getopt.h>
24
25#include "sd-network.h"
26#include "sd-rtnl.h"
27#include "libudev.h"
28
29#include "build.h"
30#include "util.h"
31#include "pager.h"
32#include "rtnl-util.h"
33#include "udev-util.h"
34#include "arphrd-list.h"
35#include "local-addresses.h"
36
37static bool arg_no_pager = false;
38static bool arg_legend = true;
39
40static void pager_open_if_enabled(void) {
41
42 if (arg_no_pager)
43 return;
44
45 pager_open(false);
46}
47
48static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
49 const char *t;
50 char *p;
51
52 if (iftype == ARPHRD_ETHER && d) {
53 const char *devtype, *id = NULL;
54 /* WLANs have iftype ARPHRD_ETHER, but we want
55 * to show a more useful type string for
56 * them */
57
58 devtype = udev_device_get_devtype(d);
59 if (streq_ptr(devtype, "wlan"))
60 id = "wlan";
61 else if (streq_ptr(devtype, "wwan"))
62 id = "wwan";
63
64 if (id) {
65 p = strdup(id);
66 if (!p)
67 return -ENOMEM;
68
69 *ret = p;
70 return 1;
71 }
72 }
73
74 t = arphrd_to_name(iftype);
75 if (!t) {
76 *ret = NULL;
77 return 0;
78 }
79
80 p = strdup(t);
81 if (!p)
82 return -ENOMEM;
83
84 ascii_strlower(p);
85 *ret = p;
86
87 return 0;
88}
89
90static int list_links(char **args, unsigned n) {
91 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
92 _cleanup_udev_unref_ struct udev *udev = NULL;
93 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
94 sd_rtnl_message *i;
95 unsigned c = 0;
96 int r;
97
98 pager_open_if_enabled();
99
100 r = sd_rtnl_open(&rtnl, 0);
101 if (r < 0) {
102 log_error("Failed to connect to netlink: %s", strerror(-r));
103 return r;
104 }
105
106 udev = udev_new();
107 if (!udev) {
108 log_error("Failed to connect to udev: %m");
109 return -errno;
110 }
111
112 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
113 if (r < 0)
114 return rtnl_log_create_error(r);
115
116 r = sd_rtnl_message_request_dump(req, true);
117 if (r < 0)
118 return rtnl_log_create_error(r);
119
120 r = sd_rtnl_call(rtnl, req, 0, &reply);
121 if (r < 0) {
122 log_error("Failed to enumerate links: %s", strerror(-r));
123 return r;
124 }
125
126 if (arg_legend)
127 printf("%3s %-16s %-10s %-10s %-10s\n", "IDX", "LINK", "TYPE", "STATE", "OPERATIONAL");
128
129 for (i = reply; i; i = sd_rtnl_message_next(i)) {
130 _cleanup_free_ char *state = NULL, *operational_state = NULL;
131 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
132 char devid[2 + DECIMAL_STR_MAX(int)];
133 _cleanup_free_ char *t = NULL;
134 const char *name;
135 unsigned iftype;
136 uint16_t type;
137 int ifindex;
138
139 r = sd_rtnl_message_get_type(i, &type);
140 if (r < 0)
141 return rtnl_log_parse_error(r);
142
143 if (type != RTM_NEWLINK)
144 continue;
145
146 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
147 if (r < 0)
148 return rtnl_log_parse_error(r);
149
150 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
151 if (r < 0)
152 return rtnl_log_parse_error(r);
153
154 r = sd_rtnl_message_link_get_type(i, &iftype);
155 if (r < 0)
156 return rtnl_log_parse_error(r);
157
158 sd_network_get_link_state(ifindex, &state);
159 sd_network_get_link_operational_state(ifindex, &operational_state);
160
161 sprintf(devid, "n%i", ifindex);
162 d = udev_device_new_from_device_id(udev, devid);
163
164 link_get_type_string(iftype, d, &t);
165
166 printf("%3i %-16s %-10s %-10s %-10s\n", ifindex, name, strna(t), strna(state), strna(operational_state));
167 c++;
168 }
169
170 if (arg_legend)
171 printf("\n%u links listed.\n", c);
172
173 return 0;
174}
175
176static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
177 _cleanup_free_ struct local_address *local = NULL;
178 int r, n, i;
179
180 n = local_addresses(rtnl, ifindex, &local);
181 if (n < 0)
182 return n;
183
184 for (i = 0; i < n; i++) {
185 _cleanup_free_ char *pretty = NULL;
186
187 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
188 if (r < 0)
189 return r;
190
191 printf("%*s%s\n",
192 (int) strlen(prefix),
193 i == 0 ? prefix : "",
194 pretty);
195 }
196
197 return 0;
198}
199
200static void dump_list(const char *prefix, char **l) {
201 char **i;
202
203 STRV_FOREACH(i, l) {
204 printf("%*s%s\n",
205 (int) strlen(prefix),
206 i == l ? prefix : "",
207 *i);
208 }
209}
210
211static int link_status(char **args, unsigned n) {
212 _cleanup_udev_unref_ struct udev *udev = NULL;
213 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
214 char **name;
215 int r;
216
217 if (n <= 1) {
218 _cleanup_free_ char *operational_state = NULL;
03cc0fd1 219 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
ee8c4568 220
03cc0fd1
LP
221 sd_network_get_operational_state(&operational_state);
222 if (operational_state)
223 printf(" State: %s\n", operational_state);
224
225 sd_network_get_dns(&dns);
226 if (!strv_isempty(dns))
227 dump_list(" DNS: ", dns);
228
229 sd_network_get_dns(&ntp);
230 if (!strv_isempty(ntp))
231 dump_list(" NTP: ", ntp);
ee8c4568 232
ee8c4568
LP
233 return 0;
234 }
235
236 pager_open_if_enabled();
237
238 r = sd_rtnl_open(&rtnl, 0);
239 if (r < 0) {
240 log_error("Failed to connect to netlink: %s", strerror(-r));
241 return r;
242 }
243
244 udev = udev_new();
245 if (!udev) {
246 log_error("Failed to connect to udev: %m");
247 return -errno;
248 }
249
250 STRV_FOREACH(name, args + 1) {
251 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
252 _cleanup_free_ char *state = NULL, *operational_state = NULL;
253 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
254 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
255 const char *canonical_name = NULL;
256 char devid[2 + DECIMAL_STR_MAX(int)];
257 int ifindex, canonical_ifindex;
258 _cleanup_free_ char *t = NULL;
259 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
260 struct ether_addr e;
261 unsigned iftype;
262 bool have_mac;
263 uint32_t mtu;
264
265 if (name != args+1)
266 printf("\n");
267
268 if (safe_atoi(*name, &ifindex) >= 0 && ifindex > 0)
269 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
270 else {
271 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
272 if (r < 0)
273 return rtnl_log_create_error(r);
274
275 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, *name);
276 }
277
278 if (r < 0)
279 return rtnl_log_create_error(r);
280
281 r = sd_rtnl_call(rtnl, req, 0, &reply);
282 if (r < 0) {
283 log_error("Failed to query link: %s", strerror(-r));
284 continue;
285 }
286
287 r = sd_rtnl_message_link_get_ifindex(reply, &canonical_ifindex);
288 if (r < 0)
289 return rtnl_log_parse_error(r);
290
291 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &canonical_name);
292 if (r < 0)
293 return rtnl_log_parse_error(r);
294
295 r = sd_rtnl_message_link_get_type(reply, &iftype);
296 if (r < 0)
297 return rtnl_log_parse_error(r);
298
299 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
300
301 if (have_mac) {
302 const uint8_t *p;
303 bool all_zeroes = true;
304
305 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
306 if (*p != 0) {
307 all_zeroes = false;
308 break;
309 }
310
311 if (all_zeroes)
312 have_mac = false;
313 }
314
315 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
316
317 sd_network_get_link_state(canonical_ifindex, &state);
318 sd_network_get_link_operational_state(canonical_ifindex, &operational_state);
319
320 sd_network_get_link_dns(canonical_ifindex, &dns);
03cc0fd1 321 sd_network_get_link_ntp(canonical_ifindex, &ntp);
ee8c4568
LP
322
323 sprintf(devid, "n%i", canonical_ifindex);
324 d = udev_device_new_from_device_id(udev, devid);
325
326 link_get_type_string(iftype, d, &t);
327
328 if (d) {
329 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
330 path = udev_device_get_property_value(d, "ID_PATH");
331
332 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
333 if (!vendor)
334 vendor = udev_device_get_property_value(d, "ID_VENDOR");
335
336 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
337 if (!model)
338 model = udev_device_get_property_value(d, "ID_MODEL");
339 }
340
341 printf("%i: %s\n", canonical_ifindex, canonical_name);
342
343 printf(" Type: %s\n"
344 " State: %s (%s)\n",
345 strna(t),
346 strna(operational_state),
347 strna(state));
348
349 if (path)
350 printf(" Path: %s\n", path);
351 if (driver)
352 printf(" Driver: %s\n", driver);
353 if (vendor)
354 printf(" Vendor: %s\n", vendor);
355 if (model)
356 printf(" Model: %s\n", model);
357
1b17f211
LP
358 if (have_mac)
359 printf(" HW Address: %s\n", ether_ntoa(&e));
ee8c4568
LP
360
361 if (mtu > 0)
362 printf(" MTU: %u\n", mtu);
363
364 dump_addresses(rtnl, " Address: ", canonical_ifindex);
365
366 if (!strv_isempty(dns))
367 dump_list(" DNS: ", dns);
368 if (!strv_isempty(ntp))
369 dump_list(" NTP: ", ntp);
370 }
371
372 return 0;
373}
374
375static void help(void) {
376 printf("%s [OPTIONS...]\n\n"
377 "Query and control the networking subsystem.\n\n"
378 " -h --help Show this help\n"
379 " --version Show package version\n\n"
380 "Commands:\n"
381 " list List links\n"
382 " status LINK Show link status\n"
383 , program_invocation_short_name);
384}
385
386static int parse_argv(int argc, char *argv[]) {
387
388 enum {
389 ARG_VERSION = 0x100,
390 ARG_NO_PAGER,
391 ARG_NO_LEGEND,
392 };
393
394 static const struct option options[] = {
395 { "help", no_argument, NULL, 'h' },
396 { "version", no_argument, NULL, ARG_VERSION },
397 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
398 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
399 {}
400 };
401
402 int c;
403
404 assert(argc >= 0);
405 assert(argv);
406
407 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
408
409 switch (c) {
410
411 case 'h':
412 help();
413 return 0;
414
415 case ARG_VERSION:
416 puts(PACKAGE_STRING);
417 puts(SYSTEMD_FEATURES);
418 return 0;
419
420 case ARG_NO_PAGER:
421 arg_no_pager = true;
422 break;
423
424 case ARG_NO_LEGEND:
425 arg_legend = false;
426 break;
427
428 case '?':
429 return -EINVAL;
430
431 default:
432 assert_not_reached("Unhandled option");
433 }
434 }
435
436 return 1;
437}
438
439static int networkctl_main(int argc, char *argv[]) {
440
441 static const struct {
442 const char* verb;
443 const enum {
444 MORE,
445 LESS,
446 EQUAL
447 } argc_cmp;
448 const int argc;
449 int (* const dispatch)(char **args, unsigned n);
450 } verbs[] = {
451 { "list", LESS, 1, list_links },
452 { "status", MORE, 1, link_status },
453 };
454
455 int left;
456 unsigned i;
457
458 assert(argc >= 0);
459 assert(argv);
460
461 left = argc - optind;
462
463 if (left <= 0)
464 /* Special rule: no arguments means "list" */
465 i = 0;
466 else {
467 if (streq(argv[optind], "help")) {
468 help();
469 return 0;
470 }
471
472 for (i = 0; i < ELEMENTSOF(verbs); i++)
473 if (streq(argv[optind], verbs[i].verb))
474 break;
475
476 if (i >= ELEMENTSOF(verbs)) {
477 log_error("Unknown operation %s", argv[optind]);
478 return -EINVAL;
479 }
480 }
481
482 switch (verbs[i].argc_cmp) {
483
484 case EQUAL:
485 if (left != verbs[i].argc) {
486 log_error("Invalid number of arguments.");
487 return -EINVAL;
488 }
489
490 break;
491
492 case MORE:
493 if (left < verbs[i].argc) {
494 log_error("Too few arguments.");
495 return -EINVAL;
496 }
497
498 break;
499
500 case LESS:
501 if (left > verbs[i].argc) {
502 log_error("Too many arguments.");
503 return -EINVAL;
504 }
505
506 break;
507
508 default:
509 assert_not_reached("Unknown comparison operator.");
510 }
511
512 return verbs[i].dispatch(argv + optind, left);
513}
514
515int main(int argc, char* argv[]) {
516 int r;
517
518 log_parse_environment();
519 log_open();
520
521 r = parse_argv(argc, argv);
522 if (r <= 0)
523 goto finish;
524
525 r = networkctl_main(argc, argv);
526
527finish:
528 pager_close();
529
530 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
531}