#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 "fd-util.h"
#include "fileio.h"
#include "missing_network.h"
+#include "netdev/vrf.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "stdio-util.h"
#include "string-table.h"
#include "strv.h"
+#include "sysctl-util.h"
#include "tmpfile-util.h"
+#include "udev-util.h"
#include "util.h"
#include "virt.h"
+uint32_t link_get_vrf_table(Link *link) {
+ return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN;
+}
+
+uint32_t link_get_dhcp_route_table(Link *link) {
+ /* When the interface is part of an VRF use the VRFs routing table, unless
+ * another table is explicitly specified. */
+ if (link->network->dhcp_route_table_set)
+ return link->network->dhcp_route_table;
+ return link_get_vrf_table(link);
+}
+
+uint32_t link_get_ipv6_accept_ra_route_table(Link *link) {
+ if (link->network->ipv6_accept_ra_route_table_set)
+ return link->network->ipv6_accept_ra_route_table;
+ return link_get_vrf_table(link);
+}
+
DUID* link_get_duid(Link *link) {
if (link->network->duid.type != _DUID_TYPE_INVALID)
return &link->network->duid;
if (link->network->bond)
return false;
+ if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ return false;
+
return link->network->dhcp & ADDRESS_FAMILY_IPV6;
}
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)
if (!link->network)
return false;
- if (streq_ptr(link->kind, "wireguard"))
+ if (STRPTR_IN_SET(link->kind, "vrf", "wireguard"))
return false;
if (link->network->bond)
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);
if (!link->network)
return false;
- if (streq_ptr(link->kind, "wireguard"))
+ if (STRPTR_IN_SET(link->kind, "vrf", "wireguard"))
return false;
if (link->network->bond)
return false;
+ if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ return false;
+
return link->network->link_local & ADDRESS_FAMILY_IPV6;
}
if (!socket_ipv6_is_supported())
return false;
- if (link->network->bridge || link->network->bond)
+ if (link->network->bond)
+ return false;
+
+ if (manager_sysctl_ipv6_enabled(link->manager) == 0)
return false;
/* DHCPv6 client will not be started if no IPv6 link-local address is configured. */
if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
return false;
+ if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ return false;
+
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
}
}
static int link_enable_ipv6(Link *link) {
- const char *p = NULL;
bool disabled;
int r;
disabled = !link_ipv6_enabled(link);
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6");
-
- r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ 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);
return 0;
}
-void link_update_operstate(Link *link, bool also_update_bond_master) {
+static bool link_is_enslaved(Link *link) {
+ if (link->flags & IFF_SLAVE)
+ /* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */
+ return true;
+
+ if (!link->enslaved_raw)
+ return false;
+
+ if (!link->network)
+ return false;
+
+ if (link->network->bridge)
+ /* TODO: support the case when link is not managed by networkd. */
+ return true;
+
+ return false;
+}
+
+static void link_update_master_operstate(Link *link, NetDev *netdev) {
+ Link *master;
+
+ if (!netdev)
+ return;
+
+ if (link_get(link->manager, netdev->ifindex, &master) < 0)
+ return;
+
+ link_update_operstate(master, true);
+}
+
+void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
+ Iterator i;
assert(link);
else if (link_has_carrier(link)) {
Address *address;
uint8_t scope = RT_SCOPE_NOWHERE;
- Iterator i;
/* if we have carrier, check what addresses we have */
SET_FOREACH(address, link->addresses, i) {
operstate = LINK_OPERSTATE_OFF;
if (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) &&
- link->flags & IFF_SLAVE)
+ link_is_enslaved(link))
operstate = LINK_OPERSTATE_ENSLAVED;
- if (IN_SET(operstate, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE) &&
- !hashmap_isempty(link->bond_slaves)) {
- Iterator i;
+ if (operstate >= LINK_OPERSTATE_CARRIER) {
Link *slave;
- HASHMAP_FOREACH(slave, link->bond_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;
}
}
link_dirty(link);
}
- if (also_update_bond_master && link->network && link->network->bond) {
- Link *master;
-
- if (link_get(link->manager, link->network->bond->ifindex, &master) < 0)
- return;
-
- link_update_operstate(master, true);
+ if (also_update_master && link->network) {
+ link_update_master_operstate(link, link->network->bond);
+ link_update_master_operstate(link, link->network->bridge);
}
}
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) {
- Address *address;
Link *carrier;
+ Address *address;
Route *route;
Iterator i;
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);
hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
hashmap_free(link->bound_by_links);
- hashmap_free(link->bond_slaves);
+ set_free_with_destructor(link->slaves, link_unref);
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);
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->network->bridge) {
+ 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_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))
- return;
+ 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)
- return;
- }
+ if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
+ return;
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
if (link->network->dhcp_use_dns && link->dhcp_lease) {
const struct in_addr *da = NULL;
- int n;
+ int j, n;
n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
if (n > 0) {
if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
return log_oom();
- memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr));
- n_addresses += n;
+ for (j = 0; j < n; j++)
+ if (in4_addr_is_non_local(&da[j]))
+ addresses[n_addresses++] = da[j];
}
}
if (link->network->dhcp_use_ntp && link->dhcp_lease) {
const struct in_addr *da = NULL;
- int n;
+ int j, n;
n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
if (n > 0) {
if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
return log_oom();
- memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr));
- n_addresses += n;
+ for (j = 0; j < n; j++)
+ if (in4_addr_is_non_local(&da[j]))
+ addresses[n_addresses++] = da[j];
}
}
}
static int link_set_proxy_arp(Link *link) {
- const char *p = NULL;
int r;
if (!link_proxy_arp_enabled(link))
return 0;
- p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp");
-
- r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m");
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);
return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
/* If IPv6 not configured (no static IPv6 address and IPv6LL autoconfiguration is disabled)
- * for this interface, or if it is a bridge slave, then disable IPv6 else enable it. */
+ * for this interface, then disable IPv6 else enable it. */
(void) link_enable_ipv6(link);
/* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
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->cost != 0) {
r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost);
if (r < 0)
return r;
}
-static int link_append_bond_slave(Link *link) {
- Link *master;
- int r;
-
- assert(link);
- assert(link->network);
- assert(link->network->bond);
-
- r = link_get(link->manager, link->network->bond->ifindex, &master);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&master->bond_slaves, NULL);
- if (r < 0)
- return r;
-
- r = hashmap_put(master->bond_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;
int r;
assert(link);
- assert(link->network);
assert(link->manager);
assert(link->manager->event);
if (!link_ipv6ll_enabled(link))
ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
- else {
- const char *p = NULL;
- _cleanup_free_ char *stable_secret = NULL;
-
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/stable_secret");
-
+ 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,
- * then reading the file fails and EIO is returned. */
- r = read_one_line_file(p, &stable_secret);
- if (r < 0)
- ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
- else
- ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
- }
+ * 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_CAN_RESTART_MS attribute: %m");
}
+ if (link->network->can_triple_sampling >= 0) {
+ struct can_ctrlmode cm = {
+ .mask = CAN_CTRLMODE_3_SAMPLES,
+ .flags = link->network->can_triple_sampling ? CAN_CTRLMODE_3_SAMPLES : 0,
+ };
+
+ log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis");
+
+ r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE 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");
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) {
r = link_set_bridge(link);
if (r < 0)
log_link_error_errno(link, r, "Could not set bridge message: %m");
+
+ r = link_append_to_master(link, link->network->bridge);
+ if (r < 0)
+ log_link_error_errno(link, r, "Failed to add to bridge master's slave list: %m");
}
if (link->network->bond) {
if (r < 0)
log_link_error_errno(link, r, "Could not set bond message: %m");
- r = link_append_bond_slave(link);
+ r = link_append_to_master(link, link->network->bond);
if (r < 0)
log_link_error_errno(link, r, "Failed to add to bond master's slave list: %m");
}
assert(link);
assert(link->network);
+ assert(link->enslaving > 0);
+ assert(!link->enslaved_raw);
link->enslaving--;
} else
log_link_debug(link, "Joined netdev");
- if (link->enslaving <= 0)
+ if (link->enslaving == 0) {
+ link->enslaved_raw = true;
link_joined(link);
+ }
return 1;
}
assert(link);
assert(link->network);
- assert(link->state == LINK_STATE_PENDING);
+ assert(link->state == LINK_STATE_INITIALIZED);
link_set_state(link, LINK_STATE_CONFIGURING);
link_dirty(link);
-
- if (!link->network->bridge &&
- !link->network->bond &&
- !link->network->vrf &&
- hashmap_isempty(link->network->stacked_netdevs))
- return link_joined(link);
+ link->enslaving = 0;
+ link->enslaved_raw = false;
if (link->network->bond) {
if (link->network->bond->state == NETDEV_STATE_READY &&
LOG_NETDEV_INTERFACE(link->network->bond),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bond->ifname));
+ link->enslaving++;
+
r = netdev_join(link->network->bond, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
link_enter_failed(link);
return r;
}
-
- link->enslaving++;
}
if (link->network->bridge) {
LOG_NETDEV_INTERFACE(link->network->bridge),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bridge->ifname));
+ link->enslaving++;
+
r = netdev_join(link->network->bridge, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
link_enter_failed(link);
return r;
}
-
- link->enslaving++;
}
if (link->network->vrf) {
LOG_NETDEV_INTERFACE(link->network->vrf),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->vrf->ifname));
+ link->enslaving++;
+
r = netdev_join(link->network->vrf, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
link_enter_failed(link);
return r;
}
-
- link->enslaving++;
}
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),
LOG_NETDEV_INTERFACE(netdev),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname));
+ link->enslaving++;
+
r = netdev_join(netdev, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
link_enter_failed(link);
return r;
}
-
- link->enslaving++;
}
+ if (link->enslaving == 0)
+ return link_joined(link);
+
return 0;
}
* primarily to keep IPv4 and IPv6 packet forwarding behaviour
* somewhat in sync (see below). */
- r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
if (r < 0)
log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
* 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 | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
}
static int link_set_ipv6_privacy_extensions(Link *link) {
- char buf[DECIMAL_STR_MAX(unsigned) + 1];
IPv6PrivacyExtensions s;
- const char *p = NULL;
int r;
s = link_ipv6_privacy_extensions(link);
if (s < 0)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr");
- xsprintf(buf, "%u", (unsigned) link->network->ipv6_privacy_extensions);
-
- r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m");
}
static int link_set_ipv6_accept_ra(Link *link) {
- const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (!link->network)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
-
- /* We handle router advertisements ourselves, tell the kernel to GTFO */
- r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
if (r < 0)
log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m");
}
static int link_set_ipv6_dad_transmits(Link *link) {
- char buf[DECIMAL_STR_MAX(int) + 1];
- const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (link->network->ipv6_dad_transmits < 0)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits");
- xsprintf(buf, "%i", link->network->ipv6_dad_transmits);
-
- r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m");
}
static int link_set_ipv6_hop_limit(Link *link) {
- char buf[DECIMAL_STR_MAX(int) + 1];
- const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (link->network->ipv6_hop_limit < 0)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit");
- xsprintf(buf, "%i", link->network->ipv6_hop_limit);
-
- r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m");
}
static int link_set_ipv6_mtu(Link *link) {
- char buf[DECIMAL_STR_MAX(unsigned) + 1];
- const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
if (link->network->ipv6_mtu == 0)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/mtu");
-
- xsprintf(buf, "%" PRIu32, link->network->ipv6_mtu);
-
- r = write_string_file(p, buf, WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m");
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;
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(link);
- if (link->network->ignore_carrier_loss)
+ if (link->network && link->network->ignore_carrier_loss)
return 0;
/* Some devices reset itself while setting the MTU. This causes the DHCP client fall into a loop.
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)
const struct in_addr *addresses;
r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
- if (r > 0) {
- if (space)
- fputc(' ', f);
- serialize_in_addrs(f, addresses, r);
- space = true;
- }
+ if (r > 0)
+ if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0)
+ space = true;
}
if (link->network->dhcp_use_dns && dhcp6_lease) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
- if (r > 0) {
- if (space)
- fputc(' ', f);
- serialize_in_addrs(f, addresses, r);
- space = true;
- }
+ if (r > 0)
+ if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0)
+ space = true;
}
if (link->network->dhcp_use_ntp && dhcp6_lease) {
(void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
}
- fputs("DOMAINS=", f);
- space = false;
- fputstrv(f, link->network->search_domains, NULL, &space);
+ ordered_set_print(f, "DOMAINS=", link->network->search_domains);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
NDiscDNSSL *dd;
fputc('\n', f);
- fputs("ROUTE_DOMAINS=", f);
- space = false;
- fputstrv(f, link->network->route_domains, NULL, &space);
+ ordered_set_print(f, "ROUTE_DOMAINS=", link->network->route_domains);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
NDiscDNSSL *dd;
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r >= 0) {
fputs("DHCP4_ADDRESS=", f);
- serialize_in_addrs(f, &address, 1);
+ serialize_in_addrs(f, &address, 1, false, NULL);
fputc('\n', f);
}
"DHCP_LEASE=%s\n",
link->lease_file);
} else
- unlink(link->lease_file);
+ (void) unlink(link->lease_file);
if (link->ipv4ll) {
struct in_addr address;
r = sd_ipv4ll_get_address(link->ipv4ll, &address);
if (r >= 0) {
fputs("IPV4LL_ADDRESS=", f);
- serialize_in_addrs(f, &address, 1);
+ serialize_in_addrs(f, &address, 1, false, NULL);
fputc('\n', f);
}
}
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);