networkd-link.c revision 63348d13fae61fefcb29133bfae8371b33cf4b6d
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Tom Gundersen <teg@jklm.no>
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include "alloc-util.h"
#include "bus-util.h"
#include "dhcp-lease-internal.h"
#include "event-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-link.h"
#include "networkd-netdev.h"
#include "set.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "udev-util.h"
#include "util.h"
#include "virt.h"
return false;
return false;
}
return false;
return false;
}
return false;
return false;
}
return false;
return false;
}
return false;
return false;
}
return false;
return false;
return false;
}
return false;
return false;
return false;
}
if (!socket_ipv6_is_supported())
return false;
return false;
return false;
return false;
}
return false;
return false;
/* If unset use system default (enabled if local forwarding is disabled.
* disabled if local forwarding is enabled).
* If set, ignore or enforce RA independent of local forwarding state.
*/
/* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
return !link_ipv6_forward_enabled(link);
/* accept RA even if ip_forward is enabled */
return true;
else
/* ignore RA */
return false;
}
if (!socket_ipv6_is_supported())
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
}
else if (link_has_carrier(link)) {
Iterator i;
/* if we have carrier, check what addresses we have */
if (!address_is_ready(address))
continue;
}
/* for operstate we also take foreign addresses into account */
if (!address_is_ready(address))
continue;
}
if (scope < RT_SCOPE_SITE)
/* universally accessible addresses found */
else if (scope < RT_SCOPE_HOST)
/* only link or site local addresses found */
else
/* no useful addresses found */
else
}
}
: "")
int r;
r = sd_rtnl_message_link_get_flags(m, &flags);
if (r < 0)
if (r < 0)
/* if we got a message without operstate, take it to mean
the state was unchanged */
return 0;
/* link flags are currently at most 18 bits, let's align to
* printing 20 */
if (unknown_flags_added)
"Unknown link flags gained: %#.5x (ignoring)",
"Unknown link flags lost: %#.5x (ignoring)",
}
return 0;
}
const char *ifname;
int r, ifindex;
if (r < 0)
return r;
else if (type != RTM_NEWLINK)
return -EINVAL;
if (r < 0)
return r;
else if (ifindex <= 0)
return -EINVAL;
if (r < 0)
return r;
if (!link)
return -ENOMEM;
link->rtnl_extended_attrs = true;
return -ENOMEM;
if (r < 0)
if (r < 0)
return -ENOMEM;
if (r < 0)
return -ENOMEM;
if (r < 0)
return -ENOMEM;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
Iterator i;
if (!link)
return;
}
}
if (!link)
return NULL;
return NULL;
return NULL;
}
if (!link)
return NULL;
return link;
}
assert(m);
if (!link)
return -ENODEV;
return 0;
}
return;
return;
}
}
int r = 0, k;
if (link->dhcp_client) {
if (k < 0)
}
if (k < 0)
}
if (link->dhcp6_client) {
if (k < 0)
}
if (link->ndisc_router_discovery) {
if (k < 0)
}
if (k < 0)
}
return r;
}
return;
}
/* The first statically configured address if there is any */
continue;
continue;
return address;
}
/* If that didn't work, find a suitable address we got from the pool */
continue;
return address;
}
return NULL;
}
return 0;
}
Address *a;
Iterator i;
return;
if (!link->static_configured)
return;
if (link_ipv4ll_enabled(link))
if (!link->ipv4ll_address ||
!link->ipv4ll_route)
return;
if (link_ipv6ll_enabled(link))
if (!link->ipv6ll_address)
return;
!link->dhcp4_configured) ||
!link->dhcp6_configured) ||
return;
return;
if (!address_is_ready(a))
return;
return;
}
int r;
link->link_messages --;
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
if (link->link_messages == 0) {
link->static_configured = true;
}
return 1;
}
int r;
if (r < 0) {
return r;
}
link->link_messages ++;
}
if (link->link_messages == 0) {
link->static_configured = true;
} else
return 0;
}
int r;
assert(m);
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH)
return 1;
}
int r;
assert(m);
link->link_messages --;
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
else if (r >= 0)
if (link->link_messages == 0) {
}
return 1;
}
char **a;
return 0;
/* Only look for IPv4 addresses */
continue;
return log_oom();
}
link->dhcp_lease) {
int n;
if (n > 0) {
return log_oom();
n_addresses += n;
}
}
if (n_addresses <= 0)
return 0;
}
char **a;
return 0;
/* Only look for IPv4 addresses */
continue;
return log_oom();
}
link->dhcp_lease) {
int n;
if (n > 0) {
return log_oom();
n_addresses += n;
}
}
if (n_addresses <= 0)
return 0;
}
int r;
if (r < 0) {
return r;
}
link->link_messages ++;
}
/* now that we can figure out a default address for the dhcp server,
start it */
if (link_dhcp4_server_enabled(link)) {
bool acquired_uplink = false;
if (!address) {
return 0;
}
/* use the server address' subnet as the pool */
if (r < 0)
return r;
/* TODO:
r = sd_dhcp_server_set_router(link->dhcp_server,
&main_address->in_addr.in);
if (r < 0)
return r;
*/
if (r < 0)
return r;
}
if (r < 0)
return r;
}
r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
else {
acquired_uplink = true;
if (!uplink) {
log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
r = 0;
} else
}
if (r < 0)
}
r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
else {
if (!acquired_uplink)
if (!uplink) {
log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
r = 0;
} else
}
if (r < 0)
}
else {
r = get_timezone(&buffer);
if (r < 0)
log_warning_errno(r, "Failed to determine timezone: %m");
else
}
if (tz) {
if (r < 0)
return r;
}
}
if (r < 0) {
return 0;
}
}
if (link->link_messages == 0)
else
return 0;
}
int r;
assert(m);
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EADDRNOTAVAIL)
return 1;
}
int r = 0;
if(r < 0) {
break;
}
}
return r;
}
int r;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
return 1;
}
return 0;
}
const sd_bus_error *e;
assert(m);
return 1;
e = sd_bus_message_get_error(m);
if (e)
return 1;
}
int r;
/* TODO: replace by assert when we can rely on kdbus */
return 0;
}
NULL,
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
"org.freedesktop.hostname1",
"SetHostname",
link,
"sb",
false);
if (r < 0)
return 0;
}
const sd_bus_error *e;
assert(m);
return 1;
e = sd_bus_message_get_error(m);
if (e)
return 1;
}
int r;
return 0;
}
NULL,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
"SetTimezone",
link,
"sb",
tz,
false);
if (r < 0)
return 0;
}
int r;
assert(m);
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0)
return 1;
}
int r;
if (r < 0)
if (r < 0)
if (r < 0)
return 0;
}
int r;
if (r < 0)
if (r < 0)
if (r < 0)
if (r < 0)
if (r < 0)
if (r < 0)
if (r < 0)
if (r < 0)
if (r < 0)
}
if (r < 0)
if (r < 0)
return r;
}
int r;
switch (event) {
if (r < 0)
break;
default:
break;
}
}
int r;
if (link_dhcp6_enabled(link)) {
if (r < 0 && r != -EBUSY)
}
if (link_ipv6_accept_ra_enabled(link)) {
if (r < 0 && r != -EBUSY)
}
return 0;
}
int r;
if (link_ipv4ll_enabled(link)) {
if (r < 0)
}
if (link_dhcp4_enabled(link)) {
if (r < 0)
}
if (link_lldp_enabled(link)) {
if (r < 0)
}
return 0;
}
/* see Documentation/networking/operstates.txt in the kernel sources */
return true;
/* operstate may not be implemented, so fall back to flags */
return true;
return false;
}
int r;
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0)
/* we warn but don't fail the link, as it may be
brought up later */
return 1;
}
int r;
if (r < 0)
if (r < 0)
if (r < 0)
}
if (r < 0)
}
if (r < 0)
if (socket_ipv6_is_supported()) {
/* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */
if (r < 0)
if (r < 0)
if (r < 0)
}
if (r < 0)
}
if (r < 0)
if (r < 0)
return 0;
}
int r;
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0)
return 1;
}
int r;
if (r < 0)
if (r < 0)
if (r < 0)
return 0;
}
Link *l;
Iterator i;
int r;
bool required_up = false;
bool link_is_up = false;
return 0;
link_is_up = true;
if (link_has_carrier(l)) {
required_up = true;
break;
}
if (!required_up && link_is_up) {
if (r < 0)
return r;
} else if (required_up && !link_is_up) {
if (r < 0)
return r;
}
return 0;
}
Iterator i;
Link *l;
int r;
return 0;
r = link_handle_bound_to_list(l);
if (r < 0)
return r;
}
return 0;
}
int r;
return 0;
return 0;
r = hashmap_ensure_allocated(h, NULL);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
Manager *m;
Iterator i;
int r;
bool list_updated = false;
continue;
continue;
if (r < 0)
return r;
list_updated = true;
}
}
if (list_updated)
if (r < 0)
return r;
}
return 0;
}
Manager *m;
Iterator i;
int r;
bool list_updated = false;
return 0;
return 0;
if (r < 0)
return r;
list_updated = true;
}
}
if (list_updated)
if (r < 0)
return r;
}
return 0;
}
int r;
r = link_new_bound_by_list(link);
if (r < 0)
return r;
if (r < 0)
return r;
r = link_new_bound_to_list(link);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
Iterator i;
}
return;
}
Iterator i;
}
}
return;
}
bool list_updated = false;
list_updated = true;
}
list_updated = true;
}
if (list_updated)
return;
}
return;
return;
}
int r;
if (r < 0)
return r;
if (r < 0) {
return r;
}
}
r = link_set_bridge(link);
if (r < 0)
}
return link_enter_set_addresses(link);
}
int r;
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
return 1;
} else
return 1;
}
Iterator i;
int r;
return link_joined(link);
NULL);
if (r < 0) {
NULL);
return r;
}
}
NULL);
if (r < 0) {
NULL),
return r;
}
}
NULL);
if (r < 0) {
NULL);
return r;
}
}
return 0;
}
int r;
if (!link_ipv4_forward_enabled(link))
return 0;
/* We propagate the forwarding flag from one interface to the
* global setting one way. This means: as long as at least one
* interface was configured at any time that had IP forwarding
* enabled the setting will stay on for good. We do this
* primarily to keep IPv4 and IPv6 packet forwarding behaviour
* somewhat in sync (see below). */
if (r < 0)
return 0;
}
int r;
if (!link_ipv6_forward_enabled(link))
return 0;
/* On Linux, the IPv6 stack does not not know a per-interface
* packet forwarding setting: either packet forwarding is on
* for all, or off for all. We hence don't bother with a
* per-interface setting, but simply propagate the interface
* flag, if it is set, to the global flag, one-way. Note that
* while IPv4 would allow a per-interface flag, we expose the
* same behaviour there and also propagate the setting from
* one to all, to keep things simple (see above). */
r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
if (r < 0)
return 0;
}
const char *p = NULL;
int r;
if (s < 0)
return 0;
if (r < 0)
return 0;
}
const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (!socket_ipv6_is_supported())
return 0;
return 0;
return 0;
/* We handle router advertisments ourselves, tell the kernel to GTFO */
if (r < 0)
return 0;
}
const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (!socket_ipv6_is_supported())
return 0;
return 0;
return 0;
return 0;
if (r < 0)
return 0;
}
const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (!socket_ipv6_is_supported())
return 0;
return 0;
return 0;
return 0;
if (r < 0)
return 0;
}
Iterator i;
int r;
/* we consider IPv6LL addresses to be managed by the kernel */
continue;
if (r < 0)
return r;
}
/* do not touch routes managed by the kernel */
continue;
if (r < 0)
return r;
}
return 0;
}
int r;
r = link_drop_foreign_config(link);
if (r < 0)
return r;
r = link_set_bridge_fdb(link);
if (r < 0)
return r;
r = link_set_ipv4_forward(link);
if (r < 0)
return r;
r = link_set_ipv6_forward(link);
if (r < 0)
return r;
if (r < 0)
return r;
r = link_set_ipv6_accept_ra(link);
if (r < 0)
return r;
if (r < 0)
return r;
r = link_set_ipv6_hop_limit(link);
if (r < 0)
return r;
if (link_ipv4ll_enabled(link)) {
r = ipv4ll_configure(link);
if (r < 0)
return r;
}
if (link_dhcp4_enabled(link)) {
r = dhcp4_configure(link);
if (r < 0)
return r;
}
if (link_dhcp4_server_enabled(link)) {
if (r < 0)
return r;
if (r < 0)
return r;
}
if (link_dhcp6_enabled(link)) {
r = dhcp6_configure(link);
if (r < 0)
return r;
}
if (link_ipv6_accept_ra_enabled(link)) {
r = ndisc_configure(link);
if (r < 0)
return r;
}
if (link_lldp_enabled(link)) {
if (r < 0)
return r;
if (r < 0)
return r;
lldp_handler, link);
if (r < 0)
return r;
}
if (link_has_carrier(link)) {
r = link_acquire_conf(link);
if (r < 0)
return r;
if (link->ipv6ll_address) {
r = link_acquire_ipv6_conf(link);
if (r < 0)
return r;
}
}
return link_enter_join_netdev(link);
}
void *userdata) {
int r;
return 1;
r = link_new_bound_by_list(link);
if (r < 0)
return r;
if (r < 0)
return r;
if (r == -ENOENT) {
return 1;
} else if (r < 0)
return r;
if (network->dhcp_server)
}
if (r < 0)
return r;
}
r = link_new_bound_to_list(link);
if (r < 0)
return r;
r = link_configure(link);
if (r < 0)
return r;
return 1;
}
int r;
return 0;
if (link->udev_device)
return 0;
/* udev has initialized the link, but we don't know if we have yet
* processed the NEWLINK messages with the latest state. Do a GETLINK,
* when it returns we know that the pending NEWLINKs have already been
* processed and that we are up-to-date */
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
*dhcp4_address = NULL,
*ipv4ll_address = NULL;
union in_addr_union address;
union in_addr_union route_dst;
const char *p;
int r;
"NETWORK_FILE", &network_file,
"ADDRESSES", &addresses,
"ROUTES", &routes,
"DHCP4_ADDRESS", &dhcp4_address,
"IPV4LL_ADDRESS", &ipv4ll_address,
NULL);
if (r < 0 && r != -ENOENT)
if (network_file) {
char *suffix;
/* drop suffix */
if (!suffix) {
goto network_file_fail;
}
*suffix = '\0';
if (r < 0) {
goto network_file_fail;
}
if (r < 0)
}
if (addresses) {
p = addresses;
for (;;) {
char *prefixlen_str;
int family;
unsigned char prefixlen;
if (r < 0) {
continue;
} if (r == 0)
break;
if (!prefixlen_str) {
continue;
}
*prefixlen_str ++ = '\0';
if (r != 1) {
continue;
}
if (r < 0) {
continue;
}
if (r < 0)
}
}
if (routes) {
for (;;) {
char *prefixlen_str;
int family;
if (r < 0) {
continue;
} if (r == 0)
break;
if (!prefixlen_str) {
continue;
}
*prefixlen_str ++ = '\0';
r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime);
if (r != 5) {
"Failed to parse destination prefix length, tos, priority, table or expiration %s",
continue;
}
if (r < 0) {
continue;
}
if (r < 0)
if (lifetime != USEC_INFINITY) {
0, route_expire_handler, route);
if (r < 0)
}
}
}
if (dhcp4_address) {
if (r < 0) {
goto dhcp4_address_fail;
}
if (r < 0)
if (r < 0)
}
if (ipv4ll_address) {
if (r < 0) {
goto ipv4ll_address_fail;
}
if (r < 0)
if (r < 0)
}
return 0;
}
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
if (detect_container() <= 0) {
/* not in a container, udev will be around */
if (!device) {
goto failed;
}
if (udev_device_get_is_initialized(device) <= 0) {
/* not yet ready */
return 0;
}
if (r < 0)
goto failed;
} else {
/* we are calling a callback directly, so must take a ref */
if (r < 0)
goto failed;
}
return 0;
return r;
}
int r;
link->ipv6ll_address = true;
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
r = link_acquire_ipv6_conf(link);
if (r < 0) {
return r;
}
}
return 0;
}
int r;
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
r = link_acquire_conf(link);
if (r < 0) {
return r;
}
}
if (r < 0)
return r;
return 0;
}
int r;
r = link_stop_clients(link);
if (r < 0) {
return r;
}
if (r < 0)
return r;
return 0;
}
int r;
if (link_has_carrier(link)) {
r = link_carrier_lost(link);
if (r < 0)
return r;
r = link_carrier_gained(link);
if (r < 0)
return r;
}
return 0;
}
struct ether_addr mac;
const char *ifname;
int r;
assert(m);
r = link_new_carrier_maps(link);
if (r < 0)
return r;
}
if (r < 0)
return r;
r = link_new_carrier_maps(link);
if (r < 0)
return r;
}
if (r >= 0 && mtu > 0) {
if (!link->original_mtu) {
}
if (link->dhcp_client) {
if (r < 0) {
return r;
}
}
}
/* The kernel may broadcast NEWLINK messages without the MAC address
set, simply ignore them. */
if (r >= 0) {
ETH_ALEN)) {
ETH_ALEN);
"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
mac.ether_addr_octet[0],
if (r < 0)
}
if (link->dhcp_client) {
if (r < 0)
}
if (link->dhcp6_client) {
if (r < 0)
}
}
}
r = link_update_flags(link, m);
if (r < 0)
return r;
if (carrier_gained) {
r = link_carrier_gained(link);
if (r < 0)
return r;
} else if (carrier_lost) {
r = link_carrier_lost(link);
if (r < 0)
return r;
}
return 0;
}
const char *admin_state, *oper_state;
Address *a;
Iterator i;
int r;
return 0;
}
if (r < 0)
goto fail;
fprintf(f,
"# This is private data. Do not parse.\n"
"ADMIN_STATE=%s\n"
"OPER_STATE=%s\n",
bool space;
if (link->dhcp6_client) {
if (r < 0 && r != -ENOMSG)
}
fputs("DNS=", f);
space = false;
if (space)
fputc(' ', f);
space = true;
}
link->dhcp_lease) {
if (r > 0) {
if (space)
fputc(' ', f);
serialize_in_addrs(f, addresses, r);
space = true;
}
}
if (r > 0) {
if (space)
fputc(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
}
}
fputc('\n', f);
fputs("NTP=", f);
space = false;
if (space)
fputc(' ', f);
space = true;
}
link->dhcp_lease) {
if (r > 0) {
if (space)
fputc(' ', f);
serialize_in_addrs(f, addresses, r);
space = true;
}
}
char **hosts;
char **hostname;
&in6_addrs);
if (r > 0) {
if (space)
fputc(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
space = true;
}
if (r > 0) {
if (space)
fputc(' ', f);
space = true;
}
}
}
fputc('\n', f);
fputs("DOMAINS=", f);
space = false;
if (space)
fputc(' ', f);
space = true;
}
link->dhcp_lease) {
const char *domainname;
if (r >= 0) {
if (space)
fputc(' ', f);
fputs(domainname, f);
space = true;
}
}
char **domains;
if (r >= 0) {
if (space)
fputc(' ', f);
space = true;
}
}
}
fputc('\n', f);
fprintf(f, "WILDCARD_DOMAIN=%s\n",
fprintf(f, "LLMNR=%s\n",
fputs("ADDRESSES=", f);
space = false;
if (r < 0)
goto fail;
space = true;
}
fputc('\n', f);
fputs("ROUTES=", f);
space = false;
if (r < 0)
goto fail;
space = true;
}
fputc('\n', f);
}
bool space = false;
fputs("CARRIER_BOUND_TO=", f);
if (space)
fputc(' ', f);
space = true;
}
fputc('\n', f);
}
bool space = false;
fputs("CARRIER_BOUND_BY=", f);
if (space)
fputc(' ', f);
space = true;
}
fputc('\n', f);
}
if (link->dhcp_lease) {
if (r >= 0)
if (r >= 0) {
fputs("DHCP4_ADDRESS=", f);
fputc('\n', f);
}
if (r < 0)
goto fail;
fprintf(f,
"DHCP_LEASE=%s\n",
link->lease_file);
} else
if (r >= 0) {
fputs("IPV4LL_ADDRESS=", f);
fputc('\n', f);
}
}
if (r < 0)
goto fail;
fprintf(f,
"LLDP_FILE=%s\n",
} else
r = fflush_and_check(f);
if (r < 0)
goto fail;
r = -errno;
goto fail;
}
return 0;
fail:
if (temp_path)
}
/* The serialized state in /run is no longer up-to-date. */
int r;
if (r < 0)
/* allocation errors are ignored */
return;
if (r < 0)
/* allocation errors are ignored */
return;
}
/* The serialized state in /run is up-to-date */
}
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
[LINK_STATE_ENSLAVING] = "configuring",
[LINK_STATE_SETTING_ADDRESSES] = "configuring",
[LINK_STATE_SETTING_ROUTES] = "configuring",
[LINK_STATE_CONFIGURED] = "configured",
[LINK_STATE_UNMANAGED] = "unmanaged",
[LINK_STATE_FAILED] = "failed",
[LINK_STATE_LINGER] = "linger",
};
static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
[LINK_OPERSTATE_OFF] = "off",
[LINK_OPERSTATE_NO_CARRIER] = "no-carrier",
[LINK_OPERSTATE_DORMANT] = "dormant",
[LINK_OPERSTATE_CARRIER] = "carrier",
[LINK_OPERSTATE_DEGRADED] = "degraded",
[LINK_OPERSTATE_ROUTABLE] = "routable",
};