network: several cleanups for required operstate for online, and change the default for CAN devices
<varlistentry>
<term><varname>RequiredForOnline=</varname></term>
<listitem>
- <para>Takes a boolean or a minimum operational state and an optional maximum operational
- state. Please see
+ <para>Takes a boolean, a minimum operational state (e.g., <literal>carrier</literal>), or a range
+ of operational state separated with a colon (e.g., <literal>degraded:routable</literal>).
+ Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. When <literal>yes</literal>, the network is deemed required
when determining whether the system is online (including when running
minimum and maximum operational state required for the network interface to be considered
online.</para>
+ <para>When <literal>yes</literal> is specified for a CAN device,
+ <command>systemd-networkd-wait-online</command> deems that the interface is online when its
+ operational state becomes <literal>carrier</literal>. For an interface with other type, e.g.
+ <literal>ether</literal>, the interface is deened online when its online state is
+ <literal>degraded</literal> or <literal>routable</literal>.</para>
+
<para>Defaults to <literal>yes</literal> when <varname>ActivationPolicy=</varname> is not
set, or set to <literal>up</literal>, <literal>always-up</literal>, or
<literal>bound</literal>. Defaults to <literal>no</literal> when
DEFINE_STRING_TABLE_LOOKUP(link_online_state, LinkOnlineState);
-int parse_operational_state_range(const char *str, LinkOperationalStateRange *out) {
- LinkOperationalStateRange range = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
- _cleanup_free_ const char *min = NULL;
+int parse_operational_state_range(const char *s, LinkOperationalStateRange *ret) {
+ LinkOperationalStateRange range = LINK_OPERSTATE_RANGE_INVALID;
+ _cleanup_free_ char *buf = NULL;
const char *p;
- assert(str);
- assert(out);
-
- p = strchr(str, ':');
- if (p) {
- min = strndup(str, p - str);
+ assert(s);
+ assert(ret);
- if (!isempty(p + 1)) {
- range.max = link_operstate_from_string(p + 1);
- if (range.max < 0)
- return -EINVAL;
- }
- } else
- min = strdup(str);
+ /* allowed formats: "min", "min:", "min:max", ":max" */
- if (!min)
- return -ENOMEM;
+ if (isempty(s) || streq(s, ":"))
+ return -EINVAL;
- if (!isempty(min)) {
- range.min = link_operstate_from_string(min);
- if (range.min < 0)
+ p = strchr(s, ':');
+ if (!p || isempty(p + 1))
+ range.max = LINK_OPERSTATE_ROUTABLE;
+ else {
+ range.max = link_operstate_from_string(p + 1);
+ if (range.max < 0)
return -EINVAL;
}
- /* Fail on empty strings. */
- if (range.min == _LINK_OPERSTATE_INVALID && range.max == _LINK_OPERSTATE_INVALID)
- return -EINVAL;
+ if (p) {
+ buf = strndup(s, p - s);
+ if (!buf)
+ return -ENOMEM;
- if (range.min == _LINK_OPERSTATE_INVALID)
+ s = buf;
+ }
+
+ if (isempty(s))
range.min = LINK_OPERSTATE_MISSING;
- if (range.max == _LINK_OPERSTATE_INVALID)
- range.max = LINK_OPERSTATE_ROUTABLE;
+ else {
+ range.min = link_operstate_from_string(s);
+ if (range.min < 0)
+ return -EINVAL;
+ }
- if (range.min > range.max)
+ if (!operational_state_range_is_valid(&range))
return -EINVAL;
- *out = range;
-
+ *ret = range;
return 0;
}
LinkOperationalState max;
} LinkOperationalStateRange;
-#define LINK_OPERSTATE_RANGE_DEFAULT (LinkOperationalStateRange) { LINK_OPERSTATE_DEGRADED, \
- LINK_OPERSTATE_ROUTABLE }
-
-int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
+#define LINK_OPERSTATE_RANGE_DEFAULT \
+ (const LinkOperationalStateRange) { \
+ .min = LINK_OPERSTATE_DEGRADED, \
+ .max = LINK_OPERSTATE_ROUTABLE, \
+ }
+
+#define LINK_OPERSTATE_RANGE_INVALID \
+ (const LinkOperationalStateRange) { \
+ .min = _LINK_OPERSTATE_INVALID, \
+ .max = _LINK_OPERSTATE_INVALID, \
+ }
+
+int parse_operational_state_range(const char *s, LinkOperationalStateRange *ret);
int network_link_get_operational_state(int ifindex, LinkOperationalState *ret);
+
+static inline bool operational_state_is_valid(LinkOperationalState s) {
+ return s >= 0 && s < _LINK_OPERSTATE_MAX;
+}
+static inline bool operational_state_range_is_valid(const LinkOperationalStateRange *range) {
+ return range &&
+ operational_state_is_valid(range->min) &&
+ operational_state_is_valid(range->max) &&
+ range->min <= range->max;
+}
+static inline bool operational_state_is_in_range(LinkOperationalState s, const LinkOperationalStateRange *range) {
+ return range && range->min <= s && s <= range->max;
+}
#include "udev-util.h"
#include "vrf.h"
+void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret) {
+ assert(link);
+ assert(ret);
+
+ if (link->network && operational_state_range_is_valid(&link->network->required_operstate_for_online))
+ *ret = link->network->required_operstate_for_online;
+ else if (link->iftype == ARPHRD_CAN)
+ *ret = (const LinkOperationalStateRange) {
+ .min = LINK_OPERSTATE_CARRIER,
+ .max = LINK_OPERSTATE_CARRIER,
+ };
+ else
+ *ret = LINK_OPERSTATE_RANGE_DEFAULT;
+}
+
bool link_ipv6_enabled(Link *link) {
assert(link);
else
operstate = LINK_OPERSTATE_ENSLAVED;
+ LinkOperationalStateRange req;
+ link_required_operstate_for_online(link, &req);
+
/* 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)
+
+ else if (!operational_state_is_in_range(operstate, &req))
online_state = LINK_ONLINE_STATE_OFFLINE;
+
else {
AddressFamily required_family = link->network->required_family_for_online;
bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
* to offline in the blocks below. */
online_state = LINK_ONLINE_STATE_ONLINE;
- if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_DEGRADED) {
+ if (req.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 (req.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)
int link_flags_to_string_alloc(uint32_t flags, char **ret);
const char *kernel_operstate_to_string(int t) _const_;
+
+void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret);
.n_ref = 1,
.required_for_online = -1,
- .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
+ .required_operstate_for_online = LINK_OPERSTATE_RANGE_INVALID,
.activation_policy = _ACTIVATION_POLICY_INVALID,
.group = -1,
.arp = -1,
void *userdata) {
Network *network = ASSERT_PTR(userdata);
- LinkOperationalStateRange range;
- bool required = true;
int r;
assert(filename);
if (isempty(rvalue)) {
network->required_for_online = -1;
- network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
+ network->required_operstate_for_online = LINK_OPERSTATE_RANGE_INVALID;
return 0;
}
- r = parse_operational_state_range(rvalue, &range);
+ r = parse_operational_state_range(rvalue, &network->required_operstate_for_online);
if (r < 0) {
r = parse_boolean(rvalue);
if (r < 0) {
return 0;
}
- required = r;
- range = LINK_OPERSTATE_RANGE_DEFAULT;
+ network->required_for_online = r;
+ network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
+ return 0;
}
- network->required_for_online = required;
- network->required_operstate_for_online = range;
-
+ network->required_for_online = true;
return 0;
}
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
yes_no(link->network->required_for_online));
- LinkOperationalStateRange st = link->network->required_operstate_for_online;
- fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n",
- strempty(link_operstate_to_string(st.min)),
- st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
- st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
+ LinkOperationalStateRange st;
+ link_required_operstate_for_online(link, &st);
+
+ fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s:%s\n",
+ link_operstate_to_string(st.min), link_operstate_to_string(st.max));
fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n",
link_required_address_family_to_string(link->network->required_family_for_online));
return false;
}
-static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
+static int manager_link_is_online(Manager *m, Link *l, const LinkOperationalStateRange *state_range) {
AddressFamily required_family;
bool needs_ipv4;
bool needs_ipv6;
"link is being processed by networkd: setup state is %s.",
l->state);
- if (s.min < 0)
- s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
- : l->required_operstate.min;
+ const LinkOperationalStateRange *range;
+ FOREACH_POINTER(range, state_range, &m->required_operstate, &l->required_operstate)
+ if (operational_state_range_is_valid(range))
+ break;
+ assert(range != POINTER_MAX);
- if (s.max < 0)
- s.max = m->required_operstate.max >= 0 ? m->required_operstate.max
- : l->required_operstate.max;
-
- if (l->operational_state < s.min || l->operational_state > s.max)
+ if (!operational_state_is_in_range(l->operational_state, range))
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
"Operational state '%s' is not in range ['%s':'%s']",
link_operstate_to_string(l->operational_state),
- link_operstate_to_string(s.min), link_operstate_to_string(s.max));
+ link_operstate_to_string(range->min), link_operstate_to_string(range->max));
required_family = m->required_family > 0 ? m->required_family : l->required_family;
needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6;
- if (s.min < LINK_OPERSTATE_ROUTABLE) {
+ if (range->min < LINK_OPERSTATE_ROUTABLE) {
if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
"No routable or link-local IPv4 address is configured.");
continue;
}
- r = manager_link_is_online(m, l, *range);
+ r = manager_link_is_online(m, l, range);
if (r <= 0 && !m->any)
return false;
if (r > 0 && m->any)
continue;
}
- r = manager_link_is_online(m, l,
- (LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID,
- _LINK_OPERSTATE_INVALID });
+ r = manager_link_is_online(m, l, /* state_range = */ NULL);
if (r < 0 && !m->any) /* Unlike the above loop, unmanaged interfaces are ignored here. */
return false;
if (r > 0) {
static usec_t arg_timeout = 120 * USEC_PER_SEC;
static Hashmap *arg_interfaces = NULL;
static char **arg_ignore = NULL;
-static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
+static LinkOperationalStateRange arg_required_operstate = LINK_OPERSTATE_RANGE_INVALID;
static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
static bool arg_any = false;
if (p) {
r = parse_operational_state_range(p + 1, range);
if (r < 0)
- log_error_errno(r, "Invalid operational state range '%s'", p + 1);
+ return log_error_errno(r, "Invalid operational state range: %s", p + 1);
ifname = strndup(optarg, p - optarg);
} else {
- range->min = _LINK_OPERSTATE_INVALID;
- range->max = _LINK_OPERSTATE_INVALID;
+ *range = LINK_OPERSTATE_RANGE_INVALID;
ifname = strdup(str);
}
if (!ifname)
if (!ifname_valid(ifname))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid interface name '%s'", ifname);
+ "Invalid interface name: %s", ifname);
- r = hashmap_ensure_put(&arg_interfaces, &string_hash_ops, ifname, TAKE_PTR(range));
+ r = hashmap_ensure_put(&arg_interfaces, &string_hash_ops, ifname, range);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to store interface name: %m");
if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Interface name %s is already specified", ifname);
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Interface name %s is already specified.", ifname);
TAKE_PTR(ifname);
+ TAKE_PTR(range);
return 0;
}
break;
- case 'o': {
- LinkOperationalStateRange range;
-
- r = parse_operational_state_range(optarg, &range);
+ case 'o':
+ r = parse_operational_state_range(optarg, &arg_required_operstate);
if (r < 0)
return log_error_errno(r, "Invalid operational state range '%s'", optarg);
-
- arg_required_operstate = range;
-
break;
- }
case '4':
arg_required_family |= ADDRESS_FAMILY_IPV4;
start_networkd()
self.wait_online(['vcan99:carrier', 'vcan98:carrier'])
+ # For can devices, 'carrier' is the default required operational state.
+ self.wait_online(['vcan99', 'vcan98'])
# https://github.com/systemd/systemd/issues/30140
output = check_output('ip -d link show vcan99')
start_networkd()
self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
+ # For can devices, 'carrier' is the default required operational state.
+ self.wait_online(['vxcan99', 'vxcan-peer'])
@expectedFailureIfModuleIsNotAvailable('wireguard')
def test_wireguard(self):