#include "bus-util.h"
#include "device-private.h"
#include "device-util.h"
-#include "dhcp-identifier.h"
#include "dhcp-lease-internal.h"
#include "env-file.h"
#include "ethtool-util.h"
#include "networkd-address.h"
#include "networkd-bridge-fdb.h"
#include "networkd-bridge-mdb.h"
+#include "networkd-bridge-vlan.h"
#include "networkd-can.h"
#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp-server.h"
#include "networkd-nexthop.h"
#include "networkd-queue.h"
#include "networkd-radv.h"
+#include "networkd-route-util.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-setlink.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))
+ *ret = link->network->required_operstate_for_online;
+ else if (link->iftype == ARPHRD_CAN)
+ *ret = (const LinkOperationalStateRange) {
+ .min = LINK_OPERSTATE_CARRIER,
+ .max = LINK_OPERSTATE_CARRIER,
+ };
+ else
+ *ret = LINK_OPERSTATE_RANGE_DEFAULT;
+}
+
bool link_ipv6_enabled(Link *link) {
assert(link);
return false;
}
+bool link_has_ipv6_connectivity(Link *link) {
+ LinkAddressState ipv6_address_state;
+
+ assert(link);
+
+ link_get_address_states(link, NULL, &ipv6_address_state, NULL);
+
+ switch (ipv6_address_state) {
+ case LINK_ADDRESS_STATE_ROUTABLE:
+ /* If the interface has a routable IPv6 address, then we assume yes. */
+ return true;
+
+ case LINK_ADDRESS_STATE_DEGRADED:
+ /* If the interface has only degraded IPv6 address (mostly, link-local address), then let's check
+ * there is an IPv6 default gateway. */
+ return link_has_default_gateway(link, AF_INET6);
+
+ case LINK_ADDRESS_STATE_OFF:
+ /* No IPv6 address. */
+ return false;
+
+ default:
+ assert_not_reached();
+ }
+}
+
static bool link_is_ready_to_configure_one(Link *link, bool allow_unmanaged) {
assert(link);
link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx);
link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx);
+ link->ipv4acd_by_address = hashmap_free(link->ipv4acd_by_address);
+
link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll);
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
link_dns_settings_clear(link);
link->routes = set_free(link->routes);
- link->nexthops = set_free(link->nexthops);
link->neighbors = set_free(link->neighbors);
link->addresses = set_free(link->addresses);
link->qdiscs = set_free(link->qdiscs);
free(link->ssid);
free(link->previous_ssid);
free(link->driver);
- free(link->ndisc_captive_portal);
unlink_and_free(link->lease_file);
unlink_and_free(link->lldp_file);
Link *link;
assert(m);
- assert(ifindex > 0);
+
+ if (ifindex <= 0)
+ return -EINVAL;
link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
if (!link)
}
int link_stop_engines(Link *link, bool may_keep_dhcp) {
- int r = 0, k;
+ int r, ret = 0;
assert(link);
assert(link->manager);
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
if (!keep_dhcp) {
- k = sd_dhcp_client_stop(link->dhcp_client);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
+ r = sd_dhcp_client_stop(link->dhcp_client);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m"));
}
- k = sd_dhcp_server_stop(link->dhcp_server);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop DHCPv4 server: %m");
+ r = sd_dhcp_server_stop(link->dhcp_server);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 server: %m"));
- k = sd_lldp_rx_stop(link->lldp_rx);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop LLDP Rx: %m");
+ r = sd_lldp_rx_stop(link->lldp_rx);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop LLDP Rx: %m"));
- k = sd_lldp_tx_stop(link->lldp_tx);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop LLDP Tx: %m");
+ r = sd_lldp_tx_stop(link->lldp_tx);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop LLDP Tx: %m"));
- k = sd_ipv4ll_stop(link->ipv4ll);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m");
+ r = sd_ipv4ll_stop(link->ipv4ll);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"));
- k = ipv4acd_stop(link);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m");
+ r = ipv4acd_stop(link);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 ACD client: %m"));
- k = sd_dhcp6_client_stop(link->dhcp6_client);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
+ r = sd_dhcp6_client_stop(link->dhcp6_client);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"));
- k = dhcp_pd_remove(link, /* only_marked = */ false);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
+ r = dhcp_pd_remove(link, /* only_marked = */ false);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not remove DHCPv6 PD addresses and routes: %m"));
- k = ndisc_stop(link);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
+ r = ndisc_stop(link);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"));
ndisc_flush(link);
- k = sd_radv_stop(link->radv);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m");
+ r = sd_radv_stop(link->radv);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Advertisement: %m"));
- return r;
+ return ret;
}
void link_enter_failed(Link *link) {
+ int r;
+
assert(link);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
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.");
+ r = link_reconfigure(link, /* force = */ true);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+ goto stop;
+ }
+
+ 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__);
/* 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 &&
- link->dhcp_lease && dhcp4_lease_has_pd_prefix(link->dhcp_lease)) {
+ sd_dhcp_lease_has_6rd(link->dhcp_lease)) {
if (!dhcp4_ready)
return (void) log_link_debug(link, "%s(): DHCPv4 6rd prefix is assigned, but DHCPv4 protocol is not finished yet.", __func__);
if (!dhcp_pd_ready)
}
if (link_dhcp6_enabled(link) && link->network->dhcp6_use_pd_prefix &&
- link->dhcp6_lease && dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) {
+ sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) {
if (!dhcp6_ready)
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)
link_unref(set_remove(master->slaves, link));
}
-static void link_drop_requests(Link *link) {
+static int link_drop_requests(Link *link) {
Request *req;
+ int ret = 0;
assert(link);
assert(link->manager);
- ORDERED_SET_FOREACH(req, link->manager->request_queue)
- if (req->link == link)
- request_detach(link->manager, req);
+ ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+ if (req->link != link)
+ continue;
+
+ /* If the request is already called, but its reply is not received, then we need to
+ * drop the configuration (e.g. address) here. Note, if the configuration is known,
+ * it will be handled later by link_drop_foreign_addresses() or so. */
+ if (req->waiting_reply && link->state != LINK_STATE_LINGER)
+ switch (req->type) {
+ case REQUEST_TYPE_ADDRESS: {
+ Address *address = ASSERT_PTR(req->userdata);
+
+ if (address_get(link, address, NULL) < 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;
+ }
+ default:
+ ;
+ }
+
+ request_detach(req);
+ }
+
+ return ret;
}
static Link *link_drop(Link *link) {
/* Drop all references from other links and manager. Note that async netlink calls may have
* references to the link, and they will be dropped when we receive replies. */
- link_drop_requests(link);
+ (void) link_drop_requests(link);
link_free_bound_to_list(link);
link_free_bound_by_list(link);
}
static int link_drop_foreign_config(Link *link) {
- int k, r;
+ int r;
assert(link);
assert(link->manager);
r = link_drop_foreign_routes(link);
- k = link_drop_foreign_nexthops(link);
- if (k < 0 && r >= 0)
- r = k;
-
- k = link_drop_foreign_addresses(link);
- if (k < 0 && r >= 0)
- r = k;
-
- k = link_drop_foreign_neighbors(link);
- if (k < 0 && r >= 0)
- r = k;
-
- k = manager_drop_foreign_routing_policy_rules(link->manager);
- if (k < 0 && r >= 0)
- r = k;
+ RET_GATHER(r, link_drop_foreign_nexthops(link));
+ RET_GATHER(r, link_drop_foreign_addresses(link));
+ RET_GATHER(r, link_drop_foreign_neighbors(link));
+ RET_GATHER(r, manager_drop_foreign_routing_policy_rules(link->manager));
return r;
}
static int link_drop_managed_config(Link *link) {
- int k, r;
+ int r;
assert(link);
assert(link->manager);
r = link_drop_managed_routes(link);
- k = link_drop_managed_nexthops(link);
- if (k < 0 && r >= 0)
- r = k;
-
- k = link_drop_managed_addresses(link);
- if (k < 0 && r >= 0)
- r = k;
-
- k = link_drop_managed_neighbors(link);
- if (k < 0 && r >= 0)
- r = k;
-
- k = link_drop_managed_routing_policy_rules(link);
- if (k < 0 && r >= 0)
- r = k;
+ 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));
return r;
}
if (r < 0)
return r;
+ r = link_configure_mtu(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. */
r = link_request_to_set_can(link);
if (r < 0)
return r;
- r = link_configure_mtu(link);
- if (r < 0)
- return r;
-
r = link_request_to_set_addrgen_mode(link);
if (r < 0)
return r;
return 0;
if (network) {
+ _cleanup_free_ char *joined = strv_join(network->dropins, ", ");
+
if (link->state == LINK_STATE_INITIALIZED)
- log_link_info(link, "Configuring with %s.", network->filename);
+ log_link_info(link, "Configuring with %s%s%s%s.",
+ network->filename,
+ isempty(joined) ? "" : " (dropins: ",
+ joined,
+ isempty(joined) ? "" : ")");
else
- log_link_info(link, "Reconfiguring with %s.", network->filename);
+ log_link_info(link, "Reconfiguring with %s%s%s%s.",
+ network->filename,
+ isempty(joined) ? "" : " (dropins: ",
+ joined,
+ isempty(joined) ? "" : ")");
} else
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
"Unmanaging interface.");
if (r < 0)
return r;
- link_drop_requests(link);
+ r = link_drop_requests(link);
+ if (r < 0)
+ return r;
if (network && !force && network->keep_configuration != KEEP_CONFIGURATION_YES)
/* When a new/updated .network file is assigned, first make all configs (addresses,
int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action) {
int r, ifindex;
+ const char *s;
Link *link;
assert(m);
return 0;
}
+ r = sd_device_get_property_value(device, "ID_NET_MANAGED_BY", &s);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get ID_NET_MANAGED_BY udev property, ignoring: %m");
+ if (r >= 0 && !streq(s, "io.systemd.Network")) {
+ log_device_debug(device, "Interface is requested to be managed by '%s', not managing the interface.", s);
+ link_set_state(link, LINK_STATE_UNMANAGED);
+ return 0;
+ }
+
r = link_initialized(link, device);
if (r < 0)
link_enter_failed(link);
usec = 5 * USEC_PER_SEC;
else
- /* Otherwise, use the currently set value. */
+ /* Otherwise, use the implied default value. */
usec = link->network->ignore_carrier_loss_usec;
if (usec == USEC_INFINITY)
return false;
}
-static LinkAddressState address_state_from_scope(uint8_t scope) {
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- return LINK_ADDRESS_STATE_ROUTABLE;
-
- if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- return LINK_ADDRESS_STATE_DEGRADED;
-
- /* no useful addresses found */
- return LINK_ADDRESS_STATE_OFF;
-}
-
void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
LinkCarrierState carrier_state;
LinkAddressState ipv4_address_state, ipv6_address_state, address_state;
LinkOnlineState online_state;
_cleanup_strv_free_ char **p = NULL;
- uint8_t ipv4_scope = RT_SCOPE_NOWHERE, ipv6_scope = RT_SCOPE_NOWHERE;
bool changed = false;
- Address *address;
assert(link);
}
}
- SET_FOREACH(address, link->addresses) {
- if (!address_is_ready(address))
- continue;
-
- if (address->family == AF_INET)
- ipv4_scope = MIN(ipv4_scope, address->scope);
-
- if (address->family == AF_INET6)
- ipv6_scope = MIN(ipv6_scope, address->scope);
- }
-
- ipv4_address_state = address_state_from_scope(ipv4_scope);
- ipv6_address_state = address_state_from_scope(ipv6_scope);
- address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
+ link_get_address_states(link, &ipv4_address_state, &ipv6_address_state, &address_state);
/* Mapping of address and carrier state vs operational state
* carrier state
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;
bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
* 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)
if (r < 0)
return r;
+ r = link_update_bridge_vlan(link, message);
+ if (r < 0)
+ return r;
+
return needs_reconfigure;
}
.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),
.kind = TAKE_PTR(kind),
+ .bridge_vlan_pvid = UINT16_MAX,
+
.ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
.state_file = TAKE_PTR(state_file),