networkd-link.c revision ea6ec096db5f9f3562b9bbf438da7fe21411c74d
/*-*- 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 "util.h"
#include "virt.h"
#include "fileio.h"
#include "bus-util.h"
#include "udev-util.h"
#include "libudev-private.h"
#include "network-internal.h"
#include "networkd-link.h"
#include "networkd-netdev.h"
#include "conf-parser.h"
#include "dhcp-lease-internal.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;
return false;
}
: "")
int r;
r = sd_rtnl_message_link_get_flags(m, &flags);
if (r < 0) {
return r;
}
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;
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;
}
if (!link)
return;
}
}
}
return NULL;
}
if (link)
return link;
}
assert(m);
if (!link)
return -ENODEV;
return 0;
}
return;
return;
}
return;
return;
}
}
int r = 0, k;
return 0;
if (link->dhcp_client) {
if (k < 0) {
strerror(-r));
r = k;
}
}
if (k < 0) {
strerror(-r));
r = k;
}
}
if(link->icmp6_router_discovery) {
if (link->dhcp6_client) {
if (k < 0) {
strerror(-r));
r = k;
}
}
if (k < 0) {
"Could not stop ICMPv6 router discovery: %s",
strerror(-r));
r = k;
}
}
if (k < 0) {
strerror(-r));
r = k;
}
}
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;
}
if (!link->static_configured)
return;
if (link_ipv4ll_enabled(link))
if (!link->ipv4ll_address ||
!link->ipv4ll_route)
return;
return;
return;
}
int r;
link->link_messages --;
return 1;
r = sd_rtnl_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) {
"could not set routes: %s",
strerror(-r));
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_rtnl_message_get_errno(m);
if (r < 0 && r != -ESRCH)
return 1;
}
int r;
assert(m);
link->link_messages --;
return 1;
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST)
else if (r >= 0)
if (link->link_messages == 0) {
}
return 1;
}
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)) {
struct in_addr pool_start;
if (!address) {
"Failed to find suitable address for DHCPv4 server instance.");
return 0;
}
if (r < 0)
return r;
/* offer 32 addresses starting from the address following the server address */
&pool_start, 32);
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;
r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
main_address->prefixlen);
if (r < 0)
return r;
*/
if (r < 0) {
"instance: %s", strerror(-r));
return 0;
}
}
if (link->link_messages == 0) {
} else
return 0;
}
int r;
assert(m);
return 1;
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EADDRNOTAVAIL)
return 1;
}
int r = 0;
if(r < 0) {
break;
}
}
return r;
}
int r;
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
"MESSAGE=%-*s: could not join netdev: %s",
"ERRNO=%d", -r,
NULL);
return 1;
}
return 0;
}
int r;
return 1;
r = sd_bus_message_get_errno(m);
if (r > 0)
strerror(r));
return 1;
}
int r = 0;
/* TODO: replace by assert when we can rely on kdbus */
"Not connected to system bus, ignoring transient hostname.");
return 0;
}
&m,
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
"org.freedesktop.hostname1",
"SetHostname");
if (r < 0)
return r;
if (r < 0)
return r;
link, 0);
if (r < 0) {
strerror(-r));
return r;
}
return 0;
}
int r;
assert(m);
return 1;
r = sd_rtnl_message_get_errno(m);
if (r < 0)
return 1;
}
int r;
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
0, NULL);
if (r < 0) {
"Could not send rtnetlink message: %s",
strerror(-r));
return r;
}
return 0;
}
int r;
return 0;
if (r < 0) {
return r;
}
if (r < 0) {
"Could not set message family %s", strerror(-r));
return r;
}
if (r < 0) {
"Could not append IFLA_PROTINFO attribute: %s",
strerror(-r));
return r;
}
if (r < 0) {
"Could not append IFLA_BRPORT_COST attribute: %s",
strerror(-r));
return r;
}
}
if (r < 0) {
"Could not append IFLA_LINKINFO attribute: %s",
strerror(-r));
return r;
}
if (r < 0) {
"Could not send rtnetlink message: %s",
strerror(-r));
return r;
}
return r;
}
int r;
if (event != UPDATE_INFO)
return;
if (r < 0)
}
int r;
if (link_ipv4ll_enabled(link)) {
if (r < 0) {
"link-local address");
return r;
}
}
if (link_dhcp4_enabled(link)) {
if (r < 0) {
"lease");
return r;
}
}
if (link_dhcp6_enabled(link)) {
if (r < 0) {
"could not start IPv6 router discovery");
return r;
}
}
if (link_lldp_enabled(link)) {
if (r < 0) {
return r;
}
}
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_rtnl_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) {
return r;
}
if (r < 0) {
strerror(-r));
return r;
}
if (r < 0) {
return r;
}
}
if (r < 0) {
return r;
}
}
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
if (!link_ipv6ll_enabled(link)) {
if (r < 0) {
return r;
}
}
if (r < 0) {
return r;
}
}
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
0, NULL);
if (r < 0) {
"Could not send rtnetlink message: %s",
strerror(-r));
return r;
}
return 0;
}
int r;
if (r < 0) {
return r;
}
}
r = link_set_bridge(link);
if (r < 0) {
"Could not set bridge message: %s",
strerror(-r));
}
}
return link_enter_set_addresses(link);
}
void *userdata) {
int r;
return 1;
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
return 1;
} else
return 1;
}
Iterator i;
int r;
return link_joined(link);
"MESSAGE=%-*s: enslaving by '%s'",
NULL);
if (r < 0) {
"MESSAGE=%-*s: could not join netdev '%s': %s",
strerror(-r),
NULL);
return r;
}
}
"MESSAGE=%-*s: enslaving by '%s'",
NULL);
if (r < 0) {
"MESSAGE=%-*s: could not join netdev '%s': %s",
strerror(-r),
NULL);
return r;
}
}
"MESSAGE=%-*s: enslaving by '%s'",
NULL);
if (r < 0) {
"MESSAGE=%-*s: could not join netdev '%s': %s",
strerror(-r),
return r;
}
}
return 0;
}
const char *p = NULL;
bool b;
int r;
r = write_string_file_no_create(p, one_zero(b));
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv4 forwarding for interface %s: %m", link->ifname);
if (b) {
/* If IP forwarding is turned on for this interface,
* then propagate this to the global setting. Given
* that turning this on has side-effects on other
* fields, we'll try to avoid doing this unless
* necessary, hence check the previous value
* first. Note that we never turn this option off
* again, since all interfaces we manage do not do
* forwarding anyway by default, and ownership rules
* of this control are so unclear. */
if (r < 0)
if (r < 0)
}
}
return 0;
}
const char *p = NULL;
int r;
if (r < 0)
return 0;
}
int 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 (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 = icmp6_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;
}
return link_enter_join_netdev(link);
}
void *userdata) {
int r;
return 1;
if (r == -ENOENT) {
return 1;
} else if (r < 0)
return r;
if (network->dhcp_server)
}
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;
}
Address *i;
if (address_equal(i, needle))
return i;
return NULL;
}
int r, ifindex;
assert(m);
if (sd_rtnl_message_is_error(message)) {
if (r < 0)
log_warning_errno(r, "rtnl: failed to receive address: %m");
return 0;
}
if (r < 0) {
log_warning("rtnl: could not get message type");
return 0;
}
if (r < 0) {
log_warning_errno(r, "rtnl: could not get ifindex: %m");
return 0;
} else if (ifindex <= 0) {
return 0;
} else {
if (r < 0 || !link) {
/* when enumerating we might be out of sync, but we will
* get the address again, so just ignore it */
if (!m->enumerating)
return 0;
}
}
r = address_new_dynamic(&address);
if (r < 0)
return r;
return 0;
}
if (r < 0) {
return 0;
}
if (r < 0) {
return 0;
}
if (r < 0) {
return 0;
}
case AF_INET:
if (r < 0) {
return 0;
}
break;
case AF_INET6:
if (r < 0) {
return 0;
}
break;
default:
assert_not_reached("invalid address family");
}
return 0;
}
if (r >= 0) {
valid_str = "ever";
else
}
switch (type) {
case RTM_NEWADDR:
if (existing) {
} else {
}
break;
case RTM_DELADDR:
if (existing) {
} else
log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
break;
default:
assert_not_reached("Received invalid RTNL message type");
}
return 1;
}
int r;
assert(m);
if (r < 0)
return r;
if (detect_container(NULL) <= 0) {
/* not in a container, udev will be around */
if (!device) {
"could not find udev device: %m");
return -errno;
}
if (udev_device_get_is_initialized(device) <= 0) {
/* not yet ready */
return 0;
}
if (r < 0)
return r;
} else {
/* we are calling a callback directly, so must take a ref */
if (r < 0)
return r;
}
return 0;
}
int r;
r = link_acquire_conf(link);
if (r < 0) {
return r;
}
}
return 0;
}
int r;
r = link_stop_clients(link);
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);
}
return -ENOMEM;
}
if (r >= 0 && mtu > 0) {
if (!link->original_mtu) {
}
if (link->dhcp_client) {
if (r < 0) {
"Could not update MTU in DHCP client: %s",
strerror(-r));
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) {
"Could not update MAC address in IPv4LL client: %s",
strerror(-r));
return r;
}
}
if (link->dhcp_client) {
if (r < 0) {
"Could not update MAC address in DHCP client: %s",
strerror(-r));
return r;
}
}
if (link->dhcp6_client) {
if (r < 0) {
"Could not update MAC address in DHCPv6 client: %s",
strerror(-r));
return r;
}
}
}
}
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;
}
else if (link_has_carrier(link)) {
/* if we have carrier, check what addresses we have */
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
}
}
const char *admin_state, *oper_state;
int r;
if (r < 0)
return r;
return 0;
}
if (r < 0)
return r;
fprintf(f,
"# This is private data. Do not parse.\n"
"ADMIN_STATE=%s\n"
"OPER_STATE=%s\n",
bool space;
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);
}
}
fputs("\n", f);
fprintf(f, "NTP=");
space = false;
if (space)
fputc(' ', f);
space = true;
}
link->dhcp_lease) {
if (r > 0) {
if (space)
fputc(' ', f);
serialize_in_addrs(f, addresses, r);
}
}
fputs("\n", f);
fprintf(f, "DOMAINS=");
space = false;
if (space)
fputc(' ', f);
space = true;
}
link->dhcp_lease) {
const char *domainname;
if (r >= 0) {
if (space)
fputc(' ', f);
fputs(domainname, f);
}
}
fputs("\n", f);
fprintf(f, "WILDCARD_DOMAIN=%s\n",
fprintf(f, "LLMNR=%s\n",
}
if (link->dhcp_lease) {
if (r < 0)
goto fail;
fprintf(f,
"DHCP_LEASE=%s\n",
link->lease_file);
} else
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:
return r;
}
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",
};