/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <netinet/ether.h>
+#include <netinet/in.h>
#include <linux/if.h>
-#include <linux/can/netlink.h>
#include <unistd.h>
-#include <stdio_ext.h>
#include "alloc-util.h"
#include "bus-util.h"
#include "netdev/vrf.h"
#include "netlink-util.h"
#include "network-internal.h"
+#include "networkd-can.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "strv.h"
#include "sysctl-util.h"
#include "tmpfile-util.h"
+#include "udev-util.h"
#include "util.h"
#include "virt.h"
return link->network->dhcp_server;
}
-static bool link_ipv4ll_enabled(Link *link) {
+bool link_ipv4ll_enabled(Link *link) {
assert(link);
if (link->flags & IFF_LOOPBACK)
return link->network->link_local & ADDRESS_FAMILY_IPV4;
}
+bool link_ipv4ll_fallback_enabled(Link *link) {
+ assert(link);
+
+ if (link->flags & IFF_LOOPBACK)
+ return false;
+
+ if (!link->network)
+ return false;
+
+ if (STRPTR_IN_SET(link->kind, "vrf", "wireguard"))
+ return false;
+
+ if (link->network->bond)
+ return false;
+
+ return link->network->link_local & ADDRESS_FAMILY_FALLBACK_IPV4;
+}
+
static bool link_ipv6ll_enabled(Link *link) {
assert(link);
return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE;
}
-static bool link_lldp_rx_enabled(Link *link) {
- assert(link);
-
- if (link->flags & IFF_LOOPBACK)
- return false;
-
- if (link->iftype != ARPHRD_ETHER)
- return false;
-
- if (!link->network)
- return false;
-
- /* LLDP should be handled on bridge slaves as those have a direct
- * connection to their peers not on the bridge master. Linux doesn't
- * even (by default) forward lldp packets to the bridge master.*/
- if (streq_ptr("bridge", link->kind))
- return false;
-
- return link->network->lldp_mode != LLDP_MODE_NO;
-}
-
-static bool link_lldp_emit_enabled(Link *link) {
- assert(link);
-
- if (link->flags & IFF_LOOPBACK)
- return false;
-
- if (link->iftype != ARPHRD_ETHER)
- return false;
-
- if (!link->network)
- return false;
-
- return link->network->lldp_emit != LLDP_EMIT_NO;
-}
-
static bool link_ipv4_forward_enabled(Link *link) {
assert(link);
r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", disabled);
if (r < 0)
- log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m",
- enable_disable(!disabled), link->ifname);
+ log_link_warning_errno(link, r, "Cannot %s IPv6: %m", enable_disable(!disabled));
else
log_link_info(link, "IPv6 successfully %sd", enable_disable(!disabled));
link_is_enslaved(link))
operstate = LINK_OPERSTATE_ENSLAVED;
- if (IN_SET(operstate, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE)) {
+ if (operstate >= LINK_OPERSTATE_CARRIER) {
Link *slave;
- HASHMAP_FOREACH(slave, link->slaves, i) {
+ SET_FOREACH(slave, link->slaves, i) {
link_update_operstate(slave, false);
- if (IN_SET(slave->operstate,
- LINK_OPERSTATE_OFF, LINK_OPERSTATE_NO_CARRIER, LINK_OPERSTATE_DORMANT))
- operstate = LINK_OPERSTATE_DEGRADED;
+ if (slave->operstate < LINK_OPERSTATE_CARRIER)
+ operstate = LINK_OPERSTATE_DEGRADED_CARRIER;
}
}
return 0;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
-
static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
_cleanup_(link_unrefp) Link *link = NULL;
uint16_t type;
return 0;
}
-static void link_detach_from_manager(Link *link) {
- if (!link || !link->manager)
- return;
-
- hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
- set_remove(link->manager->links_requesting_uuid, link);
- link_clean(link);
-}
-
static Link *link_free(Link *link) {
- Link *carrier, *master;
Address *address;
- Route *route;
- Iterator i;
assert(link);
- while ((route = set_first(link->routes)))
- route_free(route);
-
- while ((route = set_first(link->routes_foreign)))
- route_free(route);
-
- link->routes = set_free(link->routes);
- link->routes_foreign = set_free(link->routes_foreign);
-
- while ((address = set_first(link->addresses)))
- address_free(address);
-
- while ((address = set_first(link->addresses_foreign)))
- address_free(address);
+ link->routes = set_free_with_destructor(link->routes, route_free);
+ link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
- link->addresses = set_free(link->addresses);
- link->addresses_foreign = set_free(link->addresses_foreign);
+ link->addresses = set_free_with_destructor(link->addresses, address_free);
+ link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free);
while ((address = link->pool_addresses)) {
LIST_REMOVE(addresses, link->pool_addresses, address);
sd_ndisc_unref(link->ndisc);
sd_radv_unref(link->radv);
- link_detach_from_manager(link);
-
free(link->ifname);
-
free(link->kind);
(void) unlink(link->state_file);
sd_device_unref(link->sd_device);
- HASHMAP_FOREACH (carrier, link->bound_to_links, i)
- hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex));
hashmap_free(link->bound_to_links);
-
- HASHMAP_FOREACH (carrier, link->bound_by_links, i)
- hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
hashmap_free(link->bound_by_links);
- hashmap_free(link->slaves);
+ set_free_with_destructor(link->slaves, link_unref);
- if (link->network) {
- if (link->network->bond &&
- link_get(link->manager, link->network->bond->ifindex, &master) >= 0)
- (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex));
-
- if (link->network->bridge &&
- link_get(link->manager, link->network->bridge->ifindex, &master) >= 0)
- (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex));
- }
+ network_unref(link->network);
return mfree(link);
}
if (link->state == state)
return;
+ log_link_debug(link, "State changed: %s -> %s",
+ link_state_to_string(link->state),
+ link_state_to_string(state));
+
link->state = state;
link_send_changed(link, "AdministrativeState", NULL);
link_dirty(link);
}
-static int link_stop_clients(Link *link) {
+int link_stop_clients(Link *link) {
int r = 0, k;
assert(link);
return NULL;
}
+static int link_join_netdevs_after_configured(Link *link) {
+ NetDev *netdev;
+ Iterator i;
+ int r;
+
+ HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
+ if (netdev->ifindex > 0)
+ /* Assume already enslaved. */
+ continue;
+
+ if (netdev_get_create_type(netdev) != NETDEV_CREATE_AFTER_CONFIGURED)
+ continue;
+
+ log_struct(LOG_DEBUG,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(netdev),
+ LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname));
+
+ r = netdev_join(netdev, link, NULL);
+ if (r < 0)
+ return log_struct_errno(LOG_WARNING, r,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(netdev),
+ LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", netdev->ifname));
+ }
+
+ return 0;
+}
+
static void link_enter_configured(Link *link) {
assert(link);
assert(link->network);
link_set_state(link, LINK_STATE_CONFIGURED);
+ (void) link_join_netdevs_after_configured(link);
+
link_dirty(link);
}
return 1;
}
-static int link_request_set_routes(Link *link) {
+int link_request_set_routes(Link *link) {
enum {
PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
PHASE_GATEWAY, /* Second phase: Routes with a gateway */
if (!link->routing_policy_rules_configured)
return;
- if (link_ipv4ll_enabled(link))
- if (!link->ipv4ll_address ||
- !link->ipv4ll_route)
- return;
+ if (link_ipv4ll_enabled(link) && !(link->ipv4ll_address && link->ipv4ll_route))
+ return;
if (link_ipv6ll_enabled(link) &&
in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
return;
- if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
- !link->dhcp4_configured) ||
- (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
- !link->dhcp6_configured) ||
- (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) &&
- !link->dhcp4_configured && !link->dhcp6_configured))
+ if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
+ !(link->dhcp4_configured || link->dhcp6_configured) &&
+ !(link_ipv4ll_fallback_enabled(link) && link->ipv4ll_address && link->ipv4ll_route))
+ /* When DHCP is enabled, at least one protocol must provide an address, or
+ * an IPv4ll fallback address must be configured. */
return;
if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
log_debug("Copying NTP server information from %s", link->ifname);
STRV_FOREACH(a, link->network->ntp) {
- struct in_addr ia;
+ union in_addr_union ia;
/* Only look for IPv4 addresses */
- if (inet_pton(AF_INET, *a, &ia) <= 0)
+ if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
continue;
/* Never propagate obviously borked data */
- if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
+ if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
continue;
if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
return log_oom();
- addresses[n_addresses++] = ia;
+ addresses[n_addresses++] = ia.in;
}
if (link->network->dhcp_use_ntp && link->dhcp_lease) {
log_link_debug(link, "Setting MTU done.");
- if (link->state == LINK_STATE_PENDING)
+ if (link->state == LINK_STATE_INITIALIZED)
(void) link_configure_after_setting_mtu(link);
return 1;
}
-int link_set_mtu(Link *link, uint32_t mtu) {
+int link_set_mtu(Link *link, uint32_t mtu, bool force) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link->manager);
assert(link->manager->rtnl);
- if (link->mtu == mtu || link->setting_mtu)
+ if (mtu == 0 || link->setting_mtu)
+ return 0;
+
+ if (force ? link->mtu == mtu : link->mtu >= mtu)
return 0;
log_link_debug(link, "Setting MTU: %" PRIu32, mtu);
r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root);
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m");
-
}
if (link->network->unicast_flood >= 0) {
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m");
}
+ if (link->network->multicast_flood >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_FLOOD, link->network->multicast_flood);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_FLOOD attribute: %m");
+ }
+
if (link->network->multicast_to_unicast >= 0) {
r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_TO_UCAST, link->network->multicast_to_unicast);
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_TO_UCAST attribute: %m");
}
+ if (link->network->neighbor_suppression >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_NEIGH_SUPPRESS, link->network->neighbor_suppression);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_NEIGH_SUPPRESS attribute: %m");
+ }
+
+ if (link->network->learning >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LEARNING, link->network->learning);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_LEARNING attribute: %m");
+ }
+
+ if (link->network->bridge_proxy_arp >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP, link->network->bridge_proxy_arp);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROXYARP attribute: %m");
+ }
+
+ if (link->network->bridge_proxy_arp_wifi >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP_WIFI, link->network->bridge_proxy_arp_wifi);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROXYARP_WIFI attribute: %m");
+ }
+
if (link->network->cost != 0) {
r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost);
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m");
}
+ if (link->network->multicast_router != _MULTICAST_ROUTER_INVALID) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MULTICAST_ROUTER, link->network->multicast_router);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MULTICAST_ROUTER attribute: %m");
+ }
+
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m");
return r;
}
-static int link_append_to_master(Link *link, NetDev *netdev) {
- Link *master;
- int r;
-
- assert(link);
- assert(netdev);
-
- r = link_get(link->manager, netdev->ifindex, &master);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&master->slaves, NULL);
- if (r < 0)
- return r;
-
- r = hashmap_put(master->slaves, INT_TO_PTR(link->ifindex), link);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int link_lldp_save(Link *link) {
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- sd_lldp_neighbor **l = NULL;
- int n = 0, r, i;
-
- assert(link);
- assert(link->lldp_file);
-
- if (!link->lldp) {
- (void) unlink(link->lldp_file);
- return 0;
- }
-
- r = sd_lldp_get_neighbors(link->lldp, &l);
- if (r < 0)
- goto finish;
- if (r == 0) {
- (void) unlink(link->lldp_file);
- goto finish;
- }
-
- n = r;
-
- r = fopen_temporary(link->lldp_file, &f, &temp_path);
- if (r < 0)
- goto finish;
-
- fchmod(fileno(f), 0644);
-
- for (i = 0; i < n; i++) {
- const void *p;
- le64_t u;
- size_t sz;
-
- r = sd_lldp_neighbor_get_raw(l[i], &p, &sz);
- if (r < 0)
- goto finish;
-
- u = htole64(sz);
- (void) fwrite(&u, 1, sizeof(u), f);
- (void) fwrite(p, 1, sz, f);
- }
-
- r = fflush_and_check(f);
- if (r < 0)
- goto finish;
-
- if (rename(temp_path, link->lldp_file) < 0) {
- r = -errno;
- goto finish;
- }
-
-finish:
- if (r < 0) {
- (void) unlink(link->lldp_file);
- if (temp_path)
- (void) unlink(temp_path);
-
- log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file);
- }
-
- if (l) {
- for (i = 0; i < n; i++)
- sd_lldp_neighbor_unref(l[i]);
- free(l);
- }
-
- return r;
-}
-
-static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
- Link *link = userdata;
- int r;
-
- assert(link);
-
- (void) link_lldp_save(link);
-
- if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) {
- /* If we received information about a new neighbor, restart the LLDP "fast" logic */
-
- log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission.");
-
- r = link_lldp_emit_start(link);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m");
- }
-}
-
static int link_acquire_ipv6_conf(Link *link) {
int r;
int r;
assert(link);
- assert(link->network);
assert(link->manager);
assert(link->manager->event);
}
if (link_ipv6_enabled(link)) {
+ uint8_t ipv6ll_mode;
+
r = sd_netlink_message_open_container(req, IFLA_AF_SPEC);
if (r < 0)
return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m");
}
+ if (!link_ipv6ll_enabled(link))
+ ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
+ else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0)
+ /* The file may not exist. And event if it exists, when stable_secret is unset,
+ * reading the file fails with EIO. */
+ ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
+ else
+ ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+
+ r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE: %m");
+
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m");
return 0;
}
-static int link_up_can(Link *link) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
- int r;
-
- assert(link);
-
- log_link_debug(link, "Bringing CAN link up");
-
- r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
-
- r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set link flags: %m");
-
- r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler,
- link_netlink_destroy_callback, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
- link_ref(link);
-
- return 0;
-}
-
-static int link_set_can(Link *link) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- int r;
-
- assert(link);
- assert(link->network);
- assert(link->manager);
- assert(link->manager->rtnl);
-
- log_link_debug(link, "link_set_can");
-
- r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to allocate netlink message: %m");
-
- r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set netlink flags: %m");
-
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
-
- if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) {
- struct can_bittiming bt = {
- .bitrate = link->network->can_bitrate,
- .sample_point = link->network->can_sample_point,
- };
-
- if (link->network->can_bitrate > UINT32_MAX) {
- log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate);
- return -ERANGE;
- }
-
- log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
- if (link->network->can_sample_point > 0)
- log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
- else
- log_link_debug(link, "Using default sample point");
-
- r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
- }
-
- if (link->network->can_restart_us > 0) {
- char time_string[FORMAT_TIMESPAN_MAX];
- uint64_t restart_ms;
-
- if (link->network->can_restart_us == USEC_INFINITY)
- restart_ms = 0;
- else
- restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
-
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC);
-
- if (restart_ms > UINT32_MAX) {
- log_link_error(link, "restart timeout (%s) too big.", time_string);
- return -ERANGE;
- }
-
- log_link_debug(link, "Setting restart = %s", time_string);
-
- r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m");
- }
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to close netlink container: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to close netlink container: %m");
-
- r = netlink_call_async(link->manager->rtnl, NULL, m, link_set_handler,
- link_netlink_destroy_callback, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
- link_ref(link);
-
- if (!(link->flags & IFF_UP)) {
- r = link_up_can(link);
- if (r < 0) {
- link_enter_failed(link);
- return r;
- }
- }
-
- log_link_debug(link, "link_set_can done");
-
- return r;
-}
-
static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
if (r < 0)
log_link_warning_errno(link, r, "Could not bring down interface: %m");
- if (streq_ptr(link->kind, "can"))
- link_set_can(link);
-
return 1;
}
-int link_down(Link *link) {
+int link_down(Link *link, link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
if (r < 0)
return log_link_error_errno(link, r, "Could not set link flags: %m");
- r = netlink_call_async(link->manager->rtnl, NULL, req, link_down_handler,
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ callback ?: link_down_handler,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
}
if (!required_up && link_is_up) {
- r = link_down(link);
+ r = link_down(link, NULL);
if (r < 0)
return r;
} else if (required_up && !link_is_up) {
return;
}
+static int link_append_to_master(Link *link, NetDev *netdev) {
+ Link *master;
+ int r;
+
+ assert(link);
+ assert(netdev);
+
+ r = link_get(link->manager, netdev->ifindex, &master);
+ if (r < 0)
+ return r;
+
+ r = set_ensure_allocated(&master->slaves, NULL);
+ if (r < 0)
+ return r;
+
+ r = set_put(master->slaves, link);
+ if (r < 0)
+ return r;
+
+ link_ref(link);
+ return 0;
+}
+
+static void link_drop_from_master(Link *link, NetDev *netdev) {
+ Link *master;
+
+ assert(link);
+
+ if (!link->manager || !netdev)
+ return;
+
+ if (link_get(link->manager, netdev->ifindex, &master) < 0)
+ return;
+
+ link_unref(set_remove(master->slaves, link));
+}
+
+static void link_detach_from_manager(Link *link) {
+ if (!link || !link->manager)
+ return;
+
+ link_unref(set_remove(link->manager->links_requesting_uuid, link));
+ link_clean(link);
+
+ /* The following must be called at last. */
+ assert_se(hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)) == link);
+ link_unref(link);
+}
+
void link_drop(Link *link) {
if (!link || link->state == LINK_STATE_LINGER)
return;
link_free_carrier_maps(link);
+ if (link->network) {
+ link_drop_from_master(link, link->network->bridge);
+ link_drop_from_master(link, link->network->bond);
+ }
+
log_link_debug(link, "Link removed");
(void) unlink(link->state_file);
-
link_detach_from_manager(link);
-
- link_unref(link);
-
- return;
}
static int link_joined(Link *link) {
assert(link);
assert(link->network);
- assert(link->state == LINK_STATE_PENDING);
+ assert(link->state == LINK_STATE_INITIALIZED);
link_set_state(link, LINK_STATE_CONFIGURING);
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
- if (netdev->ifindex > 0) {
- link_joined(link);
+ if (netdev->ifindex > 0)
+ /* Assume already enslaved. */
+ continue;
+
+ if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED)
continue;
- }
log_struct(LOG_DEBUG,
LOG_LINK_INTERFACE(link),
return 0;
}
-static int link_update_lldp(Link *link) {
- int r;
-
- assert(link);
-
- if (!link->lldp)
- return 0;
-
- if (link->flags & IFF_UP) {
- r = sd_lldp_start(link->lldp);
- if (r > 0)
- log_link_debug(link, "Started LLDP.");
- } else {
- r = sd_lldp_stop(link->lldp);
- if (r > 0)
- log_link_debug(link, "Stopped LLDP.");
- }
-
- return r;
-}
-
-static int link_configure_can(Link *link) {
- int r;
-
- if (streq_ptr(link->kind, "can")) {
- /* The CAN interface must be down to configure bitrate, etc... */
- if ((link->flags & IFF_UP)) {
- r = link_down(link);
- if (r < 0) {
- link_enter_failed(link);
- return r;
- }
-
- return 0;
- }
-
- return link_set_can(link);
- }
-
- if (!(link->flags & IFF_UP)) {
- r = link_up_can(link);
- if (r < 0) {
- link_enter_failed(link);
- return r;
- }
- }
-
- return 0;
-}
-
static int link_configure(Link *link) {
int r;
assert(link);
assert(link->network);
- assert(link->state == LINK_STATE_PENDING);
+ assert(link->state == LINK_STATE_INITIALIZED);
if (STRPTR_IN_SET(link->kind, "can", "vcan"))
return link_configure_can(link);
if (r < 0)
return r;
- if (link_ipv4ll_enabled(link)) {
+ if (link_ipv4ll_enabled(link) || link_ipv4ll_fallback_enabled(link)) {
r = ipv4ll_configure(link);
if (r < 0)
return r;
}
if (link_lldp_rx_enabled(link)) {
- r = sd_lldp_new(&link->lldp);
- if (r < 0)
- return r;
-
- r = sd_lldp_set_ifindex(link->lldp, link->ifindex);
- if (r < 0)
- return r;
-
- r = sd_lldp_match_capabilities(link->lldp,
- link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ?
- SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS :
- SD_LLDP_SYSTEM_CAPABILITIES_ALL);
- if (r < 0)
- return r;
-
- r = sd_lldp_set_filter_address(link->lldp, &link->mac);
- if (r < 0)
- return r;
-
- r = sd_lldp_attach_event(link->lldp, NULL, 0);
- if (r < 0)
- return r;
-
- r = sd_lldp_set_callback(link->lldp, lldp_handler, link);
- if (r < 0)
- return r;
-
- r = link_update_lldp(link);
+ r = link_lldp_rx_configure(link);
if (r < 0)
return r;
}
- if (link->network->mtu > 0) {
- r = link_set_mtu(link, link->network->mtu);
- if (r < 0)
- return r;
- }
+ r = link_set_mtu(link, link->network->mtu, link->network->mtu_is_set);
+ if (r < 0)
+ return r;
if (socket_ipv6_is_supported()) {
r = link_configure_addrgen_mode(link);
assert(link);
assert(link->network);
- assert(link->state == LINK_STATE_PENDING);
+ assert(link->state == LINK_STATE_INITIALIZED);
if (link->setting_mtu)
return 0;
assert(link->ifname);
assert(link->manager);
- if (link->state != LINK_STATE_PENDING)
- return 1;
+ /* We may get called either from the asynchronous netlink callback,
+ * or directly for link_add() if running in a container. See link_add(). */
+ if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
+ return 0;
log_link_debug(link, "Link state is up-to-date");
+ link_set_state(link, LINK_STATE_INITIALIZED);
r = link_new_bound_by_list(link);
if (r < 0)
&link->mac, &network);
if (r == -ENOENT) {
link_enter_unmanaged(link);
- return 1;
+ return 0;
} else if (r == 0 && network->unmanaged) {
link_enter_unmanaged(link);
return 0;
if (r < 0)
return r;
- return 1;
+ return 0;
}
static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return 0;
log_link_debug(link, "udev initialized link");
+ link_set_state(link, LINK_STATE_INITIALIZED);
link->sd_device = sd_device_ref(device);
return 0;
}
+ r = device_is_renaming(device);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to determine the device is renamed or not: %m");
+ goto failed;
+ }
+ if (r > 0) {
+ log_link_debug(link, "Interface is under renaming, pending initialization.");
+ return 0;
+ }
+
r = link_initialized(link, device);
if (r < 0)
goto failed;
link->ipv6ll_address = *address;
link_check_ready(link);
- if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+ if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
r = link_acquire_ipv6_conf(link);
if (r < 0) {
link_enter_failed(link);
assert(link);
- if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+ if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
r = link_acquire_conf(link);
if (r < 0) {
link_enter_failed(link);
assert(m);
if (link->state == LINK_STATE_LINGER) {
- log_link_info(link, "Link readded");
+ log_link_info(link, "Link re-added");
link_set_state(link, LINK_STATE_CONFIGURING);
r = link_new_carrier_maps(link);
r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname);
if (r >= 0 && !streq(ifname, link->ifname)) {
- log_link_info(link, "Interface name change detected, %s has been renamed to %s.", link->ifname, ifname);
+ Manager *manager = link->manager;
- if (link->state == LINK_STATE_PENDING) {
- r = free_and_strdup(&link->ifname, ifname);
- if (r < 0)
- return r;
- } else {
- Manager *manager = link->manager;
+ log_link_info(link, "Interface name change detected, %s has been renamed to %s.", link->ifname, ifname);
- link_drop(link);
- r = link_add(manager, m, &link);
- if (r < 0)
- return r;
- }
+ link_drop(link);
+ r = link_add(manager, m, &link);
+ if (r < 0)
+ return r;
}
r = sd_netlink_message_read_u32(m, IFLA_MTU, &mtu);
assert(link->manager);
if (link->state == LINK_STATE_LINGER) {
- unlink(link->state_file);
+ (void) unlink(link->state_file);
return 0;
}
if (r < 0)
goto fail;
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
(void) fchmod(fileno(f), 0644);
fprintf(f,
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
yes_no(link->network->required_for_online));
+ fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s\n",
+ strempty(link_operstate_to_string(link->network->required_operstate_for_online)));
+
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
if (r < 0 && r != -ENOMSG)
"DHCP_LEASE=%s\n",
link->lease_file);
} else
- unlink(link->lease_file);
+ (void) unlink(link->lease_file);
if (link->ipv4ll) {
struct in_addr address;
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
+ [LINK_STATE_INITIALIZED] = "initialized",
[LINK_STATE_CONFIGURING] = "configuring",
[LINK_STATE_CONFIGURED] = "configured",
[LINK_STATE_UNMANAGED] = "unmanaged",
};
DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState);
-
-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_ENSLAVED] = "enslaved",
- [LINK_OPERSTATE_ROUTABLE] = "routable",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState);