nmcli: show the CLAT state

It is useful to show that the CLAT is enabled and which addresses and
prefix it is using. Add this information to the overview and to the
device/connection output. Example:

$ nmcli
  veth0: connected to clat
          "veth0"
          ethernet (veth), 4A:37:01:56:9D:AE, sw, mtu 1500
          ip4 default
          inet4 192.0.0.5/32
          route4 default metric 101
          inet6 2002:aaaa::64d4:2932:3585:7c89/64
          inet6 fe80::c060:8caf:f69b:e41a/64
          route6 fe80::/64 metric 1024
          route6 2002:aaaa::/64 metric 101
          route6 default via fe80::871:7ff:fe14:b7b9 metric 101
          clat inet4 192.0.0.5 inet6 2002:aaaa::2c0d:1e71:ef87:fac7 pref64 64:ff9b::/96

$ nmcli connection show clat
   ...
  IP4.ADDRESS[1]:                         192.0.0.5/32
  IP4.GATEWAY:                            0.0.0.0
  IP4.ROUTE[1]:                           dst = 0.0.0.0/0, nh = 0.0.0.0, mt = 101
  IP4.CLAT-ADDRESS:                       192.0.0.5
  IP6.ADDRESS[1]:                         2002:aaaa::64d4:2932:3585:7c89/64
  IP6.ADDRESS[2]:                         fe80::c060:8caf:f69b:e41a/64
  IP6.GATEWAY:                            fe80::871:7ff:fe14:b7b9
  IP6.ROUTE[1]:                           dst = fe80::/64, nh = ::, mt = 1024
  IP6.ROUTE[2]:                           dst = 2002:aaaa::/64, nh = ::, mt = 101
  IP6.ROUTE[3]:                           dst = ::/0, nh = fe80::871:7ff:fe14:b7b9, mt = 101
  IP6.CLAT-ADDRESS:                       2002:aaaa::2c0d:1e71:ef87:fac7
  IP6.CLAT-PREF64:                        64:ff9b::/96

Note how the IPv4 CLAT address is displayed both in IP4.ADDRESS and
IP4.CLAT-ADDRESS. That's because it is also configured in kernel. The
IPv6 CLAT address is not displayed in IP6.ADDRESS because it's not
configured in kernel.
This commit is contained in:
Beniamino Galvani 2026-01-29 18:58:43 +01:00
parent d1598a10ec
commit c86d234516
5 changed files with 481 additions and 256 deletions

View file

@ -156,6 +156,11 @@ _metagen_ip4_config_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS)
return NULL;
arrc = nm_ip_config_get_wins_servers(cfg4);
goto arrc_out;
case NMC_GENERIC_INFO_TYPE_IP4_CONFIG_CLAT_ADDRESS:
*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_HIDE;
str = nm_ip_config_get_clat_address(cfg4);
NM_SET_OUT(out_is_default, !str);
return str;
default:
break;
}
@ -184,6 +189,7 @@ const NmcMetaGenericInfo *const metagen_ip4_config[_NMC_GENERIC_INFO_TYPE_IP4_CO
_METAGEN_IP4_CONFIG(NMC_GENERIC_INFO_TYPE_IP4_CONFIG_DOMAIN, "DOMAIN"),
_METAGEN_IP4_CONFIG(NMC_GENERIC_INFO_TYPE_IP4_CONFIG_SEARCHES, "SEARCHES"),
_METAGEN_IP4_CONFIG(NMC_GENERIC_INFO_TYPE_IP4_CONFIG_WINS, "WINS"),
_METAGEN_IP4_CONFIG(NMC_GENERIC_INFO_TYPE_IP4_CONFIG_CLAT_ADDRESS, "CLAT-ADDRESS"),
};
/*****************************************************************************/
@ -245,6 +251,16 @@ _metagen_ip6_config_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS)
return NULL;
arrc = nm_ip_config_get_searches(cfg6);
goto arrc_out;
case NMC_GENERIC_INFO_TYPE_IP6_CONFIG_CLAT_ADDRESS:
*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_HIDE;
str = nm_ip_config_get_clat_address(cfg6);
NM_SET_OUT(out_is_default, !str);
return str;
case NMC_GENERIC_INFO_TYPE_IP6_CONFIG_CLAT_PREF64:
*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_HIDE;
str = nm_ip_config_get_clat_pref64(cfg6);
NM_SET_OUT(out_is_default, !str);
return str;
default:
break;
}
@ -272,6 +288,8 @@ const NmcMetaGenericInfo *const metagen_ip6_config[_NMC_GENERIC_INFO_TYPE_IP6_CO
_METAGEN_IP6_CONFIG(NMC_GENERIC_INFO_TYPE_IP6_CONFIG_DNS, "DNS"),
_METAGEN_IP6_CONFIG(NMC_GENERIC_INFO_TYPE_IP6_CONFIG_DOMAIN, "DOMAIN"),
_METAGEN_IP6_CONFIG(NMC_GENERIC_INFO_TYPE_IP6_CONFIG_SEARCHES, "SEARCHES"),
_METAGEN_IP6_CONFIG(NMC_GENERIC_INFO_TYPE_IP6_CONFIG_CLAT_ADDRESS, "CLAT-ADDRESS"),
_METAGEN_IP6_CONFIG(NMC_GENERIC_INFO_TYPE_IP6_CONFIG_CLAT_PREF64, "CLAT-PREF64"),
};
/*****************************************************************************/

View file

@ -1486,6 +1486,24 @@ ac_overview(NmCli *nmc, NMActiveConnection *ac)
}
}
}
{
NMIPConfig *ip4 = nm_active_connection_get_ip4_config(ac);
NMIPConfig *ip6 = nm_active_connection_get_ip6_config(ac);
const char *clat4;
const char *clat6;
const char *pref64;
if (ip4 && ip6) {
clat4 = nm_ip_config_get_clat_address(ip4);
clat6 = nm_ip_config_get_clat_address(ip6);
pref64 = nm_ip_config_get_clat_pref64(ip6);
if (clat4 && clat6 && pref64) {
nmc_print("\tclat inet4 %s inet6 %s pref64 %s\n", clat4, clat6, pref64);
}
}
}
}
void

View file

@ -111,6 +111,7 @@ typedef enum {
NMC_GENERIC_INFO_TYPE_IP4_CONFIG_DOMAIN,
NMC_GENERIC_INFO_TYPE_IP4_CONFIG_SEARCHES,
NMC_GENERIC_INFO_TYPE_IP4_CONFIG_WINS,
NMC_GENERIC_INFO_TYPE_IP4_CONFIG_CLAT_ADDRESS,
_NMC_GENERIC_INFO_TYPE_IP4_CONFIG_NUM,
NMC_GENERIC_INFO_TYPE_IP6_CONFIG_ADDRESS = 0,
@ -119,6 +120,8 @@ typedef enum {
NMC_GENERIC_INFO_TYPE_IP6_CONFIG_DNS,
NMC_GENERIC_INFO_TYPE_IP6_CONFIG_DOMAIN,
NMC_GENERIC_INFO_TYPE_IP6_CONFIG_SEARCHES,
NMC_GENERIC_INFO_TYPE_IP6_CONFIG_CLAT_ADDRESS,
NMC_GENERIC_INFO_TYPE_IP6_CONFIG_CLAT_PREF64,
_NMC_GENERIC_INFO_TYPE_IP6_CONFIG_NUM,
NMC_GENERIC_INFO_TYPE_DHCP_CONFIG_OPTION = 0,

File diff suppressed because it is too large Load diff

View file

@ -1046,6 +1046,18 @@ class Device(ExportedObj):
PRP_DEVICE_DHCP6_CONFIG,
ExportedObj.to_path(self.dhcp6_config),
)
if Util.random_int(self.path, 100) > 80:
self.ip4_config._dbus_property_set(
IFACE_IP4_CONFIG, PRP_IP4_CONFIG_CLAT_ADDRESS, "192.0.0.1"
)
self.ip6_config._dbus_property_set(
IFACE_IP6_CONFIG,
PRP_IP6_CONFIG_CLAT_ADDRESS,
"2002:aaaa::2c0d:1e71:ef87:fac7",
)
self.ip6_config._dbus_property_set(
IFACE_IP6_CONFIG, PRP_IP6_CONFIG_CLAT_PREF64, "64:ff9b::/96"
)
def stop(self):
self._dbus_property_set(
@ -2446,6 +2458,7 @@ PRP_IP4_CONFIG_SEARCHES = "Searches"
PRP_IP4_CONFIG_DNSOPTIONS = "DnsOptions"
PRP_IP4_CONFIG_DNSPRIORITY = "DnsPriority"
PRP_IP4_CONFIG_WINSSERVERS = "WinsServers"
PRP_IP4_CONFIG_CLAT_ADDRESS = "ClatAddress"
class IP4Config(ExportedObj):
@ -2624,6 +2637,7 @@ class IP4Config(ExportedObj):
PRP_IP4_CONFIG_WINSSERVERS: dbus.Array(
[dbus.UInt32(Util.ip4_addr_be32(n)) for n in winsservers], "u"
),
PRP_IP4_CONFIG_CLAT_ADDRESS: dbus.String(""),
}
def props_regenerate(self, generate_seed):
@ -2648,6 +2662,8 @@ PRP_IP6_CONFIG_DOMAINS = "Domains"
PRP_IP6_CONFIG_SEARCHES = "Searches"
PRP_IP6_CONFIG_DNSOPTIONS = "DnsOptions"
PRP_IP6_CONFIG_DNSPRIORITY = "DnsPriority"
PRP_IP6_CONFIG_CLAT_ADDRESS = "ClatAddress"
PRP_IP6_CONFIG_CLAT_PREF64 = "ClatPref64"
class IP6Config(ExportedObj):
@ -2814,6 +2830,8 @@ class IP6Config(ExportedObj):
PRP_IP6_CONFIG_SEARCHES: dbus.Array(searches, "s"),
PRP_IP6_CONFIG_DNSOPTIONS: dbus.Array(dnsoptions, "s"),
PRP_IP6_CONFIG_DNSPRIORITY: dbus.Int32(dnspriority),
PRP_IP6_CONFIG_CLAT_ADDRESS: dbus.String(""),
PRP_IP6_CONFIG_CLAT_PREF64: dbus.String(""),
}
def props_regenerate(self, generate_seed):