diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 2832982f3f..f39d7f84d7 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -954,6 +954,10 @@ ipv6.ip6-privacy=0 ipv4.forwarding 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). + + ipv4.clat + If left unspecified, defaults to "auto". + ipv4.routed-dns diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index de8edc8064..475871f3ad 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -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; } diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 92a91844c2..cd77eac3f1 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -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; diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index b1b2c5c711..750960bd35 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -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); diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 2533a44cfd..9a8e3809a7 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -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;