diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 7a201552c0..033c8942c2 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -1792,7 +1792,10 @@ nm_l3_config_data_set_ip6_mtu(NML3ConfigData *self, guint32 ip6_mtu) NMSettingIP6ConfigPrivacy nm_l3_config_data_get_ip6_privacy(const NML3ConfigData *self) { - nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); + nm_assert(!self || _NM_IS_L3_CONFIG_DATA(self, TRUE)); + + if (!self) + return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; return self->ip6_privacy; } @@ -1894,7 +1897,7 @@ nm_l3_config_data_set_proxy_pac_script(NML3ConfigData *self, const char *value) gboolean nm_l3_config_data_get_ndisc_hop_limit(const NML3ConfigData *self, int *out_val) { - nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); if (!self->ndisc_hop_limit_set) { NM_SET_OUT(out_val, 0); @@ -1917,7 +1920,7 @@ nm_l3_config_data_set_ndisc_hop_limit(NML3ConfigData *self, int val) gboolean nm_l3_config_data_get_ndisc_reachable_time_msec(const NML3ConfigData *self, guint32 *out_val) { - nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); if (!self->ndisc_reachable_time_msec_set) { NM_SET_OUT(out_val, 0); @@ -1940,7 +1943,7 @@ nm_l3_config_data_set_ndisc_reachable_time_msec(NML3ConfigData *self, guint32 va gboolean nm_l3_config_data_get_ndisc_retrans_timer_msec(const NML3ConfigData *self, guint32 *out_val) { - nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); if (!self->ndisc_retrans_timer_msec_set) { NM_SET_OUT(out_val, 0); @@ -2230,7 +2233,7 @@ nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self, gboolea const NMPObject * my_addr_obj; NMDedupMultiIter iter; - nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); /* For IPv6 slaac, we explicitly add the device-routes (onlink). * As we don't do that for IPv4 and manual IPv6 addresses. Add them here diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 05022ee117..435d14dc1f 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -259,6 +259,25 @@ typedef struct _NML3CfgPrivate { gint8 commit_reentrant_count; + /* The value that was set before we touched the sysctl (this only is + * meaningful if "ip6_privacy_set" is true. At the end, we want to restore + * this value. */ + NMSettingIP6ConfigPrivacy ip6_privacy_initial : 4; + + /* The value that we set the last time. This is cached so that we don't + * repeatedly try to commit the same value. */ + NMSettingIP6ConfigPrivacy ip6_privacy_set_before : 4; + + guint32 ndisc_retrans_timer_msec; + guint32 ndisc_reachable_time_msec; + int ndisc_hop_limit; + + /* Whether "self" set the ip6_privacy sysctl (and whether it needs to be reset). */ + bool ip6_privacy_set : 1; + bool ndisc_reachable_time_msec_set : 1; + bool ndisc_retrans_timer_msec_set : 1; + bool ndisc_hop_limit_set : 1; + bool commit_type_update_sticky : 1; bool acd_is_pending : 1; @@ -3654,6 +3673,193 @@ out_prune: } /*****************************************************************************/ +static const char * +ip6_privacy_to_str(NMSettingIP6ConfigPrivacy ip6_privacy) +{ + switch (ip6_privacy) { + case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: + return "0"; + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: + return "1"; + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: + return "2"; + case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: + break; + } + return nm_assert_unreachable_val("0"); +} + +static void +_l3_commit_ndisc_params(NML3Cfg *self, NML3CfgCommitType commit_type) +{ + const NML3ConfigData *l3cd; + gboolean retrans_set = FALSE; + gboolean reachable_set = FALSE; + gboolean hop_limit_set = FALSE; + guint32 reachable; + guint32 retrans; + int hop_limit; + const char * ifname; + + if (commit_type < NM_L3_CFG_COMMIT_TYPE_UPDATE) { + self->priv.p->ndisc_reachable_time_msec_set = FALSE; + self->priv.p->ndisc_retrans_timer_msec_set = FALSE; + self->priv.p->ndisc_hop_limit_set = FALSE; + return; + } + + l3cd = self->priv.p->combined_l3cd_commited; + if (l3cd) { + reachable_set = nm_l3_config_data_get_ndisc_reachable_time_msec(l3cd, &reachable); + retrans_set = nm_l3_config_data_get_ndisc_retrans_timer_msec(l3cd, &retrans); + hop_limit = nm_l3_config_data_get_ndisc_hop_limit(l3cd, &hop_limit); + } + ifname = nm_l3cfg_get_ifname(self, TRUE); + + if (reachable_set + && (!self->priv.p->ndisc_reachable_time_msec_set + || self->priv.p->ndisc_reachable_time_msec != reachable)) { + self->priv.p->ndisc_reachable_time_msec = reachable; + self->priv.p->ndisc_reachable_time_msec_set = TRUE; + if (ifname) { + nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(self->priv.platform, + ifname, + reachable); + } + } + + if (retrans_set + && (!self->priv.p->ndisc_retrans_timer_msec_set + || self->priv.p->ndisc_reachable_time_msec != retrans)) { + self->priv.p->ndisc_retrans_timer_msec = retrans; + self->priv.p->ndisc_retrans_timer_msec_set = TRUE; + if (ifname) { + nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(self->priv.platform, ifname, retrans); + } + } + + if (hop_limit_set + && (!self->priv.p->ndisc_hop_limit_set || self->priv.p->ndisc_hop_limit != hop_limit)) { + self->priv.p->ndisc_hop_limit = hop_limit; + self->priv.p->ndisc_hop_limit_set = TRUE; + if (ifname) { + nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(self->priv.platform, + ifname, + hop_limit); + } + } + + // FIXME: restore values if necessary +} + +static void +_l3_commit_ip6_privacy(NML3Cfg *self, NML3CfgCommitType commit_type) +{ + NMSettingIP6ConfigPrivacy ip6_privacy; + NMSettingIP6ConfigPrivacy ip6_privacy_set_before; + const char * ifname; + + if (commit_type < NM_L3_CFG_COMMIT_TYPE_UPDATE) + ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; + else + ip6_privacy = nm_l3_config_data_get_ip6_privacy(self->priv.p->combined_l3cd_commited); + + if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) { + if (!self->priv.p->ip6_privacy_set) { + /* Nothing to set. But do we need to reset a previous value? */ + return; + } + self->priv.p->ip6_privacy_set = FALSE; + ip6_privacy = self->priv.p->ip6_privacy_initial; + ifname = nm_l3cfg_get_ifname(self, TRUE); + _LOGT("commit-ip6-privacy: reset initial value %d (was %d)%s%s", + (int) ip6_privacy, + (int) self->priv.p->ip6_privacy_set_before, + NM_PRINT_FMT_QUOTED2(ifname, ", ifname ", ifname, " (skip, no interface)")); + if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) + return; + if (!ifname) + return; + goto set; + } + + nm_assert(NM_IN_SET(ip6_privacy, + NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR)); + + if (self->priv.p->ip6_privacy_set && self->priv.p->ip6_privacy_set_before == ip6_privacy + && commit_type < NM_L3_CFG_COMMIT_TYPE_REAPPLY) { + /* Already set. We leave this alone except during reapply. */ + return; + } + + ip6_privacy_set_before = self->priv.p->ip6_privacy_set_before; + self->priv.p->ip6_privacy_set_before = ip6_privacy; + + if (!self->priv.p->ip6_privacy_set) { + gint64 s = G_MININT64; + + self->priv.p->ip6_privacy_set = TRUE; + ifname = nm_l3cfg_get_ifname(self, TRUE); + if (ifname) { + s = nm_platform_sysctl_ip_conf_get_int_checked(self->priv.platform, + AF_INET6, + ifname, + "use_tempaddr", + 10, + G_MININT32, + G_MAXINT32, + G_MININT64); + if (s != G_MININT64) + s = NM_CLAMP(s, 0, 2); + } + switch (s) { + case 0: + self->priv.p->ip6_privacy_initial = NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED; + break; + case 1: + self->priv.p->ip6_privacy_initial = NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR; + break; + case 2: + self->priv.p->ip6_privacy_initial = NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR; + break; + default: + nm_assert_not_reached(); + /* fall-through */ + case G_MININT64: + self->priv.p->ip6_privacy_initial = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; + break; + } + _LOGT("commit-ip6-privacy: set value %d (initial value was %d)%s%s", + (int) ip6_privacy, + (int) self->priv.p->ip6_privacy_initial, + NM_PRINT_FMT_QUOTED2(ifname, ", ifname ", ifname, " (skip, no interface)")); + if (!ifname) + return; + /* The first time, we always set the value, and don't skip it based on what we + * read. */ + goto set; + } + + ifname = nm_l3cfg_get_ifname(self, TRUE); + _LOGT("commit-ip6-privacy: set value %d (after %d, initial value was %d)%s%s", + (int) ip6_privacy, + (int) ip6_privacy_set_before, + (int) self->priv.p->ip6_privacy_initial, + NM_PRINT_FMT_QUOTED2(ifname, ", ifname ", ifname, " (skip, no interface)")); + if (!ifname) + return; + +set: + nm_assert(ifname); + self->priv.p->ip6_privacy_set_before = ip6_privacy; + nm_platform_sysctl_ip_conf_set(self->priv.platform, + AF_INET6, + ifname, + "use_tempaddr", + ip6_privacy_to_str(ip6_privacy)); +} static gboolean _l3_commit_one(NML3Cfg * self, @@ -3709,6 +3915,11 @@ _l3_commit_one(NML3Cfg * self, addr_family); } + if (!IS_IPv4) { + _l3_commit_ip6_privacy(self, commit_type); + _l3_commit_ndisc_params(self, commit_type); + } + if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL; @@ -3725,9 +3936,7 @@ _l3_commit_one(NML3Cfg * self, } else _obj_state_zombie_lst_get_prune_lists(self, addr_family, &addresses_prune, &routes_prune); - /* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ip6_privacy(). */ /* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ndisc_*(). */ - /* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ip6_mtu(). */ /* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_mtu(). */ nm_platform_ip_address_sync(self->priv.platform,