chiark / gitweb /
networkctl: properly format MAC addresses
[elogind.git] / src / network / networkctl.c
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
37 static bool arg_no_pager = false;
38 static bool arg_legend = true;
39
40 static void pager_open_if_enabled(void) {
41
42         if (arg_no_pager)
43                 return;
44
45         pager_open(false);
46 }
47
48 static 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
90 static 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
176 static 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
200 static 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
211 static 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;
219                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
220
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);
232
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);
321                 sd_network_get_link_ntp(canonical_ifindex, &ntp);
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
358                 if (have_mac)
359                         printf("  HW Address: %s\n", ether_ntoa(&e));
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
375 static 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
386 static 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
439 static 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
515 int 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
527 finish:
528         pager_close();
529
530         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
531 }