1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-10 01:17:44 +03:00

Merge pull request #19069 from LetzteInstanz/waiting_for_address_family

systemd-networkd-wait-online: wait for specific address family
This commit is contained in:
Yu Watanabe 2021-04-14 18:57:39 +09:00 committed by GitHub
commit c7cbe25d11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 383 additions and 36 deletions

View File

@ -85,6 +85,34 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-4</option></term>
<term><option>--ipv4</option></term>
<listitem><para>Waiting for an IPv4 address of each network interface to be configured. If this
option is specified with <option>--any</option>, then
<command>systemd-networkd-wait-online</command> exits with success when at least one interface
becomes online and has an IPv4 address. The option is applied only for the operational state
<literal>degraded</literal> or above. If neither <option>--ipv4</option> nor
<option>--ipv6</option> is specified, then the value from
<varname>RequiredFamilyForOnline=</varname> in the corresponding <filename>.network</filename>
file is used if present.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-6</option></term>
<term><option>--ipv6</option></term>
<listitem><para>Waiting for an IPv6 address of each network interface to be configured. If this
option is specified with <option>--any</option>, then
<command>systemd-networkd-wait-online</command> exits with success when at least one interface
becomes online and has an IPv6 address. The option is applied only for the operational state
<literal>degraded</literal> or above. If neither <option>--ipv4</option> nor
<option>--ipv6</option> is specified, then the value from
<varname>RequiredFamilyForOnline=</varname> in the corresponding <filename>.network</filename>
file is used if present.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--any</option></term>

View File

@ -229,6 +229,18 @@
if <literal>RequiredForOnline=no</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RequiredFamilyForOnline=</varname></term>
<listitem>
<para>Specifies an address family. When specified,
<command>systemd-networkd-wait-online</command> waits for at least one routable or link-local
IP address in the family should be configured on the link. Takes one of
<literal>ipv4</literal>, <literal>ipv6</literal>, <literal>both</literal>, or
<literal>any</literal>. Defaults to <literal>any</literal>. Note that this will be used only
when <varname>RequiredForOnline=</varname> is true, or its minimum operational state is
<literal>degraded</literal> or above. Otherwise, it will be ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ActivationPolicy=</varname></term>
<listitem>

View File

@ -56,6 +56,15 @@ static const char* const link_carrier_state_table[_LINK_CARRIER_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(link_carrier_state, LinkCarrierState);
static const char* const link_required_address_family_table[_ADDRESS_FAMILY_MAX] = {
[ADDRESS_FAMILY_NO] = "any",
[ADDRESS_FAMILY_IPV4] = "ipv4",
[ADDRESS_FAMILY_IPV6] = "ipv6",
[ADDRESS_FAMILY_YES] = "both",
};
DEFINE_STRING_TABLE_LOOKUP(link_required_address_family, AddressFamily);
static const char* const link_address_state_table[_LINK_ADDRESS_STATE_MAX] = {
[LINK_ADDRESS_STATE_OFF] = "off",
[LINK_ADDRESS_STATE_DEGRADED] = "degraded",

View File

@ -11,6 +11,16 @@
bool network_is_online(void);
typedef enum AddressFamily {
/* This is a bitmask, though it usually doesn't feel that way! */
ADDRESS_FAMILY_NO = 0,
ADDRESS_FAMILY_IPV4 = 1 << 0,
ADDRESS_FAMILY_IPV6 = 1 << 1,
ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6,
_ADDRESS_FAMILY_MAX,
_ADDRESS_FAMILY_INVALID = -EINVAL,
} AddressFamily;
typedef enum LinkOperationalState {
LINK_OPERSTATE_MISSING,
LINK_OPERSTATE_OFF,
@ -50,6 +60,9 @@ LinkOperationalState link_operstate_from_string(const char *s) _pure_;
const char* link_carrier_state_to_string(LinkCarrierState s) _const_;
LinkCarrierState link_carrier_state_from_string(const char *s) _pure_;
const char* link_required_address_family_to_string(AddressFamily s) _const_;
AddressFamily link_required_address_family_from_string(const char *s) _pure_;
const char* link_address_state_to_string(LinkAddressState s) _const_;
LinkAddressState link_address_state_from_string(const char *s) _pure_;

View File

@ -48,6 +48,14 @@ _public_ int sd_network_get_address_state(char **state) {
return network_get_string("ADDRESS_STATE", state);
}
_public_ int sd_network_get_ipv4_address_state(char **state) {
return network_get_string("IPV4_ADDRESS_STATE", state);
}
_public_ int sd_network_get_ipv6_address_state(char **state) {
return network_get_string("IPV6_ADDRESS_STATE", state);
}
static int network_get_strv(const char *key, char ***ret) {
_cleanup_strv_free_ char **a = NULL;
_cleanup_free_ char *s = NULL;
@ -160,6 +168,26 @@ _public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "OPER_STATE", state);
}
_public_ int sd_network_link_get_required_family_for_online(int ifindex, char **state) {
_cleanup_free_ char *s = NULL;
int r;
assert_return(state, -EINVAL);
r = network_link_get_string(ifindex, "REQUIRED_FAMILY_FOR_ONLINE", &s);
if (r < 0) {
if (r != -ENODATA)
return r;
s = strdup("any");
if (!s)
return -ENOMEM;
}
*state = TAKE_PTR(s);
return 0;
}
_public_ int sd_network_link_get_carrier_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "CARRIER_STATE", state);
}
@ -168,6 +196,14 @@ _public_ int sd_network_link_get_address_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "ADDRESS_STATE", state);
}
_public_ int sd_network_link_get_ipv4_address_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "IPV4_ADDRESS_STATE", state);
}
_public_ int sd_network_link_get_ipv6_address_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "IPV6_ADDRESS_STATE", state);
}
_public_ int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **iaid) {
return network_link_get_string(ifindex, "DHCP6_CLIENT_IAID", iaid);
}

View File

@ -684,6 +684,8 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Link, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Link, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Link, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Link, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0),

View File

@ -4,6 +4,7 @@
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_link.h>
#include <sys/socket.h>
#include <unistd.h>
#include "alloc-util.h"
@ -163,12 +164,25 @@ static void link_update_master_operstate(Link *link, NetDev *netdev) {
link_update_operstate(master, true);
}
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 address_state;
LinkAddressState ipv4_address_state, ipv6_address_state, address_state;
_cleanup_strv_free_ char **p = NULL;
uint8_t scope = RT_SCOPE_NOWHERE;
uint8_t ipv4_scope = RT_SCOPE_NOWHERE, ipv6_scope = RT_SCOPE_NOWHERE, scope;
bool changed = false;
Address *address;
@ -201,8 +215,11 @@ void link_update_operstate(Link *link, bool also_update_master) {
if (!address_is_ready(address))
continue;
if (address->scope < scope)
scope = address->scope;
if (address->family == AF_INET && address->scope < ipv4_scope)
ipv4_scope = address->scope;
if (address->family == AF_INET6 && address->scope < ipv6_scope)
ipv6_scope = address->scope;
}
/* for operstate we also take foreign addresses into account */
@ -210,19 +227,18 @@ void link_update_operstate(Link *link, bool also_update_master) {
if (!address_is_ready(address))
continue;
if (address->scope < scope)
scope = address->scope;
if (address->family == AF_INET && address->scope < ipv4_scope)
ipv4_scope = address->scope;
if (address->family == AF_INET6 && address->scope < ipv6_scope)
ipv6_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
/* no useful addresses found */
address_state = LINK_ADDRESS_STATE_OFF;
ipv4_address_state = address_state_from_scope(ipv4_scope);
ipv6_address_state = address_state_from_scope(ipv6_scope);
scope = MIN(ipv4_scope, ipv6_scope);
address_state = address_state_from_scope(scope);
/* Mapping of address and carrier state vs operational state
* carrier state
@ -256,6 +272,20 @@ void link_update_operstate(Link *link, bool also_update_master) {
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;

View File

@ -75,6 +75,8 @@ typedef struct Link {
LinkOperationalState operstate;
LinkCarrierState carrier_state;
LinkAddressState address_state;
LinkAddressState ipv4_address_state;
LinkAddressState ipv6_address_state;
unsigned address_messages;
unsigned address_remove_messages;

View File

@ -235,6 +235,8 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Manager, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Manager, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Manager, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Manager, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD_WITH_ARGS("ListLinks",
SD_BUS_NO_ARGS,

View File

@ -39,6 +39,8 @@ struct Manager {
LinkOperationalState operational_state;
LinkCarrierState carrier_state;
LinkAddressState address_state;
LinkAddressState ipv4_address_state;
LinkAddressState ipv6_address_state;
Hashmap *links;
Hashmap *netdevs;

View File

@ -67,6 +67,7 @@ Link.Promiscuous, config_parse_tristate,
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
Link.RequiredFamilyForOnline, config_parse_required_family_for_online, 0, offsetof(Network, required_family_for_online)
SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0
SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0
SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, 0

View File

@ -1196,6 +1196,9 @@ int config_parse_required_for_online(
return 0;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
"Failed to parse RequiredFamilyForOnline= setting");
DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
"Failed to parse KeepConfiguration= setting");

View File

@ -104,6 +104,7 @@ struct Network {
bool unmanaged;
bool required_for_online; /* Is this network required to be considered online? */
LinkOperationalStateRange required_operstate_for_online;
AddressFamily required_family_for_online;
ActivationPolicy activation_policy;
/* misc settings */
@ -348,6 +349,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
CONFIG_PARSER_PROTOTYPE(config_parse_required_family_for_online);
CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);

View File

@ -105,10 +105,11 @@ static int ordered_set_put_in4_addrv(
int manager_save(Manager *m) {
_cleanup_ordered_set_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
const char *operstate_str, *carrier_state_str, *address_state_str;
const char *operstate_str, *carrier_state_str, *address_state_str, *ipv4_address_state_str, *ipv6_address_state_str;
LinkOperationalState operstate = LINK_OPERSTATE_OFF;
LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
LinkAddressState ipv4_address_state = LINK_ADDRESS_STATE_OFF, ipv6_address_state = LINK_ADDRESS_STATE_OFF,
address_state = LINK_ADDRESS_STATE_OFF;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_strv_free_ char **p = NULL;
_cleanup_fclose_ FILE *f = NULL;
@ -133,6 +134,12 @@ int manager_save(Manager *m) {
if (link->address_state > address_state)
address_state = link->address_state;
if (link->ipv4_address_state > ipv4_address_state)
ipv4_address_state = link->ipv4_address_state;
if (link->ipv6_address_state > ipv6_address_state)
ipv6_address_state = link->ipv6_address_state;
if (!link->network)
continue;
@ -226,6 +233,12 @@ int manager_save(Manager *m) {
address_state_str = link_address_state_to_string(address_state);
assert(address_state_str);
ipv4_address_state_str = link_address_state_to_string(ipv4_address_state);
assert(ipv4_address_state_str);
ipv6_address_state_str = link_address_state_to_string(ipv6_address_state);
assert(ipv6_address_state_str);
r = fopen_temporary(m->state_file, &f, &temp_path);
if (r < 0)
return r;
@ -236,8 +249,10 @@ int manager_save(Manager *m) {
"# This is private data. Do not parse.\n"
"OPER_STATE=%s\n"
"CARRIER_STATE=%s\n"
"ADDRESS_STATE=%s\n",
operstate_str, carrier_state_str, address_state_str);
"ADDRESS_STATE=%s\n"
"IPV4_ADDRESS_STATE=%s\n"
"IPV6_ADDRESS_STATE=%s\n",
operstate_str, carrier_state_str, address_state_str, ipv4_address_state_str, ipv6_address_state_str);
ordered_set_print(f, "DNS=", dns);
ordered_set_print(f, "NTP=", ntp);
@ -273,6 +288,18 @@ int manager_save(Manager *m) {
log_oom();
}
if (m->ipv4_address_state != ipv4_address_state) {
m->ipv4_address_state = ipv4_address_state;
if (strv_extend(&p, "IPv4AddressState") < 0)
log_oom();
}
if (m->ipv6_address_state != ipv6_address_state) {
m->ipv6_address_state = ipv6_address_state;
if (strv_extend(&p, "IPv6AddressState") < 0)
log_oom();
}
if (p) {
r = manager_send_changed_strv(m, p);
if (r < 0)
@ -376,7 +403,7 @@ static void serialize_addresses(
}
int link_save(Link *link) {
const char *admin_state, *oper_state, *carrier_state, *address_state;
const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@ -403,6 +430,12 @@ int link_save(Link *link) {
address_state = link_address_state_to_string(link->address_state);
assert(address_state);
ipv4_address_state = link_address_state_to_string(link->ipv4_address_state);
assert(ipv4_address_state);
ipv6_address_state = link_address_state_to_string(link->ipv6_address_state);
assert(ipv6_address_state);
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
return r;
@ -414,8 +447,10 @@ int link_save(Link *link) {
"ADMIN_STATE=%s\n"
"OPER_STATE=%s\n"
"CARRIER_STATE=%s\n"
"ADDRESS_STATE=%s\n",
admin_state, oper_state, carrier_state, address_state);
"ADDRESS_STATE=%s\n"
"IPV4_ADDRESS_STATE=%s\n"
"IPV6_ADDRESS_STATE=%s\n",
admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
if (link->network) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;
@ -431,6 +466,9 @@ int link_save(Link *link) {
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(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));
fprintf(f, "ACTIVATION_POLICY=%s\n",
activation_policy_to_string(link->network->activation_policy));

View File

@ -8,18 +8,9 @@
#include "hashmap.h"
#include "log.h"
#include "macro.h"
#include "network-util.h"
#include "string-util.h"
typedef enum AddressFamily {
/* This is a bitmask, though it usually doesn't feel that way! */
ADDRESS_FAMILY_NO = 0,
ADDRESS_FAMILY_IPV4 = 1 << 0,
ADDRESS_FAMILY_IPV6 = 1 << 1,
ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6,
_ADDRESS_FAMILY_MAX,
_ADDRESS_FAMILY_INVALID = -EINVAL,
} AddressFamily;
typedef struct NetworkConfigSection {
unsigned line;
bool invalid;

View File

@ -97,7 +97,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
}
int link_update_monitor(Link *l) {
_cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL;
_cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *required_family = NULL,
*ipv4_address_state = NULL, *ipv6_address_state = NULL, *state = NULL;
int r, ret = 0;
assert(l);
@ -135,6 +136,47 @@ int link_update_monitor(Link *l) {
l->operational_state = s;
}
r = sd_network_link_get_required_family_for_online(l->ifindex, &required_family);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get required address family, ignoring: %m");
else if (isempty(required_family))
l->required_family = ADDRESS_FAMILY_NO;
else {
AddressFamily f;
f = link_required_address_family_from_string(required_family);
if (f < 0)
ret = log_link_debug_errno(l, f, "Failed to parse required address family, ignoring: %m");
else
l->required_family = f;
}
r = sd_network_link_get_ipv4_address_state(l->ifindex, &ipv4_address_state);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get IPv4 address state, ignoring: %m");
else {
LinkAddressState s;
s = link_address_state_from_string(ipv4_address_state);
if (s < 0)
ret = log_link_debug_errno(l, s, "Failed to parse IPv4 address state, ignoring: %m");
else
l->ipv4_address_state = s;
}
r = sd_network_link_get_ipv6_address_state(l->ifindex, &ipv6_address_state);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get IPv6 address state, ignoring: %m");
else {
LinkAddressState s;
s = link_address_state_from_string(ipv6_address_state);
if (s < 0)
ret = log_link_debug_errno(l, s, "Failed to parse IPv6 address state, ignoring: %m");
else
l->ipv6_address_state = s;
}
r = sd_network_link_get_setup_state(l->ifindex, &state);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get setup state, ignoring: %m");

View File

@ -19,6 +19,9 @@ struct Link {
bool required_for_online;
LinkOperationalStateRange required_operstate;
LinkOperationalState operational_state;
AddressFamily required_family;
LinkAddressState ipv4_address_state;
LinkAddressState ipv6_address_state;
char *state;
};

View File

@ -32,6 +32,13 @@ static bool manager_ignore_link(Manager *m, Link *link) {
}
static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
AddressFamily required_family;
bool needs_ipv4;
bool needs_ipv6;
assert(m);
assert(l);
/* This returns the following:
* -EAGAIN: not processed by udev or networkd
* 0: operstate is not enough
@ -60,7 +67,35 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
return 0;
}
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_DEGRADED) {
if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
goto ipv4_not_ready;
if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED)
goto ipv6_not_ready;
}
if (s.min >= LINK_OPERSTATE_ROUTABLE) {
if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE)
goto ipv4_not_ready;
if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE)
goto ipv6_not_ready;
}
return 1;
ipv4_not_ready:
log_link_debug(l, "No routable or link-local IPv4 address is configured.");
return 0;
ipv6_not_ready:
log_link_debug(l, "No routable or link-local IPv6 address is configured.");
return 0;
}
bool manager_configured(Manager *m) {
@ -298,6 +333,7 @@ static int manager_network_monitor_listen(Manager *m) {
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalStateRange required_operstate,
AddressFamily required_family,
bool any, usec_t timeout) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
@ -312,6 +348,7 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
.interfaces = interfaces,
.ignore = ignore,
.required_operstate = required_operstate,
.required_family = required_family,
.any = any,
};

View File

@ -21,6 +21,7 @@ struct Manager {
char **ignore;
LinkOperationalStateRange required_operstate;
AddressFamily required_family;
bool any;
sd_netlink *rtnl;
@ -35,6 +36,7 @@ struct Manager {
Manager* manager_free(Manager *m);
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalStateRange required_operstate,
AddressFamily required_family,
bool any, usec_t timeout);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);

View File

@ -19,6 +19,7 @@ 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 AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
static bool arg_any = false;
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
@ -42,6 +43,8 @@ static int help(void) {
" --ignore=INTERFACE Don't take these interfaces into account\n"
" -o --operational-state=MIN_OPERSTATE[:MAX_OPERSTATE]\n"
" Required operational state\n"
" -4 --ipv4 Requires at least one IPv4 address\n"
" -6 --ipv6 Requires at least one IPv6 address\n"
" --any Wait until at least one of the interfaces is online\n"
" --timeout=SECS Maximum time to wait for network connectivity\n"
"\nSee the %s for details.\n",
@ -111,6 +114,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "interface", required_argument, NULL, 'i' },
{ "ignore", required_argument, NULL, ARG_IGNORE },
{ "operational-state", required_argument, NULL, 'o' },
{ "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' },
{ "any", no_argument, NULL, ARG_ANY },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{}
@ -121,7 +126,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hi:qo:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "hi:qo:46", options, NULL)) >= 0)
switch (c) {
@ -159,6 +164,15 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case '4':
arg_required_family |= ADDRESS_FAMILY_IPV4;
break;
case '6':
arg_required_family |= ADDRESS_FAMILY_IPV6;
break;
case ARG_ANY:
arg_any = true;
break;
@ -197,7 +211,7 @@ static int run(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_any, arg_timeout);
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");

View File

@ -51,6 +51,8 @@ _SD_BEGIN_DECLARATIONS;
int sd_network_get_operational_state(char **state);
int sd_network_get_carrier_state(char **state);
int sd_network_get_address_state(char **state);
int sd_network_get_ipv4_address_state(char **state);
int sd_network_get_ipv6_address_state(char **state);
/* Get DNS entries for all links. These are string representations of
* IP addresses */
@ -92,8 +94,11 @@ int sd_network_link_get_setup_state(int ifindex, char **state);
*/
int sd_network_link_get_operational_state(int ifindex, char **state);
int sd_network_link_get_required_operstate_for_online(int ifindex, char **state);
int sd_network_link_get_required_family_for_online(int ifindex, char **state);
int sd_network_link_get_carrier_state(int ifindex, char **state);
int sd_network_link_get_address_state(int ifindex, char **state);
int sd_network_link_get_ipv4_address_state(int ifindex, char **state);
int sd_network_link_get_ipv6_address_state(int ifindex, char **state);
/* Indicates whether the network is relevant to being online.
* Possible return codes:

View File

@ -32,6 +32,7 @@ PermanentMACAddress=
[Link]
ActivationPolicy=
RequiredForOnline=
RequiredFamilyForOnline=
ARP=
AllMulticast=
Unmanaged=

View File

@ -545,6 +545,7 @@ RemoteChecksumRx=
RemoteChecksumTx=
ReorderHeader=
RequestBroadcast=
RequiredFamilyForOnline=
RequiredForOnline=
ResendIGMP=
RootDistanceMaxSec=

View File

@ -0,0 +1,9 @@
[Match]
Name=veth99
[NetworkEmulator]
DelaySec=9
[Network]
DHCP=ipv4
IPv6AcceptRA=true

View File

@ -0,0 +1,11 @@
[Match]
Name=veth-peer
[Network]
Address=192.168.5.1/24
IPv6AcceptRA=no
DHCPServer=yes
IPv6SendRA=yes
[IPv6Prefix]
Prefix=2002:da8:1:0::/64

View File

@ -0,0 +1,12 @@
[Match]
Name=veth-peer
[NetworkEmulator]
DelaySec=15
[Network]
IPv6AcceptRA=no
IPv6SendRA=yes
[IPv6Prefix]
Prefix=2002:da8:1:0::/64

View File

@ -0,0 +1,6 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=true
Address=192.168.5.1/24

View File

@ -3,6 +3,7 @@ Name=dummy98
[Link]
RequiredForOnline=routable
RequiredFamilyForOnline=both
[Network]
IPv6AcceptRA=no
@ -14,3 +15,4 @@ MulticastDNS=yes
DNSSEC=no
Address=192.168.10.10/24
Address=192.168.12.12/24
Address=2002:da8:1:0:1034:56ff:fe78:9abc/64

View File

@ -555,7 +555,7 @@ class Utilities():
self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
return False
def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, setup_state='configured', setup_timeout=5):
def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
"""Wait for the link(s) to reach the specified operstate and/or setup state.
This is similar to wait_operstate() but can be used for multiple links,
@ -569,6 +569,9 @@ class Utilities():
Set 'bool_any' to True to wait for any (instead of all) of the given links.
If this is set, no setup_state checks are done.
Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
This is applied only for the operational state 'degraded' or above.
Note that this function waits for the link(s) to reach *or exceed* the given operstate.
However, the setup_state, if specified, must be matched *exactly*.
@ -578,6 +581,10 @@ class Utilities():
args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
if bool_any:
args += ['--any']
if ipv4:
args += ['--ipv4']
if ipv6:
args += ['--ipv6']
try:
check_output(*args, env=env)
except subprocess.CalledProcessError:
@ -1781,6 +1788,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'gretun97',
'ip6gretun97',
'test1',
'veth-peer',
'veth99',
'vrf99',
]
@ -1842,6 +1850,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-vrf.netdev',
'25-vrf.network',
'26-link-local-addressing-ipv6.network',
'dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network',
'dhcp-server-with-ipv6-prefix.network',
'ipv6ra-prefix-client-with-static-ipv4-address.network',
'ipv6-prefix-with-delay.network',
'routing-policy-rule-dummy98.network',
'routing-policy-rule-test1.network',
'routing-policy-rule-reconfigure1.network',
@ -3099,6 +3111,22 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
def test_wait_online_ipv4(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-with-ipv6-prefix.network', 'dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
start_networkd()
self.wait_online(['veth99:routable'], ipv4=True)
self.wait_address('veth99', r'192.168.5.[0-9]+', ipv='-4', timeout_sec=1)
def test_wait_online_ipv6(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix-with-delay.network', 'ipv6ra-prefix-client-with-static-ipv4-address.network')
start_networkd()
self.wait_online(['veth99:routable'], ipv6=True)
self.wait_address('veth99', r'2002:da8:1:0:1034:56ff:fe78:9abc', ipv='-6', timeout_sec=1)
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [
'dummy98',
@ -3135,10 +3163,13 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
with open(path) as f:
data = f.read()
self.assertRegex(data, r'IPV4_ADDRESS_STATE=routable')
self.assertRegex(data, r'IPV6_ADDRESS_STATE=routable')
self.assertRegex(data, r'ADMIN_STATE=configured')
self.assertRegex(data, r'OPER_STATE=routable')
self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
self.assertRegex(data, r'REQUIRED_FAMILY_FOR_ONLINE=both')
self.assertRegex(data, r'ACTIVATION_POLICY=up')
self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')