core: honor the ipv4.clat property

This commit is contained in:
Beniamino Galvani 2025-09-11 06:08:57 +02:00
parent 8ec0f1aa8d
commit 615c01f322
5 changed files with 117 additions and 4 deletions

View file

@ -954,6 +954,10 @@ ipv6.ip6-privacy=0
<term><varname>ipv4.forwarding</varname></term>
<listitem><para>Whether to configure IPv4 sysctl interface-specific forwarding. When enabled, the interface will act as a router to forward the IPv4 packet from one interface to another. If left unspecified, "auto" is used, so NetworkManager sets the IPv4 forwarding if any shared connection is active, or it will use the kernel default value otherwise. The "ipv4.forwarding" property is ignored when "ipv4.method" is set to "shared", because forwarding is always enabled in this case. The accepted values are: 0: disabled, 1: enabled, 2: auto, 3: ignored (leave the forwarding unchanged).</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ipv4.clat</varname></term>
<listitem><para>If left unspecified, defaults to "auto".</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ipv4.routed-dns</varname></term>
</varlistentry>

View file

@ -1523,6 +1523,41 @@ _prop_get_connection_dnssec(NMDevice *self, NMConnection *connection)
NM_SETTING_CONNECTION_DNSSEC_DEFAULT);
}
static NMSettingIp4ConfigClat
_prop_get_ipv4_clat(NMDevice *self, NMConnection *connection)
{
NMSettingIP4Config *s_ip4 = NULL;
NMSettingIp4ConfigClat clat;
const char *method;
if (connection)
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting_ip4_config(connection);
if (!s_ip4)
return NM_SETTING_IP4_CONFIG_CLAT_NO;
method = nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(s_ip4));
if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
return NM_SETTING_IP4_CONFIG_CLAT_NO;
clat = nm_setting_ip4_config_get_clat(s_ip4);
if (clat == NM_SETTING_IP4_CONFIG_CLAT_DEFAULT) {
clat = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
NM_CON_DEFAULT("ipv4.clat"),
self,
NM_SETTING_IP4_CONFIG_CLAT_NO,
NM_SETTING_IP4_CONFIG_CLAT_AUTO,
NM_SETTING_IP4_CONFIG_CLAT_NO);
}
if (clat == NM_SETTING_IP4_CONFIG_CLAT_AUTO
&& !nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
/* clat=auto enables CLAT only with method=auto */
clat = NM_SETTING_IP4_CONFIG_CLAT_NO;
}
return clat;
}
static NMMptcpFlags
_prop_get_connection_mptcp_flags(NMDevice *self, NMConnection *connection)
{
@ -3641,6 +3676,8 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co
nm_l3_config_data_set_dnssec(l3cd, _prop_get_connection_dnssec(self, connection));
nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self, connection));
nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self, connection));
nm_l3_config_data_set_clat(l3cd, _prop_get_ipv4_clat(self, connection));
return l3cd;
}

View file

@ -125,6 +125,7 @@ struct _NML3ConfigData {
NMSettingConnectionDnsOverTls dns_over_tls;
NMSettingConnectionDnssec dnssec;
NMUtilsIPv6IfaceId ip6_token;
NMSettingIp4ConfigClat clat;
NMRefString *network_id;
NML3ConfigDatFlags flags;
@ -525,6 +526,13 @@ nm_l3_config_data_log(const NML3ConfigData *self,
_L("nis-domain: %s", self->nis_domain->str);
}
if (!IS_IPv4) {
if (self->clat == NM_SETTING_IP4_CONFIG_CLAT_AUTO)
_L("clat: auto");
else if (self->clat == NM_SETTING_IP4_CONFIG_CLAT_YES)
_L("clat: yes");
}
if (!IS_IPv4 && self->pref64_valid) {
_L("pref64_prefix: %s/%d",
nm_utils_inet6_ntop(&self->pref64_prefix, sbuf_addr),
@ -725,6 +733,7 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc
.flags = NM_L3_CONFIG_DAT_FLAGS_NONE,
.metered = NM_TERNARY_DEFAULT,
.proxy_browser_only = NM_TERNARY_DEFAULT,
.clat = NM_SETTING_IP4_CONFIG_CLAT_NO,
.proxy_method = NM_PROXY_CONFIG_METHOD_UNKNOWN,
.route_table_sync_4 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
.route_table_sync_6 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
@ -1991,6 +2000,29 @@ nm_l3_config_data_set_network_id(NML3ConfigData *self, const char *value)
return nm_ref_string_reset_str(&self->network_id, value);
}
gboolean
nm_l3_config_data_set_clat(NML3ConfigData *self, NMSettingIp4ConfigClat val)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
nm_assert(NM_IN_SET(val,
NM_SETTING_IP4_CONFIG_CLAT_NO,
NM_SETTING_IP4_CONFIG_CLAT_YES,
NM_SETTING_IP4_CONFIG_CLAT_AUTO));
if (self->clat == val)
return FALSE;
self->clat = val;
return TRUE;
}
NMSettingIp4ConfigClat
nm_l3_config_data_get_clat(const NML3ConfigData *self)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
return self->clat;
}
gboolean
nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val)
{
@ -2591,6 +2623,8 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a,
NM_CMP_DIRECT_UNSAFE(a->routed_dns_4, b->routed_dns_4);
NM_CMP_DIRECT_UNSAFE(a->routed_dns_6, b->routed_dns_6);
NM_CMP_DIRECT_UNSAFE(a->clat, b->clat);
NM_CMP_DIRECT(!!a->pref64_valid, !!b->pref64_valid);
if (a->pref64_valid) {
NM_CMP_DIRECT(a->pref64_plen, b->pref64_plen);
@ -3662,6 +3696,14 @@ nm_l3_config_data_merge(NML3ConfigData *self,
if (src->routed_dns_6)
self->routed_dns_6 = TRUE;
if (self->clat == NM_SETTING_IP4_CONFIG_CLAT_NO) {
/* 'no' always loses to 'yes' and 'auto' */
self->clat = src->clat;
} else if (src->clat == NM_SETTING_IP4_CONFIG_CLAT_YES) {
/* 'yes' always takes precedence */
self->clat = src->clat;
}
if (src->pref64_valid) {
self->pref64_prefix = src->pref64_prefix;
self->pref64_plen = src->pref64_plen;

View file

@ -5,6 +5,7 @@
#include "libnm-glib-aux/nm-dedup-multi.h"
#include "nm-setting-connection.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
#include "libnm-platform/nm-platform.h"
#include "libnm-platform/nmp-object.h"
@ -498,6 +499,10 @@ gboolean nm_l3_config_data_set_network_id(NML3ConfigData *self, const char *netw
const char *nm_l3_config_data_get_network_id(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_clat(NML3ConfigData *self, NMSettingIp4ConfigClat val);
NMSettingIp4ConfigClat nm_l3_config_data_get_clat(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val);
gboolean nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self);

View file

@ -4159,8 +4159,11 @@ _l3cfg_update_combined_config(NML3Cfg *self,
gboolean merged_changed = FALSE;
gboolean commited_changed = FALSE;
#if HAVE_CLAT
struct in6_addr pref64;
guint32 pref64_plen;
struct in6_addr pref64;
guint32 pref64_plen;
gboolean clat_enabled = FALSE;
const NMPlatformIP4Route *ip4_route;
NMDedupMultiIter iter;
#endif
nm_assert(NM_IS_L3CFG(self));
@ -4260,10 +4263,32 @@ _l3cfg_update_combined_config(NML3Cfg *self,
}
#if HAVE_CLAT
if (nm_l3_config_data_get_pref64_valid(l3cd)) {
switch (nm_l3_config_data_get_clat(l3cd)) {
case NM_SETTING_IP4_CONFIG_CLAT_YES:
clat_enabled = TRUE;
break;
case NM_SETTING_IP4_CONFIG_CLAT_NO:
clat_enabled = FALSE;
break;
case NM_SETTING_IP4_CONFIG_CLAT_AUTO:
clat_enabled = TRUE;
/* disable if there is a native IPv4 gateway */
nm_l3_config_data_iter_ip4_route_for_each (&iter, l3cd, &ip4_route) {
if (ip4_route->network == INADDR_ANY && ip4_route->plen == 0
&& ip4_route->gateway != INADDR_ANY)
clat_enabled = FALSE;
break;
}
break;
case NM_SETTING_IP4_CONFIG_CLAT_DEFAULT:
nm_assert_not_reached();
clat_enabled = TRUE;
break;
}
if (clat_enabled && nm_l3_config_data_get_pref64_valid(l3cd)) {
NMPlatformIPXRoute rx;
NMIPAddrTyped best_v6_gateway;
NMDedupMultiIter iter;
const NMPlatformIP6Route *best_v6_route;
const NMPlatformIP6Address *ip6_entry;
struct in6_addr ip6;