/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if.h>
#include "event-util.h"
#include "fd-util.h"
#include "fileio.h"
-#include "format-util.h"
+#include "format-ifname.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "logarithm.h"
#include "udev-util.h"
#include "vrf.h"
+void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret) {
+ assert(link);
+ assert(ret);
+
+ if (link->network && operational_state_range_is_valid(&link->network->required_operstate_for_online))
+ /* If explicitly specified, use it as is. */
+ *ret = link->network->required_operstate_for_online;
+ else if (link->iftype == ARPHRD_CAN)
+ /* CAN devices do not support addressing, hence defaults to 'carrier'. */
+ *ret = (const LinkOperationalStateRange) {
+ .min = LINK_OPERSTATE_CARRIER,
+ .max = LINK_OPERSTATE_CARRIER,
+ };
+ else if (link->network && link->network->bond)
+ /* Bonding slaves do not support addressing. */
+ *ret = (const LinkOperationalStateRange) {
+ .min = LINK_OPERSTATE_ENSLAVED,
+ .max = LINK_OPERSTATE_ENSLAVED,
+ };
+ else if (STRPTR_IN_SET(link->kind, "batadv", "bond", "bridge", "vrf"))
+ /* Some of slave interfaces may be offline. */
+ *ret = (const LinkOperationalStateRange) {
+ .min = LINK_OPERSTATE_DEGRADED_CARRIER,
+ .max = LINK_OPERSTATE_ROUTABLE,
+ };
+ else
+ *ret = LINK_OPERSTATE_RANGE_DEFAULT;
+}
+
+AddressFamily link_required_family_for_online(Link *link) {
+ assert(link);
+
+ if (link->network && link->network->required_family_for_online >= 0)
+ return link->network->required_family_for_online;
+
+ if (link->network && operational_state_range_is_valid(&link->network->required_operstate_for_online))
+ /* If RequiredForOnline= is explicitly specified, defaults to no. */
+ return ADDRESS_FAMILY_NO;
+
+ if (STRPTR_IN_SET(link->kind, "batadv", "bond", "bridge", "vrf"))
+ /* As the minimum required operstate for master interfaces is 'degraded-carrier',
+ * we should request an address assigned to the link for backward compatibility. */
+ return ADDRESS_FAMILY_YES;
+
+ return ADDRESS_FAMILY_NO;
+}
+
bool link_ipv6_enabled(Link *link) {
assert(link);
static Link *link_free(Link *link) {
assert(link);
+ (void) sysctl_clear_link_shadows(link);
+
link_ntp_settings_clear(link);
link_dns_settings_clear(link);
- link->routes = set_free(link->routes);
link->neighbors = set_free(link->neighbors);
link->addresses = set_free(link->addresses);
link->qdiscs = set_free(link->qdiscs);
free(link->driver);
unlink_and_free(link->lease_file);
- unlink_and_free(link->lldp_file);
unlink_and_free(link->state_file);
sd_device_unref(link->dev);
link_set_state(link, LINK_STATE_FAILED);
- (void) link_stop_engines(link, false);
+ if (!ratelimit_below(&link->automatic_reconfigure_ratelimit)) {
+ log_link_warning(link, "The interface entered the failed state frequently, refusing to reconfigure it automatically.");
+ goto stop;
+ }
+
+ log_link_info(link, "Trying to reconfigure the interface.");
+ if (link_reconfigure(link, /* force = */ true) > 0)
+ return;
+
+stop:
+ (void) link_stop_engines(link, /* may_keep_dhcp = */ false);
}
void link_check_ready(Link *link) {
if (!link->activated)
return (void) log_link_debug(link, "%s(): link is not activated.", __func__);
- if (link->iftype == ARPHRD_CAN) {
+ if (link->iftype == ARPHRD_CAN)
/* let's shortcut things for CAN which doesn't need most of checks below. */
- link_set_state(link, LINK_STATE_CONFIGURED);
- return;
- }
+ goto ready;
if (!link->stacked_netdevs_created)
return (void) log_link_debug(link, "%s(): stacked netdevs are not created.", __func__);
* Note, ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */
if (!link_ipv4ll_enabled(link) && !link_dhcp4_enabled(link) &&
!link_dhcp6_enabled(link) && !link_dhcp_pd_is_enabled(link) &&
- (link->network->configure_without_carrier || !link_ipv6_accept_ra_enabled(link)))
+ (link->network->configure_without_carrier || !link_ndisc_enabled(link)))
goto ready;
bool ipv4ll_ready =
(!link->network->dhcp_pd_assign ||
link_check_addresses_ready(link, NETWORK_CONFIG_SOURCE_DHCP_PD));
bool ndisc_ready =
- link_ipv6_accept_ra_enabled(link) && link->ndisc_configured &&
- (!link->network->ipv6_accept_ra_use_autonomous_prefix ||
+ link_ndisc_enabled(link) && link->ndisc_configured &&
+ (!link->network->ndisc_use_autonomous_prefix ||
link_check_addresses_ready(link, NETWORK_CONFIG_SOURCE_NDISC));
/* If the uplink for PD is self, then request the corresponding DHCP protocol is also ready. */
if (dhcp_pd_is_uplink(link, link, /* accept_auto = */ false)) {
if (link_dhcp4_enabled(link) && link->network->dhcp_use_6rd &&
sd_dhcp_lease_has_6rd(link->dhcp_lease)) {
- if (!dhcp4_ready)
+ if (!link->dhcp4_configured)
return (void) log_link_debug(link, "%s(): DHCPv4 6rd prefix is assigned, but DHCPv4 protocol is not finished yet.", __func__);
if (!dhcp_pd_ready)
return (void) log_link_debug(link, "%s(): DHCPv4 is finished, but prefix acquired by DHCPv4-6rd is not assigned yet.", __func__);
if (link_dhcp6_enabled(link) && link->network->dhcp6_use_pd_prefix &&
sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) {
- if (!dhcp6_ready)
+ if (!link->dhcp6_configured)
return (void) log_link_debug(link, "%s(): DHCPv6 IA_PD prefix is assigned, but DHCPv6 protocol is not finished yet.", __func__);
if (!dhcp_pd_ready)
return (void) log_link_debug(link, "%s(): DHCPv6 is finished, but prefix acquired by DHCPv6 IA_PD is not assigned yet.", __func__);
log_link_debug(link, "Acquiring IPv4 link-local address.");
}
- if (link->dhcp_server) {
- r = sd_dhcp_server_start(link->dhcp_server);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
- }
+ r = link_start_dhcp4_server(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
r = ipv4acd_start(link);
if (r < 0)
/* Update up or down state of interfaces which depend on this interface's carrier state. */
- if (hashmap_isempty(link->bound_by_links))
- return 0;
-
HASHMAP_FOREACH(l, link->bound_by_links) {
r = link_handle_bound_to_list(l);
if (r < 0)
RET_GATHER(ret, address_remove(address, link));
break;
}
+ case REQUEST_TYPE_NEIGHBOR: {
+ Neighbor *neighbor = ASSERT_PTR(req->userdata);
+
+ if (neighbor_get(link, neighbor, NULL) < 0)
+ RET_GATHER(ret, neighbor_remove(neighbor, link));
+ break;
+ }
+ case REQUEST_TYPE_NEXTHOP: {
+ NextHop *nexthop = ASSERT_PTR(req->userdata);
+
+ if (nexthop_get_by_id(link->manager, nexthop->id, NULL) < 0)
+ RET_GATHER(ret, nexthop_remove(nexthop, link->manager));
+ break;
+ }
+ case REQUEST_TYPE_ROUTE: {
+ Route *route = ASSERT_PTR(req->userdata);
+
+ if (route_get(link->manager, route, NULL) < 0)
+ RET_GATHER(ret, route_remove(route, link->manager));
+ break;
+ }
default:
;
}
- request_detach(link->manager, req);
+ request_detach(req);
}
return ret;
assert(link);
assert(link->manager);
- r = link_drop_managed_routes(link);
-
- RET_GATHER(r, link_drop_managed_nexthops(link));
- RET_GATHER(r, link_drop_managed_addresses(link));
- RET_GATHER(r, link_drop_managed_neighbors(link));
- RET_GATHER(r, link_drop_managed_routing_policy_rules(link));
+ r = link_drop_static_routes(link);
+ RET_GATHER(r, link_drop_static_nexthops(link));
+ RET_GATHER(r, link_drop_static_addresses(link));
+ RET_GATHER(r, link_drop_static_neighbors(link));
+ RET_GATHER(r, link_drop_static_routing_policy_rules(link));
return r;
}
}
log_link_full(link, warn ? LOG_WARNING : LOG_DEBUG,
- "found matching network '%s'%s.",
- network->filename,
- warn ? ", based on potentially unpredictable interface name" : "");
+ "Found matching .network file%s: %s",
+ warn ? ", based on potentially unpredictable interface name" : "",
+ network->filename);
if (network->unmanaged)
return -ENOENT;
return 0;
}
- return -ENOENT;
+ return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found.");
}
int link_reconfigure_impl(Link *link, bool force) {
Network *network = NULL;
- NetDev *netdev = NULL;
int r;
assert(link);
+ link_assign_netdev(link);
+
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
return 0;
- r = netdev_get(link->manager, link->ifname, &netdev);
- if (r < 0 && r != -ENOENT)
- return r;
-
r = link_get_network(link, &network);
if (r < 0 && r != -ENOENT)
return r;
link_free_engines(link);
link->network = network_unref(link->network);
- netdev_unref(link->netdev);
- link->netdev = netdev_ref(netdev);
-
if (!network) {
link_set_state(link, LINK_STATE_UNMANAGED);
return 0;
return 0; /* 0 means no-op. */
r = link_call_getlink(link, force ? link_force_reconfigure_handler : link_reconfigure_handler);
- if (r < 0)
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+ link_enter_failed(link);
return r;
+ }
+
+ if (force || link->state == LINK_STATE_FAILED)
+ link_set_state(link, LINK_STATE_INITIALIZED);
return 1; /* 1 means the interface will be reconfigured. */
}
+typedef struct ReconfigureData {
+ Link *link;
+ Manager *manager;
+ sd_bus_message *message;
+} ReconfigureData;
+
+static ReconfigureData* reconfigure_data_free(ReconfigureData *data) {
+ if (!data)
+ return NULL;
+
+ link_unref(data->link);
+ sd_bus_message_unref(data->message);
+
+ return mfree(data);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(ReconfigureData*, reconfigure_data_free);
+
+static void reconfigure_data_destroy_callback(ReconfigureData *data) {
+ int r;
+
+ assert(data);
+ assert(data->manager);
+ assert(data->manager->reloading > 0);
+ assert(data->message);
+
+ data->manager->reloading--;
+ if (data->manager->reloading <= 0) {
+ r = sd_bus_reply_method_return(data->message, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to send reply for 'Reload' DBus method, ignoring: %m");
+ }
+
+ reconfigure_data_free(data);
+}
+
+static int reconfigure_handler_on_bus_method_reload(sd_netlink *rtnl, sd_netlink_message *m, ReconfigureData *data) {
+ assert(data);
+ assert(data->link);
+ return link_reconfigure_handler_internal(rtnl, m, data->link, /* force = */ false);
+}
+
+int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ _cleanup_(reconfigure_data_freep) ReconfigureData *data = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+ assert(message);
+
+ /* See comments in link_reconfigure() above. */
+ if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER))
+ return 0;
+
+ data = new(ReconfigureData, 1);
+ if (!data) {
+ r = -ENOMEM;
+ goto failed;
+ }
+
+ *data = (ReconfigureData) {
+ .link = link_ref(link),
+ .manager = link->manager,
+ .message = sd_bus_message_ref(message),
+ };
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK, link->ifindex);
+ if (r < 0)
+ goto failed;
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ reconfigure_handler_on_bus_method_reload,
+ reconfigure_data_destroy_callback, data);
+ if (r < 0)
+ goto failed;
+
+ TAKE_PTR(data);
+ link->manager->reloading++;
+
+ if (link->state == LINK_STATE_FAILED)
+ link_set_state(link, LINK_STATE_INITIALIZED);
+
+ return 0;
+
+failed:
+ log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+ link_enter_failed(link);
+ return r;
+}
+
static int link_initialized_and_synced(Link *link) {
int r;
return 0;
}
- r = sd_device_get_is_initialized(device);
+ r = device_is_processed(device);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not determine whether the device is initialized: %m");
+ return log_link_warning_errno(link, r, "Could not determine whether the device is processed by udevd: %m");
if (r == 0) {
/* not yet ready */
log_link_debug(link, "link pending udev initialization...");
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable carrier lost timer, ignoring: %m");
+ /* Process BindCarrier= setting specified by other interfaces. This is independent of the .network
+ * file assigned to this interface, but depends on .network files assigned to other interfaces.
+ * Hence, this can and should be called earlier. */
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
+
/* If a wireless interface was connected to an access point, and the SSID is changed (that is,
* both previous_ssid and ssid are non-NULL), then the connected wireless network could be
* changed. So, always reconfigure the link. Which means e.g. the DHCP client will be
if (r != 0)
return r;
- r = link_handle_bound_by_list(link);
- if (r < 0)
- return r;
-
if (link->iftype == ARPHRD_CAN)
/* let's shortcut things for CAN which doesn't need most of what's done below. */
return 0;
}
static int link_carrier_lost_impl(Link *link) {
- int r, ret = 0;
+ int ret = 0;
assert(link);
link->previous_ssid = mfree(link->previous_ssid);
+ ret = link_handle_bound_by_list(link);
+
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 0;
+ return ret;
if (!link->network)
- return 0;
+ return ret;
- r = link_stop_engines(link, false);
- if (r < 0)
- ret = r;
-
- r = link_drop_managed_config(link);
- if (r < 0 && ret >= 0)
- ret = r;
+ RET_GATHER(ret, link_stop_engines(link, false));
+ RET_GATHER(ret, link_drop_managed_config(link));
return ret;
}
static int link_carrier_lost(Link *link) {
uint16_t dhcp_mtu;
usec_t usec;
- int r;
assert(link);
- r = link_handle_bound_by_list(link);
- if (r < 0)
- return r;
-
if (link->iftype == ARPHRD_CAN)
/* let's shortcut things for CAN which doesn't need most of what's done below. */
- return 0;
+ usec = 0;
- if (!link->network)
- return 0;
+ else if (!link->network)
+ usec = 0;
- if (link->network->ignore_carrier_loss_set)
+ else if (link->network->ignore_carrier_loss_set)
/* If IgnoreCarrierLoss= is explicitly specified, then use the specified value. */
usec = link->network->ignore_carrier_loss_usec;
/* We set the ipv6 mtu after the device mtu, but the kernel resets
* ipv6 mtu on NETDEV_UP, so we need to reset it. */
- r = link_set_ipv6_mtu(link);
+ r = link_set_ipv6_mtu(link, LOG_INFO);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
else
operstate = LINK_OPERSTATE_ENSLAVED;
+ LinkOperationalStateRange req;
+ link_required_operstate_for_online(link, &req);
+
/* Only determine online state for managed links with RequiredForOnline=yes */
if (!link->network || !link->network->required_for_online)
online_state = _LINK_ONLINE_STATE_INVALID;
- else if (operstate < link->network->required_operstate_for_online.min ||
- operstate > link->network->required_operstate_for_online.max)
+
+ else if (!operational_state_is_in_range(operstate, &req))
online_state = LINK_ONLINE_STATE_OFFLINE;
+
else {
- AddressFamily required_family = link->network->required_family_for_online;
+ AddressFamily required_family = link_required_family_for_online(link);
bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
bool needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6;
* to offline in the blocks below. */
online_state = LINK_ONLINE_STATE_ONLINE;
- if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_DEGRADED) {
+ if (req.min >= LINK_OPERSTATE_DEGRADED) {
if (needs_ipv4 && ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
online_state = LINK_ONLINE_STATE_OFFLINE;
if (needs_ipv6 && ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED)
online_state = LINK_ONLINE_STATE_OFFLINE;
}
- if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_ROUTABLE) {
+ if (req.min >= LINK_OPERSTATE_ROUTABLE) {
if (needs_ipv4 && ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE)
online_state = LINK_ONLINE_STATE_OFFLINE;
if (needs_ipv6 && ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE)
if (master_ifindex == link->ifindex)
master_ifindex = 0;
- if (master_ifindex == link->master_ifindex)
- return 0;
-
- if (link->master_ifindex == 0)
- log_link_debug(link, "Attached to master interface: %i", master_ifindex);
- else if (master_ifindex == 0)
- log_link_debug(link, "Detached from master interface: %i", link->master_ifindex);
- else
- log_link_debug(link, "Master interface changed: %i %s %i", link->master_ifindex,
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), master_ifindex);
-
- link_drop_from_master(link);
+ if (master_ifindex != link->master_ifindex) {
+ if (link->master_ifindex == 0)
+ log_link_debug(link, "Attached to master interface: %i", master_ifindex);
+ else if (master_ifindex == 0)
+ log_link_debug(link, "Detached from master interface: %i", link->master_ifindex);
+ else
+ log_link_debug(link, "Master interface changed: %i %s %i", link->master_ifindex,
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), master_ifindex);
- link->master_ifindex = master_ifindex;
+ link_drop_from_master(link);
+ link->master_ifindex = master_ifindex;
+ }
r = link_append_to_master(link);
if (r < 0)
link->mtu = mtu;
+ if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
+ /* The kernel resets IPv6 MTU after changing device MTU. So, we need to re-set IPv6 MTU again. */
+ r = link_set_ipv6_mtu(link, LOG_INFO);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
+ }
+
if (link->dhcp_client) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
if (r < 0)
if (link->dhcp6_client) {
r = sd_dhcp6_client_set_ifname(link->dhcp6_client, link->ifname);
if (r < 0)
- return log_link_debug_errno(link, r, "Failed to update interface name in DHCP6 client: %m");
+ return log_link_debug_errno(link, r, "Failed to update interface name in DHCPv6 client: %m");
}
if (link->ndisc) {
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_drop_or_unref);
static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
- _cleanup_free_ char *ifname = NULL, *kind = NULL, *state_file = NULL, *lease_file = NULL, *lldp_file = NULL;
+ _cleanup_free_ char *ifname = NULL, *kind = NULL, *state_file = NULL, *lease_file = NULL;
_cleanup_(link_drop_or_unrefp) Link *link = NULL;
unsigned short iftype;
int r, ifindex;
if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", ifindex) < 0)
return log_oom_debug();
-
- if (asprintf(&lldp_file, "/run/systemd/netif/lldp/%d", ifindex) < 0)
- return log_oom_debug();
}
link = new(Link, 1);
.n_ref = 1,
.state = LINK_STATE_PENDING,
.online_state = _LINK_ONLINE_STATE_INVALID,
+ .automatic_reconfigure_ratelimit = (const RateLimit) { .interval = 10 * USEC_PER_SEC, .burst = 5 },
.ifindex = ifindex,
.iftype = iftype,
.ifname = TAKE_PTR(ifname),
.state_file = TAKE_PTR(state_file),
.lease_file = TAKE_PTR(lease_file),
- .lldp_file = TAKE_PTR(lldp_file),
.n_dns = UINT_MAX,
.dns_default_route = -1,
r = netdev_set_ifindex(netdev, message);
if (r < 0) {
log_netdev_warning_errno(netdev, r, "Could not process new link message for netdev, ignoring: %m");
+ netdev_enter_failed(netdev);
return 0;
}
}