device: use the 'required-timeout' property from IP setting

Change the logic in check_ip_state() to delay the connection ACTIVATED
state if an address family is pending and its required-timeout has not
expired.
This commit is contained in:
Beniamino Galvani 2021-06-23 18:08:47 +02:00
parent cb5960cef7
commit 35cccc41cb
2 changed files with 123 additions and 0 deletions

View file

@ -868,6 +868,9 @@ ipv6.ip6-privacy=0
<listitem><para>If unspecified or zero, use 50 for VPN profiles
and 100 for other profiles.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ipv4.required-timeout</varname></term>
</varlistentry>
<varlistentry>
<term><varname>ipv4.route-metric</varname></term>
</varlistentry>
@ -910,6 +913,9 @@ ipv6.ip6-privacy=0
"/proc/sys/net/ipv6/conf/default/use_tempaddr" as last fallback.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ipv6.required-timeout</varname></term>
</varlistentry>
<varlistentry>
<term><varname>ipv6.route-metric</varname></term>
</varlistentry>

View file

@ -499,8 +499,18 @@ typedef struct _NMDevicePrivate {
NMDeviceStageState stage1_sriov_state : 3;
bool ip_config_started : 1;
char *current_stable_id;
union {
struct {
GSource *ip_req_timeout_source_6;
GSource *ip_req_timeout_source_4;
};
GSource *ip_req_timeout_source_x[2];
};
/* Proxy Configuration */
NMProxyConfig * proxy_config;
NMPacrunnerConfId *pacrunner_conf_id;
@ -766,6 +776,7 @@ static void sriov_op_cb(GError *error, gpointer user_data);
static void device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice *self);
static gboolean device_link_changed(NMDevice *self);
static void check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update);
/*****************************************************************************/
@ -1364,6 +1375,40 @@ out:
return timeout;
}
static guint32
_prop_get_ipvx_required_timeout(NMDevice *self, int addr_family)
{
NMConnection * connection;
NMSettingIPConfig *s_ip;
int timeout;
nm_assert(NM_IS_DEVICE(self));
nm_assert_addr_family(addr_family);
connection = nm_device_get_applied_connection(self);
if (!connection)
return 0;
s_ip = nm_connection_get_setting_ip_config(connection, addr_family);
if (!s_ip)
return 0;
timeout = nm_setting_ip_config_get_required_timeout(s_ip);
nm_assert(timeout >= -1);
if (timeout > -1)
return (guint32) timeout;
return nm_config_data_get_connection_default_int64(
NM_CONFIG_GET_DATA,
NM_IS_IPv4(addr_family) ? NM_CON_DEFAULT("ipv4.required-timeout")
: NM_CON_DEFAULT("ipv6.required-timeout"),
self,
0,
G_MAXINT32,
0);
}
/**
* _prop_get_ipvx_dhcp_iaid:
* @self: the #NMDevice
@ -2787,14 +2832,72 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities)
/*****************************************************************************/
static gboolean
ip_required_timeout_x(NMDevice *self, int addr_family)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
_LOGD(LOGD_CORE,
"required-timeout expired for IPv%c",
nm_utils_addr_family_to_char(addr_family));
nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[NM_IS_IPv4(addr_family)]);
check_ip_state(self, FALSE, TRUE);
return G_SOURCE_CONTINUE;
}
static gboolean
ip_required_timeout_4(gpointer data)
{
return ip_required_timeout_x(data, AF_INET);
}
static gboolean
ip_required_timeout_6(gpointer data)
{
return ip_required_timeout_x(data, AF_INET6);
}
static void
_set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
const int IS_IPv4 = NM_IS_IPv4(addr_family);
guint timeout_msec;
int v4;
nm_assert_addr_family(addr_family);
if (new_state == NM_DEVICE_IP_STATE_CONF && !priv->ip_config_started) {
/* Start the required-timeout timers when one of IPv4/IPv6
* enters the CONF state. This means that if there is no carrier and
* ipv4.method=auto,ipv6.method=manual, the timeout for IPv4 will
* start as soon as connection is activated, even if DHCPv4 did not
* start yet.
*/
priv->ip_config_started = TRUE;
for (v4 = 1; v4 >= 0; v4--) {
char buf[32];
nm_assert(!priv->ip_req_timeout_source_x[v4]);
if ((timeout_msec = _prop_get_ipvx_required_timeout(self, v4 ? AF_INET : AF_INET6))) {
_LOGD(LOGD_CORE,
"required-timeout in %s msec for IPv%c",
timeout_msec == G_MAXINT32 ? "" : nm_sprintf_buf(buf, "%u", timeout_msec),
v4 ? '4' : '6');
if (timeout_msec == G_MAXINT32) {
priv->ip_req_timeout_source_x[v4] = g_source_ref(nm_g_source_sentinel_get(0));
} else {
priv->ip_req_timeout_source_x[v4] =
nm_g_timeout_add_source(timeout_msec,
v4 ? ip_required_timeout_4 : ip_required_timeout_6,
self);
}
}
}
}
if (priv->ip_state_x[IS_IPv4] == new_state)
return;
@ -6578,6 +6681,7 @@ check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update)
gboolean ip4_disabled = FALSE, ip6_disabled = FALSE;
NMSettingIPConfig *s_ip4, *s_ip6;
NMDeviceState state;
int IS_IPv4;
if (full_state_update && nm_device_get_state(self) != NM_DEVICE_STATE_IP_CONFIG)
return;
@ -6607,6 +6711,13 @@ check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update)
return;
}
for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
if (priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_CONF
&& priv->ip_req_timeout_source_x[IS_IPv4]) {
return;
}
}
if ((priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL
|| (ip4_disabled && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE))
&& (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL
@ -11812,6 +11923,8 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
req = nm_device_get_act_request(self);
g_assert(req);
nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[IS_IPv4]);
/* Interface must be IFF_UP before IP config can be applied */
ip_ifindex = nm_device_get_ip_ifindex(self);
g_return_if_fail(ip_ifindex);
@ -15779,6 +15892,10 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type)
_cleanup_ip_pre(self, AF_INET, cleanup_type);
_cleanup_ip_pre(self, AF_INET6, cleanup_type);
priv->ip_config_started = FALSE;
nm_clear_g_source_inst(&priv->ip_req_timeout_source_4);
nm_clear_g_source_inst(&priv->ip_req_timeout_source_6);
}
static void