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);
}
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) {
request_drop(req);
}
-
static Link *link_drop(Link *link) {
char **n;
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)];
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;
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;
}
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;