networkctl.c revision 266b538958932e6fc27dfce4917336e70e17e29e
d29201dd5328b88140ce050100693c501852657dChristian Maeder/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder/***
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc This file is part of systemd.
6d81916b9004f8d9b6032113c5987ab07da47015Karl Luc
98890889ffb2e8f6f722b00e265a211f13b5a861Corneliu-Claudiu Prodescu Copyright 2014 Lennart Poettering
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc systemd is free software; you can redistribute it and/or modify it
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder under the terms of the GNU Lesser General Public License as published by
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder the Free Software Foundation; either version 2.1 of the License, or
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder (at your option) any later version.
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder systemd is distributed in the hope that it will be useful, but
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder WITHOUT ANY WARRANTY; without even the implied warranty of
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder Lesser General Public License for more details.
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder You should have received a copy of the GNU Lesser General Public License
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder along with systemd; If not, see <http://www.gnu.org/licenses/>.
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc***/
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc#include <stdbool.h>
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc#include <getopt.h>
d6ce032cac688f3698be7133d27f53d3967e6749Christian Maeder#include <net/if.h>
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc#include "sd-network.h"
d75d2d11170f1339ebe37d9d9c06aff148637b13Christian Maeder#include "sd-rtnl.h"
66977d201b3ff7ee9c1f992c0f3f701b69eac2c9Karl Luc#include "sd-hwdb.h"
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder#include "libudev.h"
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc#include "strv.h"
551f1476dea3f969775527cb15fd512e86279307Karl Luc#include "build.h"
551f1476dea3f969775527cb15fd512e86279307Karl Luc#include "util.h"
a35bacbc16daf5c10f9accfdfadc4971e9d6f648Iulia Ignatov#include "pager.h"
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder#include "rtnl-util.h"
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder#include "udev-util.h"
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder#include "hwdb-util.h"
3831cf8a3b0ea144a80d13fe0314cc2752e32107Christian Maeder#include "arphrd-list.h"
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc#include "local-addresses.h"
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder#include "socket-util.h"
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc#include "ether-addr-util.h"
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc#include "verbs.h"
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Lucstatic bool arg_no_pager = false;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Lucstatic bool arg_legend = true;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Lucstatic bool arg_all = false;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Lucstatic void pager_open_if_enabled(void) {
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc if (arg_no_pager)
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc return;
d6ce032cac688f3698be7133d27f53d3967e6749Christian Maeder
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc pager_open(false);
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc}
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
551f1476dea3f969775527cb15fd512e86279307Karl Lucstatic int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc const char *t;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc char *p;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc if (iftype == ARPHRD_ETHER && d) {
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc const char *devtype, *id = NULL;
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc /* WLANs have iftype ARPHRD_ETHER, but we want
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc * to show a more useful type string for
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc * them */
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc devtype = udev_device_get_devtype(d);
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc if (streq_ptr(devtype, "wlan"))
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc id = "wlan";
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc else if (streq_ptr(devtype, "wwan"))
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder id = "wwan";
3831cf8a3b0ea144a80d13fe0314cc2752e32107Christian Maeder
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc if (id) {
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc p = strdup(id);
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc if (!p)
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc return -ENOMEM;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc *ret = p;
d6ce032cac688f3698be7133d27f53d3967e6749Christian Maeder return 1;
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc }
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc }
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
551f1476dea3f969775527cb15fd512e86279307Karl Luc t = arphrd_to_name(iftype);
7b1111ca3b126f71cce47e60ce4b56e6f92422e9Karl Luc if (!t) {
7b1111ca3b126f71cce47e60ce4b56e6f92422e9Karl Luc *ret = NULL;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc return 0;
a35bacbc16daf5c10f9accfdfadc4971e9d6f648Iulia Ignatov }
a35bacbc16daf5c10f9accfdfadc4971e9d6f648Iulia Ignatov
a35bacbc16daf5c10f9accfdfadc4971e9d6f648Iulia Ignatov p = strdup(t);
a35bacbc16daf5c10f9accfdfadc4971e9d6f648Iulia Ignatov if (!p)
a35bacbc16daf5c10f9accfdfadc4971e9d6f648Iulia Ignatov return -ENOMEM;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
51846950b4b1f31342008cf17f667859a5f21949Christian Maeder ascii_strlower(p);
3831cf8a3b0ea144a80d13fe0314cc2752e32107Christian Maeder *ret = p;
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc return 0;
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc}
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luctypedef struct LinkInfo {
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc const char *name;
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc int ifindex;
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc unsigned iftype;
7165a916d2fa1bf87c4741ec63b253413eebbf69Karl Luc} LinkInfo;
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Lucstatic int link_info_compare(const void *a, const void *b) {
de03ed90ff6efbbc4751301bcf6b50d9790c1afbKarl Luc const LinkInfo *x = a, *y = b;
c51d1f5ff88cce030fe543e271ca6b85625b70d8Karl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc return x->ifindex - y->ifindex;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc}
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Lucstatic int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc _cleanup_free_ LinkInfo *links = NULL;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc size_t size = 0, c = 0;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc sd_rtnl_message *i;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc int r;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc for (i = m; i; i = sd_rtnl_message_next(i)) {
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc const char *name;
75067b1beba1380cde707c30e7fc050d86f6927fKarl Luc unsigned iftype;
uint16_t type;
int ifindex;
r = sd_rtnl_message_get_type(i, &type);
if (r < 0)
return r;
if (type != RTM_NEWLINK)
continue;
r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
if (r < 0)
return r;
r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
if (r < 0)
return r;
r = sd_rtnl_message_link_get_type(i, &iftype);
if (r < 0)
return r;
if (!GREEDY_REALLOC(links, size, c+1))
return -ENOMEM;
links[c].name = name;
links[c].ifindex = ifindex;
links[c].iftype = iftype;
c++;
}
qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
*ret = links;
links = NULL;
return (int) c;
}
static void operational_state_to_color(const char *state, const char **on, const char **off) {
assert(on);
assert(off);
if (streq_ptr(state, "routable")) {
*on = ansi_highlight_green();
*off = ansi_highlight_off();
} else if (streq_ptr(state, "degraded")) {
*on = ansi_highlight_yellow();
*off = ansi_highlight_off();
} else
*on = *off = "";
}
static void setup_state_to_color(const char *state, const char **on, const char **off) {
assert(on);
assert(off);
if (streq_ptr(state, "configured")) {
*on = ansi_highlight_green();
*off = ansi_highlight_off();
} else if (streq_ptr(state, "configuring")) {
*on = ansi_highlight_yellow();
*off = ansi_highlight_off();
} else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
*on = ansi_highlight_red();
*off = ansi_highlight_off();
} else
*on = *off = "";
}
static int list_links(int argc, char *argv[], void *userdata) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
_cleanup_free_ LinkInfo *links = NULL;
int r, c, i;
pager_open_if_enabled();
r = sd_rtnl_open(&rtnl, 0);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
udev = udev_new();
if (!udev)
return log_error_errno(errno, "Failed to connect to udev: %m");
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_message_request_dump(req, true);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_call(rtnl, req, 0, &reply);
if (r < 0)
return log_error_errno(r, "Failed to enumerate links: %m");
if (arg_legend)
printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
c = decode_and_sort_links(reply, &links);
if (c < 0)
return rtnl_log_parse_error(c);
for (i = 0; i < c; i++) {
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
const char *on_color_operational, *off_color_operational,
*on_color_setup, *off_color_setup;
char devid[2 + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *t = NULL;
sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
sprintf(devid, "n%i", links[i].ifindex);
d = udev_device_new_from_device_id(udev, devid);
link_get_type_string(links[i].iftype, d, &t);
printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
links[i].ifindex, links[i].name, strna(t),
on_color_operational, strna(operational_state), off_color_operational,
on_color_setup, strna(setup_state), off_color_setup);
}
if (arg_legend)
printf("\n%i links listed.\n", c);
return 0;
}
/* IEEE Organizationally Unique Identifier vendor string */
static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) {
const char *description;
char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc;
int r;
assert(ret);
if (!hwdb)
return -EINVAL;
if (!mac)
return -EINVAL;
/* skip commonly misused 00:00:00 (Xerox) prefix */
if (memcmp(mac, "\0\0\0", 3) == 0)
return -EINVAL;
snprintf(modalias, sizeof(modalias), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
if (r < 0)
return r;
desc = strdup(description);
if (!desc)
return -ENOMEM;
*ret = desc;
return 0;
}
static int get_gateway_description(
sd_rtnl *rtnl,
sd_hwdb *hwdb,
int ifindex,
int family,
union in_addr_union *gateway,
char **gateway_description) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
sd_rtnl_message *m;
int r;
assert(rtnl);
assert(ifindex >= 0);
assert(family == AF_INET || family == AF_INET6);
assert(gateway);
assert(gateway_description);
r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
if (r < 0)
return r;
r = sd_rtnl_message_request_dump(req, true);
if (r < 0)
return r;
r = sd_rtnl_call(rtnl, req, 0, &reply);
if (r < 0)
return r;
for (m = reply; m; m = sd_rtnl_message_next(m)) {
union in_addr_union gw = {};
struct ether_addr mac = {};
uint16_t type;
int ifi, fam;
r = sd_rtnl_message_get_errno(m);
if (r < 0) {
log_error_errno(r, "got error: %m");
continue;
}
r = sd_rtnl_message_get_type(m, &type);
if (r < 0) {
log_error_errno(r, "could not get type: %m");
continue;
}
if (type != RTM_NEWNEIGH) {
log_error("type is not RTM_NEWNEIGH");
continue;
}
r = sd_rtnl_message_neigh_get_family(m, &fam);
if (r < 0) {
log_error_errno(r, "could not get family: %m");
continue;
}
if (fam != family) {
log_error("family is not correct");
continue;
}
r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
if (r < 0) {
log_error_errno(r, "could not get ifindex: %m");
continue;
}
if (ifindex > 0 && ifi != ifindex)
continue;
switch (fam) {
case AF_INET:
r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
if (r < 0)
continue;
break;
case AF_INET6:
r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
if (r < 0)
continue;
break;
default:
continue;
}
if (!in_addr_equal(fam, &gw, gateway))
continue;
r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
if (r < 0)
continue;
r = ieee_oui(hwdb, &mac, gateway_description);
if (r < 0)
continue;
return 0;
}
return -ENODATA;
}
static int dump_gateways(
sd_rtnl *rtnl,
sd_hwdb *hwdb,
const char *prefix,
int ifindex) {
_cleanup_free_ struct local_address *local = NULL;
int r, n, i;
n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
if (n < 0)
return n;
for (i = 0; i < n; i++) {
_cleanup_free_ char *gateway = NULL, *description = NULL;
r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
if (r < 0)
return r;
r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
if (r < 0)
log_debug_errno(r, "Could not get description of gateway: %m");
printf("%*s%s",
(int) strlen(prefix),
i == 0 ? prefix : "",
gateway);
if (description)
printf(" (%s)", description);
/* Show interface name for the entry if we show
* entries for all interfaces */
if (ifindex <= 0) {
char name[IF_NAMESIZE+1];
if (if_indextoname(local[i].ifindex, name)) {
fputs(" on ", stdout);
fputs(name, stdout);
} else
printf(" on %%%i", local[i].ifindex);
}
fputc('\n', stdout);
}
return 0;
}
static int dump_addresses(
sd_rtnl *rtnl,
const char *prefix,
int ifindex) {
_cleanup_free_ struct local_address *local = NULL;
int r, n, i;
n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
if (n < 0)
return n;
for (i = 0; i < n; i++) {
_cleanup_free_ char *pretty = NULL;
r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
if (r < 0)
return r;
printf("%*s%s",
(int) strlen(prefix),
i == 0 ? prefix : "",
pretty);
if (ifindex <= 0) {
char name[IF_NAMESIZE+1];
if (if_indextoname(local[i].ifindex, name)) {
fputs(" on ", stdout);
fputs(name, stdout);
} else
printf(" on %%%i", local[i].ifindex);
}
fputc('\n', stdout);
}
return 0;
}
static void dump_list(const char *prefix, char **l) {
char **i;
STRV_FOREACH(i, l) {
printf("%*s%s\n",
(int) strlen(prefix),
i == l ? prefix : "",
*i);
}
}
static int link_status_one(
sd_rtnl *rtnl,
struct udev *udev,
sd_hwdb *hwdb,
const char *name) {
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
char devid[2 + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *t = NULL, *network = NULL;
const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
const char *on_color_operational, *off_color_operational,
*on_color_setup, *off_color_setup;
struct ether_addr e;
unsigned iftype;
int r, ifindex;
bool have_mac;
uint32_t mtu;
assert(rtnl);
assert(udev);
assert(name);
if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
else {
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
}
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_call(rtnl, req, 0, &reply);
if (r < 0)
return log_error_errno(r, "Failed to query link: %m");
r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
if (r < 0)
return rtnl_log_parse_error(r);
r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
if (r < 0)
return rtnl_log_parse_error(r);
r = sd_rtnl_message_link_get_type(reply, &iftype);
if (r < 0)
return rtnl_log_parse_error(r);
have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
if (have_mac) {
const uint8_t *p;
bool all_zeroes = true;
for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
if (*p != 0) {
all_zeroes = false;
break;
}
if (all_zeroes)
have_mac = false;
}
sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
sd_network_link_get_operational_state(ifindex, &operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
sd_network_link_get_setup_state(ifindex, &setup_state);
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
sd_network_link_get_dns(ifindex, &dns);
sd_network_link_get_ntp(ifindex, &ntp);
sd_network_link_get_domains(ifindex, &domains);
r = sd_network_link_get_wildcard_domain(ifindex);
if (r > 0) {
char *wildcard;
wildcard = strdup("*");
if (!wildcard)
return log_oom();
if (strv_consume(&domains, wildcard) < 0)
return log_oom();
}
sprintf(devid, "n%i", ifindex);
d = udev_device_new_from_device_id(udev, devid);
if (d) {
link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
path = udev_device_get_property_value(d, "ID_PATH");
vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
if (!vendor)
vendor = udev_device_get_property_value(d, "ID_VENDOR");
model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
if (!model)
model = udev_device_get_property_value(d, "ID_MODEL");
}
link_get_type_string(iftype, d, &t);
sd_network_link_get_network_file(ifindex, &network);
printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
printf(" Link File: %s\n"
"Network File: %s\n"
" Type: %s\n"
" State: %s%s%s (%s%s%s)\n",
strna(link),
strna(network),
strna(t),
on_color_operational, strna(operational_state), off_color_operational,
on_color_setup, strna(setup_state), off_color_setup);
if (path)
printf(" Path: %s\n", path);
if (driver)
printf(" Driver: %s\n", driver);
if (vendor)
printf(" Vendor: %s\n", vendor);
if (model)
printf(" Model: %s\n", model);
if (have_mac) {
_cleanup_free_ char *description = NULL;
char ea[ETHER_ADDR_TO_STRING_MAX];
ieee_oui(hwdb, &e, &description);
if (description)
printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
else
printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
}
if (mtu > 0)
printf(" MTU: %u\n", mtu);
dump_addresses(rtnl, " Address: ", ifindex);
dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
if (!strv_isempty(dns))
dump_list(" DNS: ", dns);
if (!strv_isempty(domains))
dump_list(" Domain: ", domains);
if (!strv_isempty(ntp))
dump_list(" NTP: ", ntp);
return 0;
}
static int link_status(int argc, char *argv[], void *userdata) {
_cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char **name;
int r;
r = sd_rtnl_open(&rtnl, 0);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
udev = udev_new();
if (!udev)
return log_error_errno(errno, "Failed to connect to udev: %m");
r = sd_hwdb_new(&hwdb);
if (r < 0)
log_debug_errno(r, "Failed to open hardware database: %m");
if (argc <= 1 && !arg_all) {
_cleanup_free_ char *operational_state = NULL;
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
const char *on_color_operational, *off_color_operational;
sd_network_get_operational_state(&operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
printf("%s%s%s State: %s%s%s\n",
on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
on_color_operational, strna(operational_state), off_color_operational);
dump_addresses(rtnl, " Address: ", 0);
dump_gateways(rtnl, hwdb, " Gateway: ", 0);
sd_network_get_dns(&dns);
if (!strv_isempty(dns))
dump_list(" DNS: ", dns);
sd_network_get_domains(&domains);
if (!strv_isempty(domains))
dump_list(" Domain: ", domains);
sd_network_get_ntp(&ntp);
if (!strv_isempty(ntp))
dump_list(" NTP: ", ntp);
return 0;
}
pager_open_if_enabled();
if (arg_all) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
_cleanup_free_ LinkInfo *links = NULL;
int c, i;
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_message_request_dump(req, true);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_call(rtnl, req, 0, &reply);
if (r < 0)
return log_error_errno(r, "Failed to enumerate links: %m");
c = decode_and_sort_links(reply, &links);
if (c < 0)
return rtnl_log_parse_error(c);
for (i = 0; i < c; i++) {
if (i > 0)
fputc('\n', stdout);
link_status_one(rtnl, udev, hwdb, links[i].name);
}
} else {
STRV_FOREACH(name, argv + 1) {
if (name != argv + 1)
fputc('\n', stdout);
link_status_one(rtnl, udev, hwdb, *name);
}
}
return 0;
}
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Query and control the networking subsystem.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" -a --all Show status for all links\n\n"
"Commands:\n"
" list List links\n"
" status LINK Show link status\n"
, program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "all", no_argument, NULL, 'a' },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case 'a':
arg_all = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
return 1;
}
static int networkctl_main(int argc, char *argv[]) {
const Verb verbs[] = {
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
{ "status", 1, VERB_ANY, 0, link_status },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
int main(int argc, char* argv[]) {
int r;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = networkctl_main(argc, argv);
finish:
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}