#include "network-internal.h"
#include "networkd-can.h"
#include "networkd-ipv6-proxy-ndp.h"
+#include "networkd-link-bus.h"
+#include "networkd-link.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
return &link->manager->duid;
}
+int link_sysctl_ipv6_enabled(Link *link) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ r = sysctl_read_ip_property(AF_INET6, link->ifname, "disable_ipv6", &value);
+ if (r < 0)
+ return log_link_warning_errno(link, r,
+ "Failed to read net.ipv6.conf.%s.disable_ipv6 sysctl property: %m",
+ link->ifname);
+
+ link->sysctl_ipv6_enabled = value[0] == '0';
+ return link->sysctl_ipv6_enabled;
+}
+
static bool link_dhcp6_enabled(Link *link) {
assert(link);
if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->dhcp & ADDRESS_FAMILY_IPV6;
if (link->network->bond)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->link_local & ADDRESS_FAMILY_IPV6;
if (link->network->bond)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
+ LinkCarrierState carrier_state;
+ LinkAddressState address_state;
+ _cleanup_strv_free_ char **p = NULL;
+ uint8_t scope = RT_SCOPE_NOWHERE;
+ bool changed = false;
+ Address *address;
Iterator i;
assert(link);
if (link->kernel_operstate == IF_OPER_DORMANT)
- operstate = LINK_OPERSTATE_DORMANT;
+ carrier_state = LINK_CARRIER_STATE_DORMANT;
else if (link_has_carrier(link)) {
- Address *address;
- uint8_t scope = RT_SCOPE_NOWHERE;
+ 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 we have carrier, check what addresses we have */
- SET_FOREACH(address, link->addresses, i) {
- if (!address_is_ready(address))
- continue;
+ if (carrier_state >= LINK_CARRIER_STATE_CARRIER) {
+ Link *slave;
+
+ SET_FOREACH(slave, link->slaves, i) {
+ link_update_operstate(slave, false);
- if (address->scope < scope)
- scope = address->scope;
+ if (slave->carrier_state < LINK_CARRIER_STATE_CARRIER)
+ carrier_state = LINK_CARRIER_STATE_DEGRADED_CARRIER;
}
+ }
- /* for operstate we also take foreign addresses into account */
- SET_FOREACH(address, link->addresses_foreign, i) {
- if (!address_is_ready(address))
- continue;
+ SET_FOREACH(address, link->addresses, i) {
+ if (!address_is_ready(address))
+ continue;
- if (address->scope < scope)
- scope = address->scope;
- }
+ if (address->scope < scope)
+ scope = address->scope;
+ }
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- operstate = LINK_OPERSTATE_ROUTABLE;
- else if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- operstate = LINK_OPERSTATE_DEGRADED;
- else
- /* no useful addresses found */
- operstate = LINK_OPERSTATE_CARRIER;
- } else if (link->flags & IFF_UP)
- operstate = LINK_OPERSTATE_NO_CARRIER;
+ /* for operstate we also take foreign addresses into account */
+ SET_FOREACH(address, link->addresses_foreign, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ address_state = LINK_ADDRESS_STATE_ROUTABLE;
+ else if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ address_state = LINK_ADDRESS_STATE_DEGRADED;
else
- operstate = LINK_OPERSTATE_OFF;
+ /* no useful addresses found */
+ address_state = LINK_ADDRESS_STATE_OFF;
+
+ /* 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 (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) &&
- link_is_enslaved(link))
+ 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;
- if (operstate >= LINK_OPERSTATE_CARRIER) {
- Link *slave;
-
- SET_FOREACH(slave, link->slaves, i) {
- link_update_operstate(slave, false);
+ if (link->carrier_state != carrier_state) {
+ link->carrier_state = carrier_state;
+ changed = true;
+ if (strv_extend(&p, "CarrierState") < 0)
+ log_oom();
+ }
- if (slave->operstate < LINK_OPERSTATE_CARRIER)
- operstate = LINK_OPERSTATE_DEGRADED_CARRIER;
- }
+ if (link->address_state != address_state) {
+ link->address_state = address_state;
+ changed = true;
+ if (strv_extend(&p, "AddressState") < 0)
+ log_oom();
}
if (link->operstate != operstate) {
link->operstate = operstate;
- link_send_changed(link, "OperationalState", NULL);
- link_dirty(link);
+ changed = true;
+ if (strv_extend(&p, "OperationalState") < 0)
+ log_oom();
}
+ if (p)
+ link_send_changed_strv(link, p);
+ if (changed)
+ link_dirty(link);
+
if (also_update_master && link->network) {
link_update_master_operstate(link, link->network->bond);
link_update_master_operstate(link, link->network->bridge);
.rtnl_extended_attrs = true,
.ifindex = ifindex,
.iftype = iftype,
+ .sysctl_ipv6_enabled = -1,
};
link->ifname = strdup(ifname);
link->routing_policy_rules_configured = false;
LIST_FOREACH(rules, rule, link->network->rules) {
- r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
- rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif,
- rule->protocol, &rule->sport, &rule->dport, &rrule);
- if (r == 0) {
- (void) routing_policy_rule_make_local(link->manager, rrule);
+ r = routing_policy_rule_get(link->manager, rule, &rrule);
+ if (r >= 0) {
+ if (r == 0)
+ (void) routing_policy_rule_make_local(link->manager, rrule);
continue;
}
link_enter_failed(link);
return r;
}
-
- link->routing_policy_rule_messages++;
+ if (r > 0)
+ link->routing_policy_rule_messages++;
}
routing_policy_rule_purge(link->manager, link);
link_enter_failed(link);
return r;
}
-
- link->route_messages++;
+ if (r > 0)
+ link->route_messages++;
}
if (link->route_messages == 0) {
if (!link->routing_policy_rules_configured)
return;
- if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !(link->ipv4ll_address && link->ipv4ll_route))
- return;
+ if (link_has_carrier(link) || !link->network->configure_without_carrier) {
- if (link_ipv6ll_enabled(link) &&
- in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
- return;
+ if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !(link->ipv4ll_address && link->ipv4ll_route))
+ return;
- if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
- !link->dhcp4_configured &&
- !link->dhcp6_configured &&
- !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && 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_ipv6ll_enabled(link) &&
+ in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
+ return;
- if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
- return;
+ if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
+ !link->dhcp4_configured &&
+ !link->dhcp6_configured &&
+ !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && 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->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
int link_save(Link *link) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *admin_state, *oper_state;
+ const char *admin_state, *oper_state, *carrier_state, *address_state;
Address *a;
Route *route;
Iterator i;
oper_state = link_operstate_to_string(link->operstate);
assert(oper_state);
+ carrier_state = link_carrier_state_to_string(link->carrier_state);
+ assert(carrier_state);
+
+ address_state = link_address_state_to_string(link->address_state);
+ assert(address_state);
+
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
goto fail;
fprintf(f,
"# This is private data. Do not parse.\n"
"ADMIN_STATE=%s\n"
- "OPER_STATE=%s\n",
- admin_state, oper_state);
+ "OPER_STATE=%s\n"
+ "CARRIER_STATE=%s\n"
+ "ADDRESS_STATE=%s\n",
+ admin_state, oper_state, carrier_state, address_state);
if (link->network) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;