From 0c9ee5d5e88beb548cbe703a44fc82ce77755ab6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 May 2021 12:47:28 +0900 Subject: [PATCH] network: move functions This changes no behavior. Preparation for later commits. --- src/network/networkd-link.c | 946 ++++++++++++++++++------------------ 1 file changed, 471 insertions(+), 475 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 4694ddd9fb..837577c48e 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -162,279 +162,6 @@ bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) { return true; } -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->network) - return false; - - if (link->master_ifindex > 0 && link->network->bridge) - return true; - - /* TODO: add conditions for other netdevs. */ - - 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); - - if (link->kernel_operstate == IF_OPER_DORMANT) - carrier_state = LINK_CARRIER_STATE_DORMANT; - else if (link_has_carrier(link)) { - if (link_is_enslaved(link)) - carrier_state = LINK_CARRIER_STATE_ENSLAVED; - else - carrier_state = LINK_CARRIER_STATE_CARRIER; - } else if (link->flags & IFF_UP) - carrier_state = LINK_CARRIER_STATE_NO_CARRIER; - else - carrier_state = LINK_CARRIER_STATE_OFF; - - if (carrier_state >= LINK_CARRIER_STATE_CARRIER) { - Link *slave; - - SET_FOREACH(slave, link->slaves) { - link_update_operstate(slave, false); - - if (slave->carrier_state < LINK_CARRIER_STATE_CARRIER) - carrier_state = LINK_CARRIER_STATE_DEGRADED_CARRIER; - } - } - - 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); - } - - /* for operstate we also take foreign addresses into account */ - SET_FOREACH(address, link->addresses_foreign) { - 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)); - - /* Mapping of address and carrier state vs operational state - * carrier state - * | off | no-carrier | dormant | degraded-carrier | carrier | enslaved - * ------------------------------------------------------------------------------ - * off | off | no-carrier | dormant | degraded-carrier | carrier | enslaved - * address_state degraded | off | no-carrier | dormant | degraded-carrier | degraded | enslaved - * routable | off | no-carrier | dormant | degraded-carrier | routable | routable - */ - - if (carrier_state < LINK_CARRIER_STATE_CARRIER || address_state == LINK_ADDRESS_STATE_OFF) - operstate = (LinkOperationalState) carrier_state; - else if (address_state == LINK_ADDRESS_STATE_ROUTABLE) - operstate = LINK_OPERSTATE_ROUTABLE; - else if (carrier_state == LINK_CARRIER_STATE_CARRIER) - operstate = LINK_OPERSTATE_DEGRADED; - else - operstate = LINK_OPERSTATE_ENSLAVED; - - /* 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) - online_state = LINK_ONLINE_STATE_OFFLINE; - else { - AddressFamily required_family = link->network->required_family_for_online; - bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4; - bool needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6; - - /* The operational state is within the range required for online. - * If a particular address family is also required, we might revert - * to offline in the blocks below. - */ - online_state = LINK_ONLINE_STATE_ONLINE; - - if (link->network->required_operstate_for_online.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 (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) - online_state = LINK_ONLINE_STATE_OFFLINE; - } - } - - if (link->carrier_state != carrier_state) { - link->carrier_state = carrier_state; - changed = true; - if (strv_extend(&p, "CarrierState") < 0) - log_oom(); - } - - if (link->address_state != address_state) { - link->address_state = address_state; - changed = true; - if (strv_extend(&p, "AddressState") < 0) - log_oom(); - } - - if (link->ipv4_address_state != ipv4_address_state) { - link->ipv4_address_state = ipv4_address_state; - changed = true; - if (strv_extend(&p, "IPv4AddressState") < 0) - log_oom(); - } - - if (link->ipv6_address_state != ipv6_address_state) { - link->ipv6_address_state = ipv6_address_state; - changed = true; - if (strv_extend(&p, "IPv6AddressState") < 0) - log_oom(); - } - - if (link->operstate != operstate) { - link->operstate = operstate; - changed = true; - if (strv_extend(&p, "OperationalState") < 0) - log_oom(); - } - - if (link->online_state != online_state) { - link->online_state = online_state; - changed = true; - if (strv_extend(&p, "OnlineState") < 0) - log_oom(); - } - - if (p) - link_send_changed_strv(link, p); - if (changed) - link_dirty(link); - - if (also_update_master) { - Link *master; - - if (link_get_master(link, &master) >= 0) - link_update_operstate(master, true); - } -} - -#define FLAG_STRING(string, flag, old, new) \ - (((old ^ new) & flag) \ - ? ((old & flag) ? (" -" string) : (" +" string)) \ - : "") - -static int link_update_flags(Link *link, sd_netlink_message *m, bool force_update_operstate) { - unsigned flags, unknown_flags_added, unknown_flags_removed, unknown_flags; - uint8_t operstate; - int r; - - assert(link); - - r = sd_rtnl_message_link_get_flags(m, &flags); - if (r < 0) - return log_link_warning_errno(link, r, "Could not get link flags: %m"); - - r = sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &operstate); - if (r < 0) - /* if we got a message without operstate, take it to mean - the state was unchanged */ - operstate = link->kernel_operstate; - - if (!force_update_operstate && (link->flags == flags) && (link->kernel_operstate == operstate)) - return 0; - - if (link->flags != flags) { - log_link_debug(link, "Flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", - FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags), - FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags), - FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags), - FLAG_STRING("UP", IFF_UP, link->flags, flags), - FLAG_STRING("DORMANT", IFF_DORMANT, link->flags, flags), - FLAG_STRING("LOWER_UP", IFF_LOWER_UP, link->flags, flags), - FLAG_STRING("RUNNING", IFF_RUNNING, link->flags, flags), - FLAG_STRING("MULTICAST", IFF_MULTICAST, link->flags, flags), - FLAG_STRING("BROADCAST", IFF_BROADCAST, link->flags, flags), - FLAG_STRING("POINTOPOINT", IFF_POINTOPOINT, link->flags, flags), - FLAG_STRING("PROMISC", IFF_PROMISC, link->flags, flags), - FLAG_STRING("ALLMULTI", IFF_ALLMULTI, link->flags, flags), - FLAG_STRING("PORTSEL", IFF_PORTSEL, link->flags, flags), - FLAG_STRING("AUTOMEDIA", IFF_AUTOMEDIA, link->flags, flags), - FLAG_STRING("DYNAMIC", IFF_DYNAMIC, link->flags, flags), - FLAG_STRING("NOARP", IFF_NOARP, link->flags, flags), - FLAG_STRING("NOTRAILERS", IFF_NOTRAILERS, link->flags, flags), - FLAG_STRING("DEBUG", IFF_DEBUG, link->flags, flags), - FLAG_STRING("ECHO", IFF_ECHO, link->flags, flags)); - - unknown_flags = ~(IFF_LOOPBACK | IFF_MASTER | IFF_SLAVE | IFF_UP | - IFF_DORMANT | IFF_LOWER_UP | IFF_RUNNING | - IFF_MULTICAST | IFF_BROADCAST | IFF_POINTOPOINT | - IFF_PROMISC | IFF_ALLMULTI | IFF_PORTSEL | - IFF_AUTOMEDIA | IFF_DYNAMIC | IFF_NOARP | - IFF_NOTRAILERS | IFF_DEBUG | IFF_ECHO); - unknown_flags_added = ((link->flags ^ flags) & flags & unknown_flags); - unknown_flags_removed = ((link->flags ^ flags) & link->flags & unknown_flags); - - /* link flags are currently at most 18 bits, let's align to - * printing 20 */ - if (unknown_flags_added) - log_link_debug(link, - "Unknown link flags gained: %#.5x (ignoring)", - unknown_flags_added); - - if (unknown_flags_removed) - log_link_debug(link, - "Unknown link flags lost: %#.5x (ignoring)", - unknown_flags_removed); - } - - link->flags = flags; - link->kernel_operstate = operstate; - - link_update_operstate(link, true); - - return 0; -} - void link_ntp_settings_clear(Link *link) { link->ntp = strv_free(link->ntp); } @@ -1006,19 +733,25 @@ static int link_acquire_dynamic_conf(Link *link) { return 0; } -bool link_has_carrier(Link *link) { - /* see Documentation/networking/operstates.txt in the kernel sources */ +int link_ipv6ll_gained(Link *link, const struct in6_addr *address) { + int r; - if (link->kernel_operstate == IF_OPER_UP) - return true; + assert(link); - if (link->kernel_operstate == IF_OPER_UNKNOWN) - /* operstate may not be implemented, so fall back to flags */ - if (FLAGS_SET(link->flags, IFF_LOWER_UP | IFF_RUNNING) && - !FLAGS_SET(link->flags, IFF_DORMANT)) - return true; + log_link_info(link, "Gained IPv6LL"); - return false; + link->ipv6ll_address = *address; + link_check_ready(link); + + if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) { + r = link_acquire_dynamic_ipv6_conf(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; } static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1364,7 +1097,6 @@ static void link_drop_requests(Link *link) { request_drop(req); } - static Link *link_drop(Link *link) { char **n; @@ -2100,139 +1832,18 @@ static int link_initialized(Link *link, sd_device *device) { if (r < 0) return r; - r = netlink_call_async(link->manager->rtnl, NULL, req, link_initialized_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return r; - - link_ref(link); - - return 0; -} - -static Link *link_drop_or_unref(Link *link) { - if (!link) - return NULL; - if (!link->manager) - return link_unref(link); - return link_drop(link); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_drop_or_unref); - -static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { - _cleanup_(link_drop_or_unrefp) Link *link = NULL; - _cleanup_free_ char *ifname = NULL, *kind = NULL; - unsigned short iftype; - int r, ifindex; - uint16_t type; - - assert(manager); - assert(message); - assert(ret); - - r = sd_netlink_message_get_type(message, &type); - if (r < 0) - return r; - else if (type != RTM_NEWLINK) - return -EINVAL; - - r = sd_rtnl_message_link_get_ifindex(message, &ifindex); - if (r < 0) - return r; - else if (ifindex <= 0) - return -EINVAL; - - r = sd_rtnl_message_link_get_type(message, &iftype); - if (r < 0) - return r; - - r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, &ifname); - if (r < 0) - return r; - - /* check for link kind */ - r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); - if (r >= 0) { - (void) sd_netlink_message_read_string_strdup(message, IFLA_INFO_KIND, &kind); - r = sd_netlink_message_exit_container(message); - if (r < 0) - return r; - } - - link = new(Link, 1); - if (!link) - return -ENOMEM; - - *link = (Link) { - .n_ref = 1, - .state = LINK_STATE_PENDING, - .online_state = _LINK_ONLINE_STATE_INVALID, - .ifindex = ifindex, - .iftype = iftype, - .ifname = TAKE_PTR(ifname), - .kind = TAKE_PTR(kind), - - .n_dns = UINT_MAX, - .dns_default_route = -1, - .llmnr = _RESOLVE_SUPPORT_INVALID, - .mdns = _RESOLVE_SUPPORT_INVALID, - .dnssec_mode = _DNSSEC_MODE_INVALID, - .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID, - }; - - r = hashmap_ensure_put(&manager->links, NULL, INT_TO_PTR(link->ifindex), link); - if (r < 0) - return r; - - link->manager = manager; - - r = sd_netlink_message_read_u32(message, IFLA_MASTER, (uint32_t*) &link->master_ifindex); - if (r < 0) - log_link_debug_errno(link, r, "New device has no master, continuing without"); - - r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &link->hw_addr); - if (r < 0) - log_link_debug_errno(link, r, "Hardware address not found for new device, continuing without"); - - r = netlink_message_read_hw_addr(message, IFLA_BROADCAST, &link->bcast_addr); - if (r < 0) - log_link_debug_errno(link, r, "Broadcast address not found for new device, continuing without"); - - r = ethtool_get_permanent_macaddr(&manager->ethtool_fd, link->ifname, &link->permanent_mac); - if (r < 0) - log_link_debug_errno(link, r, "Permanent MAC address not found for new device, continuing without: %m"); - - r = ethtool_get_driver(&manager->ethtool_fd, link->ifname, &link->driver); - if (r < 0) - log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m"); - - if (asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex) < 0) - return -ENOMEM; - - if (asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", link->ifindex) < 0) - return -ENOMEM; - - if (asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", link->ifindex) < 0) - return -ENOMEM; - - r = hashmap_ensure_put(&manager->links_by_name, &string_hash_ops, link->ifname, link); - if (r < 0) - return r; - - r = link_update_alternative_names(link, message); - if (r < 0) - return r; - - r = link_update_flags(link, message, false); + r = netlink_call_async(link->manager->rtnl, NULL, req, link_initialized_handler, + link_netlink_destroy_callback, link); if (r < 0) return r; - *ret = TAKE_PTR(link); + link_ref(link); return 0; } +static int link_new(Manager *m, sd_netlink_message *message, Link **ret); + static int link_add(Manager *m, sd_netlink_message *message, Link **ret) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; char ifindex_str[2 + DECIMAL_STR_MAX(int)]; @@ -2297,27 +1908,6 @@ failed: return r; } -int link_ipv6ll_gained(Link *link, const struct in6_addr *address) { - int r; - - assert(link); - - log_link_info(link, "Gained IPv6LL"); - - link->ipv6ll_address = *address; - link_check_ready(link); - - if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) { - r = link_acquire_dynamic_ipv6_conf(link); - if (r < 0) { - link_enter_failed(link); - return r; - } - } - - return 0; -} - int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) { sd_device_action_t action; Manager *m = userdata; @@ -2431,72 +2021,355 @@ static int link_carrier_lost(Link *link) { return r; } - r = link_handle_bound_by_list(link); - if (r < 0) - return r; + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + + return 0; +} + +int link_carrier_reset(Link *link) { + int r; + + assert(link); + + if (link_has_carrier(link)) { + r = link_carrier_lost(link); + if (r < 0) + return r; + + r = link_carrier_gained(link); + if (r < 0) + return r; + + log_link_info(link, "Reset carrier"); + } + + return 0; +} + +static int link_admin_state_up(Link *link) { + int r; + + assert(link); + + /* This is called every time an interface admin state changes to up; + * specifically, when IFF_UP flag changes from unset to set. */ + + if (!link->network) + return 0; + + if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) { + log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down"); + return link_down(link, NULL); + } + + /* 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); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m"); + + return 0; +} + +static int link_admin_state_down(Link *link) { + assert(link); + + if (!link->network) + return 0; + + if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) { + if (streq_ptr(link->kind, "can") && !link->can_configured) + /* CAN device needs to be down on configure. */ + return 0; + + log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up"); + return link_up(link); + } + + return 0; +} + +bool link_has_carrier(Link *link) { + /* see Documentation/networking/operstates.txt in the kernel sources */ + + if (link->kernel_operstate == IF_OPER_UP) + return true; + + if (link->kernel_operstate == IF_OPER_UNKNOWN) + /* operstate may not be implemented, so fall back to flags */ + if (FLAGS_SET(link->flags, IFF_LOWER_UP | IFF_RUNNING) && + !FLAGS_SET(link->flags, IFF_DORMANT)) + return true; + + return false; +} + +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->network) + return false; + + if (link->master_ifindex > 0 && link->network->bridge) + return true; + + /* TODO: add conditions for other netdevs. */ + + 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); + + if (link->kernel_operstate == IF_OPER_DORMANT) + carrier_state = LINK_CARRIER_STATE_DORMANT; + else if (link_has_carrier(link)) { + if (link_is_enslaved(link)) + carrier_state = LINK_CARRIER_STATE_ENSLAVED; + else + carrier_state = LINK_CARRIER_STATE_CARRIER; + } else if (link->flags & IFF_UP) + carrier_state = LINK_CARRIER_STATE_NO_CARRIER; + else + carrier_state = LINK_CARRIER_STATE_OFF; + + if (carrier_state >= LINK_CARRIER_STATE_CARRIER) { + Link *slave; + + SET_FOREACH(slave, link->slaves) { + link_update_operstate(slave, false); + + if (slave->carrier_state < LINK_CARRIER_STATE_CARRIER) + carrier_state = LINK_CARRIER_STATE_DEGRADED_CARRIER; + } + } + + 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); + } + + /* for operstate we also take foreign addresses into account */ + SET_FOREACH(address, link->addresses_foreign) { + 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)); + + /* Mapping of address and carrier state vs operational state + * carrier state + * | off | no-carrier | dormant | degraded-carrier | carrier | enslaved + * ------------------------------------------------------------------------------ + * off | off | no-carrier | dormant | degraded-carrier | carrier | enslaved + * address_state degraded | off | no-carrier | dormant | degraded-carrier | degraded | enslaved + * routable | off | no-carrier | dormant | degraded-carrier | routable | routable + */ + + if (carrier_state < LINK_CARRIER_STATE_CARRIER || address_state == LINK_ADDRESS_STATE_OFF) + operstate = (LinkOperationalState) carrier_state; + else if (address_state == LINK_ADDRESS_STATE_ROUTABLE) + operstate = LINK_OPERSTATE_ROUTABLE; + else if (carrier_state == LINK_CARRIER_STATE_CARRIER) + operstate = LINK_OPERSTATE_DEGRADED; + else + operstate = LINK_OPERSTATE_ENSLAVED; + + /* 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) + online_state = LINK_ONLINE_STATE_OFFLINE; + else { + AddressFamily required_family = link->network->required_family_for_online; + bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4; + bool needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6; + + /* The operational state is within the range required for online. + * If a particular address family is also required, we might revert + * to offline in the blocks below. */ + online_state = LINK_ONLINE_STATE_ONLINE; + + if (link->network->required_operstate_for_online.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 (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) + online_state = LINK_ONLINE_STATE_OFFLINE; + } + } + + if (link->carrier_state != carrier_state) { + link->carrier_state = carrier_state; + changed = true; + if (strv_extend(&p, "CarrierState") < 0) + log_oom(); + } + + if (link->address_state != address_state) { + link->address_state = address_state; + changed = true; + if (strv_extend(&p, "AddressState") < 0) + log_oom(); + } - return 0; -} + if (link->ipv4_address_state != ipv4_address_state) { + link->ipv4_address_state = ipv4_address_state; + changed = true; + if (strv_extend(&p, "IPv4AddressState") < 0) + log_oom(); + } -int link_carrier_reset(Link *link) { - int r; + if (link->ipv6_address_state != ipv6_address_state) { + link->ipv6_address_state = ipv6_address_state; + changed = true; + if (strv_extend(&p, "IPv6AddressState") < 0) + log_oom(); + } - assert(link); + if (link->operstate != operstate) { + link->operstate = operstate; + changed = true; + if (strv_extend(&p, "OperationalState") < 0) + log_oom(); + } - if (link_has_carrier(link)) { - r = link_carrier_lost(link); - if (r < 0) - return r; + if (link->online_state != online_state) { + link->online_state = online_state; + changed = true; + if (strv_extend(&p, "OnlineState") < 0) + log_oom(); + } - r = link_carrier_gained(link); - if (r < 0) - return r; + if (p) + link_send_changed_strv(link, p); + if (changed) + link_dirty(link); - log_link_info(link, "Reset carrier"); - } + if (also_update_master) { + Link *master; - return 0; + if (link_get_master(link, &master) >= 0) + link_update_operstate(master, true); + } } -static int link_admin_state_up(Link *link) { +#define FLAG_STRING(string, flag, old, new) \ + (((old ^ new) & flag) \ + ? ((old & flag) ? (" -" string) : (" +" string)) \ + : "") + +static int link_update_flags(Link *link, sd_netlink_message *m, bool force_update_operstate) { + uint8_t operstate; + unsigned flags; int r; assert(link); - /* This is called every time an interface admin state changes to up; - * specifically, when IFF_UP flag changes from unset to set. */ + r = sd_rtnl_message_link_get_flags(m, &flags); + if (r < 0) + return log_link_warning_errno(link, r, "Could not get link flags: %m"); - if (!link->network) + r = sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &operstate); + if (r < 0) + /* if we got a message without operstate, take it to mean + the state was unchanged */ + operstate = link->kernel_operstate; + + if (!force_update_operstate && (link->flags == flags) && (link->kernel_operstate == operstate)) return 0; - if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) { - log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down"); - return link_down(link, NULL); - } + if (link->flags != flags) { + unsigned unknown_flags, unknown_flags_added, unknown_flags_removed; - /* 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); - if (r < 0) - log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m"); + log_link_debug(link, "Flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags), + FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags), + FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags), + FLAG_STRING("UP", IFF_UP, link->flags, flags), + FLAG_STRING("DORMANT", IFF_DORMANT, link->flags, flags), + FLAG_STRING("LOWER_UP", IFF_LOWER_UP, link->flags, flags), + FLAG_STRING("RUNNING", IFF_RUNNING, link->flags, flags), + FLAG_STRING("MULTICAST", IFF_MULTICAST, link->flags, flags), + FLAG_STRING("BROADCAST", IFF_BROADCAST, link->flags, flags), + FLAG_STRING("POINTOPOINT", IFF_POINTOPOINT, link->flags, flags), + FLAG_STRING("PROMISC", IFF_PROMISC, link->flags, flags), + FLAG_STRING("ALLMULTI", IFF_ALLMULTI, link->flags, flags), + FLAG_STRING("PORTSEL", IFF_PORTSEL, link->flags, flags), + FLAG_STRING("AUTOMEDIA", IFF_AUTOMEDIA, link->flags, flags), + FLAG_STRING("DYNAMIC", IFF_DYNAMIC, link->flags, flags), + FLAG_STRING("NOARP", IFF_NOARP, link->flags, flags), + FLAG_STRING("NOTRAILERS", IFF_NOTRAILERS, link->flags, flags), + FLAG_STRING("DEBUG", IFF_DEBUG, link->flags, flags), + FLAG_STRING("ECHO", IFF_ECHO, link->flags, flags)); - return 0; -} + unknown_flags = ~(IFF_LOOPBACK | IFF_MASTER | IFF_SLAVE | IFF_UP | + IFF_DORMANT | IFF_LOWER_UP | IFF_RUNNING | + IFF_MULTICAST | IFF_BROADCAST | IFF_POINTOPOINT | + IFF_PROMISC | IFF_ALLMULTI | IFF_PORTSEL | + IFF_AUTOMEDIA | IFF_DYNAMIC | IFF_NOARP | + IFF_NOTRAILERS | IFF_DEBUG | IFF_ECHO); + unknown_flags_added = ((link->flags ^ flags) & flags & unknown_flags); + unknown_flags_removed = ((link->flags ^ flags) & link->flags & unknown_flags); -static int link_admin_state_down(Link *link) { - assert(link); + if (unknown_flags_added) + log_link_debug(link, "Unknown link flags gained, ignoring: %#.5x", unknown_flags_added); - if (!link->network) - return 0; + if (unknown_flags_removed) + log_link_debug(link, "Unknown link flags lost, ignoring: %#.5x", unknown_flags_removed); + } - if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) { - if (streq_ptr(link->kind, "can") && !link->can_configured) - /* CAN device needs to be down on configure. */ - return 0; + link->flags = flags; + link->kernel_operstate = operstate; - log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up"); - return link_up(link); - } + link_update_operstate(link, true); return 0; } @@ -2650,6 +2523,129 @@ static int link_update(Link *link, sd_netlink_message *m) { return 0; } +static Link *link_drop_or_unref(Link *link) { + if (!link) + return NULL; + if (!link->manager) + return link_unref(link); + return link_drop(link); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_drop_or_unref); + +static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { + _cleanup_(link_drop_or_unrefp) Link *link = NULL; + _cleanup_free_ char *ifname = NULL, *kind = NULL; + unsigned short iftype; + int r, ifindex; + uint16_t type; + + assert(manager); + assert(message); + assert(ret); + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) + return r; + else if (type != RTM_NEWLINK) + return -EINVAL; + + r = sd_rtnl_message_link_get_ifindex(message, &ifindex); + if (r < 0) + return r; + else if (ifindex <= 0) + return -EINVAL; + + r = sd_rtnl_message_link_get_type(message, &iftype); + if (r < 0) + return r; + + r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, &ifname); + if (r < 0) + return r; + + /* check for link kind */ + r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); + if (r >= 0) { + (void) sd_netlink_message_read_string_strdup(message, IFLA_INFO_KIND, &kind); + r = sd_netlink_message_exit_container(message); + if (r < 0) + return r; + } + + link = new(Link, 1); + if (!link) + return -ENOMEM; + + *link = (Link) { + .n_ref = 1, + .state = LINK_STATE_PENDING, + .online_state = _LINK_ONLINE_STATE_INVALID, + .ifindex = ifindex, + .iftype = iftype, + .ifname = TAKE_PTR(ifname), + .kind = TAKE_PTR(kind), + + .n_dns = UINT_MAX, + .dns_default_route = -1, + .llmnr = _RESOLVE_SUPPORT_INVALID, + .mdns = _RESOLVE_SUPPORT_INVALID, + .dnssec_mode = _DNSSEC_MODE_INVALID, + .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID, + }; + + r = hashmap_ensure_put(&manager->links, NULL, INT_TO_PTR(link->ifindex), link); + if (r < 0) + return r; + + link->manager = manager; + + r = sd_netlink_message_read_u32(message, IFLA_MASTER, (uint32_t*) &link->master_ifindex); + if (r < 0) + log_link_debug_errno(link, r, "New device has no master, continuing without"); + + r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &link->hw_addr); + if (r < 0) + log_link_debug_errno(link, r, "Hardware address not found for new device, continuing without"); + + r = netlink_message_read_hw_addr(message, IFLA_BROADCAST, &link->bcast_addr); + if (r < 0) + log_link_debug_errno(link, r, "Broadcast address not found for new device, continuing without"); + + r = ethtool_get_permanent_macaddr(&manager->ethtool_fd, link->ifname, &link->permanent_mac); + if (r < 0) + log_link_debug_errno(link, r, "Permanent MAC address not found for new device, continuing without: %m"); + + r = ethtool_get_driver(&manager->ethtool_fd, link->ifname, &link->driver); + if (r < 0) + log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m"); + + if (asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex) < 0) + return -ENOMEM; + + if (asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", link->ifindex) < 0) + return -ENOMEM; + + if (asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", link->ifindex) < 0) + return -ENOMEM; + + r = hashmap_ensure_put(&manager->links_by_name, &string_hash_ops, link->ifname, link); + if (r < 0) + return r; + + r = link_update_alternative_names(link, message); + if (r < 0) + return r; + + r = link_update_flags(link, message, false); + if (r < 0) + return r; + + *ret = TAKE_PTR(link); + + return 0; +} + int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { Link *link = NULL; NetDev *netdev = NULL; -- 2.25.1