merge: branch 'bg/dhcpv4-ipv6-only'

Add support for the "IPv6-Only Preferred" DHCPv4 option

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2050

https://issues.redhat.com/browse/RHEL-58660
This commit is contained in:
Beniamino Galvani
2024-10-23 13:04:12 +00:00
22 changed files with 1046 additions and 592 deletions

4
NEWS
View File

@@ -15,6 +15,10 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* Remove support for building with Autotools
* Add IPVLAN interface support
* Allow to manually configure the authentication of LTE's initial EPS Bearer.
* Add support for the "IPv6-only preferred" DHCPv4 option (RFC 8925),
used to indicate that a host supports an IPv6-only mode and is
willing to forgo obtaining an IPv4 address if the network provides
IPv6 connectivity.
=============================================
NetworkManager-1.50

View File

@@ -950,6 +950,10 @@ ipv6.ip6-privacy=0
<term><varname>ipv4.dhcp-iaid</varname></term>
<listitem><para>If left unspecified, it defaults to "ifname".</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ipv4.dhcp-ipv6-only-preferred</varname></term>
<listitem><para>If left unspecified, the "IPv6-only preferred" DHCPv4 option is disabled.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ipv4.dhcp-hostname-flags</varname></term>
<listitem><para>If left unspecified, the value 3 (fqdn-encoded,fqdn-serv-update) is used.</para></listitem>

View File

@@ -615,6 +615,9 @@ typedef struct _NMDevicePrivate {
NMPacrunnerConfId *pacrunner_conf_id;
const char *ipv4_method;
const char *ipv6_method;
struct {
union {
const NMDeviceIPState state;
@@ -1824,6 +1827,29 @@ _prop_get_ipvx_may_fail_cached(NMDevice *self, int addr_family, NMTernary *cache
return _CACHED_BOOL(cache, _prop_get_ipvx_may_fail(self, addr_family));
}
static gboolean
_prop_get_ipv4_dhcp_ipv6_only_preferred(NMDevice *self)
{
NMSettingIP4Config *s_ip4;
NMSettingIP4DhcpIpv6OnlyPreferred ipv6_only;
s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG);
if (!s_ip4)
return FALSE;
ipv6_only = nm_setting_ip4_config_get_dhcp_ipv6_only_preferred(s_ip4);
if (ipv6_only != NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT)
return ipv6_only;
return nm_config_data_get_connection_default_int64(
NM_CONFIG_GET_DATA,
NM_CON_DEFAULT("ipv4.dhcp-ipv6-only-preferred"),
self,
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO,
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES,
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO);
}
/**
* _prop_get_ipvx_dhcp_iaid:
* @self: the #NMDevice
@@ -11260,7 +11286,8 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family)
gboolean hostname_is_fqdn;
gboolean send_client_id;
guint8 dscp;
gboolean dscp_explicit = FALSE;
gboolean dscp_explicit = FALSE;
gboolean ipv6_only_pref = FALSE;
client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr, &send_client_id);
dscp = _prop_get_ipv4_dhcp_dscp(self, &dscp_explicit);
@@ -11279,6 +11306,17 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family)
hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip);
}
if (_prop_get_ipv4_dhcp_ipv6_only_preferred(self)) {
if (nm_streq0(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) {
_LOGI_ipdhcp(
addr_family,
"not requesting the \"IPv6-only preferred\" option because IPv6 is disabled");
} else {
_LOGD_ipdhcp(addr_family, "requesting the \"IPv6-only preferred\" option");
ipv6_only_pref = TRUE;
}
}
config = (NMDhcpClientConfig) {
.addr_family = AF_INET,
.l3cfg = nm_device_get_l3cfg(self),
@@ -11299,11 +11337,12 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family)
.reject_servers = reject_servers,
.v4 =
{
.request_broadcast = request_broadcast,
.acd_timeout_msec = _prop_get_ipv4_dad_timeout(self),
.send_client_id = send_client_id,
.dscp = dscp,
.dscp_explicit = dscp_explicit,
.request_broadcast = request_broadcast,
.acd_timeout_msec = _prop_get_ipv4_dad_timeout(self),
.send_client_id = send_client_id,
.dscp = dscp,
.dscp_explicit = dscp_explicit,
.ipv6_only_preferred = ipv6_only_pref,
},
.previous_lease = priv->l3cds[L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4)].d,
};
@@ -12756,7 +12795,7 @@ get_ip_method_auto(NMDevice *self, int addr_family)
}
static void
activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const char *method)
activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family)
{
const int IS_IPv4 = NM_IS_IPv4(addr_family);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
@@ -12810,27 +12849,27 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const
if (priv->ipll_data_4.v4.mode == NM_SETTING_IP4_LL_ENABLED)
_dev_ipll4_start(self);
if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
_dev_ipdhcpx_start(self, AF_INET);
else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
/* pass */
} else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
} else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
_dev_ipshared4_start(self);
else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
priv->ip_data_x[IS_IPv4].is_disabled = TRUE;
else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
/* pass */
} else
nm_assert_not_reached();
}
if (!IS_IPv4) {
if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) {
if (nm_streq(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) {
if (!priv->ip_data_x[IS_IPv4].is_disabled) {
priv->ip_data_x[IS_IPv4].is_disabled = TRUE;
nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1");
}
} else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
} else if (nm_streq(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
if (!priv->ip_data_x[IS_IPv4].is_ignore) {
priv->ip_data_x[IS_IPv4].is_ignore = TRUE;
if (priv->controller) {
@@ -12863,15 +12902,15 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const
} else {
_dev_ipll6_start(self);
if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO))
if (NM_IN_STRSET(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO))
_dev_ipac6_start(self);
else if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_SHARED))
else if (NM_IN_STRSET(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_SHARED))
_dev_ipshared6_start(self);
else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
else if (nm_streq(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_MANAGED;
_dev_ipdhcpx_start(self, AF_INET6);
} else
nm_assert(NM_IN_STRSET(method,
nm_assert(NM_IN_STRSET(priv->ipv6_method,
NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL));
}
@@ -12972,8 +13011,6 @@ activate_stage3_ip_config(NMDevice *self)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self);
int ifindex;
const char *ipv4_method;
const char *ipv6_method;
/* stage3 is different from stage1+2.
*
@@ -13023,17 +13060,17 @@ activate_stage3_ip_config(NMDevice *self)
}
nm_assert(ifindex <= 0 || priv->fw_state == FIREWALL_STATE_INITIALIZED);
ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET);
if (nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
priv->ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET);
if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
/* "auto" usually means DHCPv4 or autoconf6, but it doesn't have to be. Subclasses
* can overwrite it. For example, you cannot run DHCPv4 on PPP/WireGuard links. */
ipv4_method = klass->get_ip_method_auto(self, AF_INET);
priv->ipv4_method = klass->get_ip_method_auto(self, AF_INET);
}
ipv6_method = nm_device_get_effective_ip_config_method(self, AF_INET6);
priv->ipv6_method = nm_device_get_effective_ip_config_method(self, AF_INET6);
if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) {
_NMLOG_ip((nm_device_managed_type_is_external(self)
|| NM_IN_STRSET(ipv6_method,
|| NM_IN_STRSET(priv->ipv6_method,
NM_SETTING_IP6_CONFIG_METHOD_AUTO,
NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
@@ -13041,9 +13078,9 @@ activate_stage3_ip_config(NMDevice *self)
: LOGL_WARN,
AF_INET6,
"IPv6 not supported by kernel resulting in \"ipv6.method=disabled\"");
ipv6_method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
} else if (nm_streq(ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
ipv6_method = klass->get_ip_method_auto(self, AF_INET6);
priv->ipv6_method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
} else if (nm_streq(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
priv->ipv6_method = klass->get_ip_method_auto(self, AF_INET6);
}
if (priv->ip_data_4.do_reapply) {
@@ -13052,7 +13089,7 @@ activate_stage3_ip_config(NMDevice *self)
_cleanup_ip_pre(self,
AF_INET,
CLEANUP_TYPE_KEEP_REAPPLY,
nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO));
nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO));
}
if (priv->ip_data_6.do_reapply) {
_LOGD_ip(AF_INET6, "reapply...");
@@ -13060,7 +13097,7 @@ activate_stage3_ip_config(NMDevice *self)
_cleanup_ip_pre(self,
AF_INET6,
CLEANUP_TYPE_KEEP_REAPPLY,
nm_streq(ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO));
nm_streq(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO));
}
if (priv->state < NM_DEVICE_STATE_IP_CONFIG) {
@@ -13091,7 +13128,7 @@ activate_stage3_ip_config(NMDevice *self)
if (!nm_device_managed_type_is_external(self)
&& (!klass->ready_for_ip_config || klass->ready_for_ip_config(self, TRUE))) {
if (priv->ipmanual_data.state_6 == NM_DEVICE_IP_STATE_NONE
&& !NM_IN_STRSET(ipv6_method,
&& !NM_IN_STRSET(priv->ipv6_method,
NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
/* Ensure the MTU makes sense. If it was below 1280 the kernel would not
@@ -13115,8 +13152,8 @@ activate_stage3_ip_config(NMDevice *self)
_dev_ipmanual_start(self);
}
activate_stage3_ip_config_for_addr_family(self, AF_INET, ipv4_method);
activate_stage3_ip_config_for_addr_family(self, AF_INET6, ipv6_method);
activate_stage3_ip_config_for_addr_family(self, AF_INET);
activate_stage3_ip_config_for_addr_family(self, AF_INET6);
}
void
@@ -16612,6 +16649,9 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
priv->promisc_reset = NM_OPTION_BOOL_DEFAULT;
}
priv->ipv4_method = NULL;
priv->ipv6_method = NULL;
_cleanup_generic_post(self, reason, cleanup_type);
}

View File

@@ -91,6 +91,11 @@ typedef struct _NMDhcpClientPrivate {
union {
struct {
/* Timer for restarting DHCP after the IPv6-only timeout */
GSource *ipv6_only_restart_source;
/* Minimum value accepted for the IPv6-only option. For test/debug only.*/
guint ipv6_only_min_wait;
struct {
NML3CfgCommitTypeHandle *l3cfg_commit_handle;
GSource *done_source;
@@ -1360,6 +1365,8 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error)
g_return_val_if_fail(priv->config.uuid, FALSE);
nm_assert(!priv->effective_client_id);
priv->is_stopped = FALSE;
IS_IPv4 = NM_IS_IPv4(priv->config.addr_family);
if (!IS_IPv4) {
@@ -1401,6 +1408,51 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error)
/*****************************************************************************/
static gboolean
ipv6_only_restart_timeout_cb(gpointer user_data)
{
NMDhcpClient *self = user_data;
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
gs_free_error GError *error = NULL;
nm_assert(priv->config.addr_family == AF_INET);
nm_clear_g_source_inst(&priv->v4.ipv6_only_restart_source);
if (!nm_dhcp_client_start(self, &error)) {
_LOGW("failed to restart the DHCP client after the IPv6-only timeout: %s", error->message);
_emit_notify(self,
NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
.it_looks_bad.reason = error->message);
}
return G_SOURCE_CONTINUE;
}
/**
* nm_dhcp_client_schedule_ipv6_only_restart():
* @self: the client
* @timeout: the raw value from the DHCP option
*
* Stops the DHCPv4 client and restarts it after the timeout announced
* by the "IPv6-Only preferred" option.
*/
void
nm_dhcp_client_schedule_ipv6_only_restart(NMDhcpClient *self, guint timeout)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
nm_assert(priv->config.addr_family == AF_INET);
nm_assert(!priv->is_stopped);
timeout = NM_MAX(priv->v4.ipv6_only_min_wait, timeout);
_LOGI("received option \"ipv6-only-preferred\": stopping DHCPv4 for %u seconds", timeout);
nm_dhcp_client_stop(self, FALSE);
nm_clear_g_source_inst(&priv->no_lease_timeout_source);
priv->v4.ipv6_only_restart_source =
nm_g_timeout_add_seconds_source(timeout, ipv6_only_restart_timeout_cb, self);
}
void
nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name)
{
@@ -1473,7 +1525,10 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release)
if (priv->is_stopped)
return;
nm_clear_pointer(&priv->effective_client_id, g_bytes_unref);
nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
if (priv->config.addr_family == AF_INET)
nm_clear_g_source_inst(&priv->v4.ipv6_only_restart_source);
priv->is_stopped = TRUE;
@@ -1919,6 +1974,8 @@ static void
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object);
const char *str;
guint min_wait;
switch (prop_id) {
case PROP_CONFIG:
@@ -1929,6 +1986,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
* explicitly initialize the respective union member. */
if (NM_IS_IPv4(priv->config.addr_family)) {
priv->v4 = (typeof(priv->v4)) {
.ipv6_only_min_wait = NM_DHCP_MIN_V6ONLY_WAIT_DEFAULT,
.acd =
{
.addr = INADDR_ANY,
@@ -1937,6 +1995,14 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
.done_source = NULL,
},
};
str = g_getenv("NM_TEST_IPV6_ONLY_MIN_WAIT");
if (str) {
min_wait = _nm_utils_ascii_str_to_int64(str, 10, 1, G_MAXUINT, 0);
if (min_wait != 0) {
priv->v4.ipv6_only_min_wait = min_wait;
}
}
} else {
priv->v6 = (typeof(priv->v6)) {
.lladdr_timeout_source = NULL,
@@ -1975,13 +2041,13 @@ dispose(GObject *object)
nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
nm_clear_g_source_inst(&priv->no_lease_timeout_source);
if (!NM_IS_IPv4(priv->config.addr_family)) {
if (priv->config.addr_family == AF_INET) {
nm_clear_g_source_inst(&priv->v4.ipv6_only_restart_source);
} else {
nm_clear_g_source_inst(&priv->v6.lladdr_timeout_source);
nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
}
nm_clear_pointer(&priv->effective_client_id, g_bytes_unref);
nm_assert(!priv->watch_source);
nm_assert(!priv->l3cd_next);
nm_assert(!priv->l3cd_curr);

View File

@@ -27,6 +27,8 @@
#define NM_DHCP_CLIENT_NOTIFY "dhcp-notify"
#define NM_DHCP_MIN_V6ONLY_WAIT_DEFAULT 300u /* (seconds). RFC 8925, section 3.4 */
typedef enum {
NM_DHCP_CLIENT_EVENT_TYPE_UNSPECIFIED,
@@ -172,6 +174,8 @@ typedef struct {
/* Whether to send or not the client identifier */
bool send_client_id : 1;
/* Request and honor the "IPv6-only Preferred" option (RFC 8925).*/
bool ipv6_only_preferred : 1;
} v4;
struct {
/* If set, the DUID from the connection is used; otherwise
@@ -246,6 +250,8 @@ const NML3ConfigData *nm_dhcp_client_get_lease(NMDhcpClient *self);
void nm_dhcp_client_stop(NMDhcpClient *self, gboolean release);
void nm_dhcp_client_schedule_ipv6_only_restart(NMDhcpClient *self, guint timeout);
/* Backend helpers for subclasses */
void nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name);

View File

@@ -462,6 +462,11 @@ dhclient_start(NMDhcpClient *client,
"to LOWDELAY (0x10).");
}
if (client_config->v4.ipv6_only_preferred) {
_LOGW("the dhclient backend does not support the \"IPv6-Only Preferred\" option; ignoring "
"it");
}
/* Usually the system bus address is well-known; but if it's supposed
* to be something else, we need to push it to dhclient, since dhclient
* sanitizes the environment it gives the action scripts.

View File

@@ -168,6 +168,27 @@ lease_option_consume_route(const uint8_t **datap,
/*****************************************************************************/
static gboolean
lease_get_ipv6_only_wait_time(NDhcp4ClientLease *lease, guint32 *out_val, const char *iface)
{
const uint8_t *data;
size_t len;
int r;
r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_IPV6_ONLY_PREFERRED, &data, &len);
if (r == 0
&& nm_dhcp_lease_data_parse_u32(data,
len,
out_val,
iface,
AF_INET,
NM_DHCP_OPTION_DHCP4_IPV6_ONLY_PREFERRED)) {
return TRUE;
}
return FALSE;
}
static gboolean
lease_parse_address(NMDhcpNettools *self /* for logging context only */,
NDhcp4ClientLease *lease,
@@ -929,6 +950,22 @@ bound4_handle(NMDhcpNettools *self, guint event, NDhcp4ClientLease *lease)
l3cd);
}
static gboolean
dhcp4_handle_ipv6_only(NMDhcpNettools *self, NDhcp4ClientEvent *event)
{
NMDhcpClient *client = NM_DHCP_CLIENT(self);
guint32 val;
if (nm_dhcp_client_get_config(client)->v4.ipv6_only_preferred
&& lease_get_ipv6_only_wait_time(event->offer.lease,
&val,
nm_dhcp_client_get_iface(client))) {
nm_dhcp_client_schedule_ipv6_only_restart(client, val);
return TRUE;
}
return FALSE;
}
static void
dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event)
{
@@ -962,18 +999,23 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event)
return;
}
n_dhcp4_client_lease_get_yiaddr(event->offer.lease, &yiaddr);
if (yiaddr.s_addr == INADDR_ANY) {
_LOGD("selecting lease failed: no yiaddr address");
return;
}
if (nm_dhcp_client_server_id_is_rejected(NM_DHCP_CLIENT(self), &server_id)) {
_LOGD("server-id %s is in the reject-list, ignoring",
nm_inet_ntop(AF_INET, &server_id, addr_str));
return;
}
if (dhcp4_handle_ipv6_only(self, event))
return;
/* Check yiaddr only after evaluating the ipv6-only-preferred option, because if
* the option is present yiaddr can be zero. */
n_dhcp4_client_lease_get_yiaddr(event->offer.lease, &yiaddr);
if (yiaddr.s_addr == INADDR_ANY) {
_LOGD("selecting lease failed: no yiaddr address");
return;
}
if (!_nm_dhcp_client_accept_offer(NM_DHCP_CLIENT(self), &yiaddr.s_addr)) {
/* We don't log about this, the parent class is expected to notify about the reasons. */
return;
@@ -1001,6 +1043,17 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event)
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
return;
case N_DHCP4_CLIENT_EVENT_GRANTED:
if (dhcp4_handle_ipv6_only(self, event)) {
/* RFC 8925 says that when the client receives a DHCPACK, it should
* stop the client; but only in the INIT-REBOOT (actually, REBOOTING)
* state, otherwise it should continue to use the address.
* The GRANTED event is emitted both in the REBOOTING and REQUESTING
* state; however if we got the IPv6-only option in the OFFER we have
* already stopped the client. Therefore this point can be reached
* only in the REBOOTING state.
*/
return;
}
bound4_handle(self, event->event, event->granted.lease);
return;
case N_DHCP4_CLIENT_EVENT_EXTENDED:
@@ -1323,7 +1376,7 @@ ip4_start(NMDhcpClient *client, GError **error)
g_return_val_if_fail(!priv->probe, FALSE);
g_return_val_if_fail(client_config, FALSE);
if (!nettools_create(self, &effective_client_id, error))
if (!priv->client && !nettools_create(self, &effective_client_id, error))
return FALSE;
r = n_dhcp4_client_probe_config_new(&config);
@@ -1377,6 +1430,11 @@ ip4_start(NMDhcpClient *client, GError **error)
}
}
if (client_config->v4.ipv6_only_preferred) {
n_dhcp4_client_probe_config_request_option(config,
NM_DHCP_OPTION_DHCP4_IPV6_ONLY_PREFERRED);
}
if (client_config->mud_url) {
r = n_dhcp4_client_probe_config_append_option(config,
NM_DHCP_OPTION_DHCP4_MUD_URL,

View File

@@ -113,6 +113,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = {
REQ(NM_DHCP_OPTION_DHCP4_PXE_CLIENT_ID, "pxe_client_id", FALSE),
REQ(NM_DHCP_OPTION_DHCP4_UAP_SERVERS, "uap_servers", FALSE),
REQ(NM_DHCP_OPTION_DHCP4_GEOCONF_CIVIC, "geoconf_civic", FALSE),
REQ(NM_DHCP_OPTION_DHCP4_IPV6_ONLY_PREFERRED, "ipv6_only_preferred", FALSE),
REQ(NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_ADDRESS, "netinfo_server_address", FALSE),
REQ(NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_TAG, "netinfo_server_tag", FALSE),
REQ(NM_DHCP_OPTION_DHCP4_DEFAULT_URL, "default_url", FALSE),
@@ -183,11 +184,11 @@ static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_
A(13), A(53), A(54), A(55), A(57), A(58), A(59), A(60), A(61), A(62), A(63), A(64),
A(65), A(66), A(67), A(68), A(69), A(70), A(71), A(72), A(73), A(74), A(75), A(76),
A(77), A(78), A(79), A(80), A(81), A(82), A(83), A(84), A(85), A(86), A(87), A(56),
A(88), A(89), A(90), A(91), A(92), A(93), A(14), A(7), A(94), A(95), A(96), A(97),
A(88), A(89), A(90), A(91), A(92), A(93), A(94), A(14), A(7), A(95), A(96), A(97),
A(98), A(99), A(100), A(101), A(102), A(103), A(104), A(105), A(106), A(107), A(108), A(109),
A(110), A(111), A(112), A(113), A(114), A(115), A(116), A(117), A(118), A(119), A(120), A(121),
A(122), A(123), A(124), A(125), A(126), A(127), A(128), A(129), A(130), A(131), A(132), A(133),
A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), A(142),
A(134), A(135), A(15), A(136), A(137), A(16), A(138), A(139), A(140), A(141), A(142), A(143),
#undef A
};

View File

@@ -93,6 +93,7 @@ typedef enum {
NM_DHCP_OPTION_DHCP4_UAP_SERVERS = 98,
NM_DHCP_OPTION_DHCP4_GEOCONF_CIVIC = 99,
NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE = 101,
NM_DHCP_OPTION_DHCP4_IPV6_ONLY_PREFERRED = 108,
NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_ADDRESS = 112,
NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_TAG = 113,
NM_DHCP_OPTION_DHCP4_DEFAULT_URL = 114,
@@ -188,7 +189,7 @@ typedef struct {
bool include;
} NMDhcpOption;
extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[143];
extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[144];
extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[18];
static inline const char *

View File

@@ -934,6 +934,27 @@ nm_dhcp_lease_data_parse_u16(const guint8 *data,
return TRUE;
}
gboolean
nm_dhcp_lease_data_parse_u32(const guint8 *data,
gsize n_data,
uint32_t *out_val,
const char *iface,
int addr_family,
guint option)
{
if (n_data != 4) {
nm_dhcp_lease_log_invalid_option(iface,
addr_family,
option,
"invalid option length %lu",
(unsigned long) n_data);
return FALSE;
}
*out_val = unaligned_read_be32(data);
return TRUE;
}
gboolean
nm_dhcp_lease_data_parse_mtu(const guint8 *data,
gsize n_data,

View File

@@ -74,6 +74,12 @@ gboolean nm_dhcp_lease_data_parse_u16(const guint8 *data,
const char *iface,
int addr_family,
guint option);
gboolean nm_dhcp_lease_data_parse_u32(const guint8 *data,
gsize n_data,
uint32_t *out_val,
const char *iface,
int addr_family,
guint option);
gboolean nm_dhcp_lease_data_parse_mtu(const guint8 *data,
gsize n_data,
guint16 *out_val,

View File

@@ -3588,13 +3588,24 @@ do_write_construct(NMConnection *connection,
} else
route_ignore = FALSE;
if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))
&& nm_setting_ip_config_get_dhcp_dscp(s_ip4)) {
set_error_unsupported(error,
connection,
NM_SETTING_IP4_CONFIG_SETTING_NAME "." NM_SETTING_IP_CONFIG_DHCP_DSCP,
FALSE);
return FALSE;
if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))) {
if (nm_setting_ip_config_get_dhcp_dscp(s_ip4)) {
set_error_unsupported(error,
connection,
NM_SETTING_IP4_CONFIG_SETTING_NAME
"." NM_SETTING_IP_CONFIG_DHCP_DSCP,
FALSE);
return FALSE;
}
if (nm_setting_ip4_config_get_dhcp_ipv6_only_preferred(NM_SETTING_IP4_CONFIG(s_ip4))
!= NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT) {
set_error_unsupported(error,
connection,
NM_SETTING_IP4_CONFIG_SETTING_NAME
"." NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED,
FALSE);
return FALSE;
}
}
write_ip4_setting(connection,

View File

@@ -2016,6 +2016,8 @@ global:
nm_device_ipvlan_get_private;
nm_device_ipvlan_get_type;
nm_device_ipvlan_get_vepa;
nm_setting_ip4_config_get_dhcp_ipv6_only_preferred;
nm_setting_ip4_dhcp_ipv6_only_preferred_get_type;
nm_setting_ipvlan_get_mode;
nm_setting_ipvlan_get_parent;
nm_setting_ipvlan_get_private;

View File

@@ -1648,6 +1648,10 @@
dbus-type="s"
gprop-type="gchararray"
/>
<property name="dhcp-ipv6-only-preferred"
dbus-type="i"
gprop-type="gint"
/>
<property name="dhcp-reject-servers"
dbus-type="as"
gprop-type="GStrv"

View File

@@ -39,7 +39,8 @@
NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DHCP_CLIENT_ID,
PROP_DHCP_FQDN,
PROP_DHCP_VENDOR_CLASS_IDENTIFIER,
PROP_LINK_LOCAL, );
PROP_LINK_LOCAL,
PROP_DHCP_IPV6_ONLY_PREFERRED, );
typedef struct {
NMSettingIPConfigPrivate parent;
@@ -48,6 +49,7 @@ typedef struct {
char *dhcp_fqdn;
char *dhcp_vendor_class_identifier;
gint32 link_local;
gint32 dhcp_ipv6_only_preferred;
} NMSettingIP4ConfigPrivate;
/**
@@ -146,6 +148,26 @@ nm_setting_ip4_config_get_link_local(NMSettingIP4Config *setting)
return NM_SETTING_IP4_CONFIG_GET_PRIVATE(setting)->link_local;
}
/**
* nm_setting_ip4_config_get_dhcp_ipv6_only_preferred:
* @setting: the #NMSettingIP4Config
*
* Returns the value in the #NMSettingIP4Config:dhcp-ipv6-only-preferred
* property.
*
* Returns: the DHCP IPv6-only preferred property value
*
* Since: 1.52
**/
NMSettingIP4DhcpIpv6OnlyPreferred
nm_setting_ip4_config_get_dhcp_ipv6_only_preferred(NMSettingIP4Config *setting)
{
g_return_val_if_fail(NM_IS_SETTING_IP4_CONFIG(setting),
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT);
return NM_SETTING_IP4_CONFIG_GET_PRIVATE(setting)->dhcp_ipv6_only_preferred;
}
static gboolean
verify(NMSetting *setting, NMConnection *connection, GError **error)
{
@@ -1315,6 +1337,38 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
* ---end---
*/
/**
* NMSettingIP4Config:dhcp-ipv6-only-preferred
*
* Controls the "IPv6-Only Preferred" DHCPv4 option (RFC 8925).
*
* When set to %NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES, the host adds the
* option to the parameter request list; if the DHCP server sends the option back,
* the host stops the DHCP client for the time interval specified in the option.
*
* Enable this feature if the host supports an IPv6-only mode, i.e. either all
* applications are IPv6-only capable or there is a form of 464XLAT deployed.
*
* When set to %NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT, the actual value
* is looked up in the global configuration; if not specified, it defaults to
* %NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO.
*
* If the connection has IPv6 method set to "disabled", this property does not
* have effect and the "IPv6-Only Preferred" option is always disabled.
*
* Since: 1.52
*/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED,
PROP_DHCP_IPV6_ONLY_PREFERRED,
NM_TYPE_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED,
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT,
NM_SETTING_PARAM_NONE,
NULL,
NMSettingIP4ConfigPrivate,
dhcp_ipv6_only_preferred);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit(setting_class,

View File

@@ -4088,6 +4088,7 @@ test_connection_diff_a_only(void)
{NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE, NM_SETTING_DIFF_RESULT_IN_A},

View File

@@ -32,6 +32,7 @@ G_BEGIN_DECLS
#define NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID "dhcp-client-id"
#define NM_SETTING_IP4_CONFIG_DHCP_FQDN "dhcp-fqdn"
#define NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER "dhcp-vendor-class-identifier"
#define NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED "dhcp-ipv6-only-preferred"
#define NM_SETTING_IP4_CONFIG_LINK_LOCAL "link-local"
/**
@@ -103,6 +104,23 @@ typedef enum {
NM_SETTING_IP4_LL_FALLBACK = 4,
} NMSettingIP4LinkLocal;
/**
* NMSettingIP4DhcpIpv6OnlyPreferred:
* @NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT: use the global default value
* @NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO: the option is disabled
* @NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES: the option is enabled
*
* #NMSettingIP4DhcpIpv6OnlyPreferred values specify if the "IPv6-Only Preferred"
* option (RFC 8925) for DHCPv4 is enabled.
*
* Since: 1.52
*/
typedef enum {
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT = -1,
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO = 0,
NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES = 1,
} NMSettingIP4DhcpIpv6OnlyPreferred;
typedef struct _NMSettingIP4ConfigClass NMSettingIP4ConfigClass;
GType nm_setting_ip4_config_get_type(void);
@@ -119,6 +137,10 @@ const char *nm_setting_ip4_config_get_dhcp_vendor_class_identifier(NMSettingIP4C
NM_AVAILABLE_IN_1_42
NMSettingIP4LinkLocal nm_setting_ip4_config_get_link_local(NMSettingIP4Config *setting);
NM_AVAILABLE_IN_1_52
NMSettingIP4DhcpIpv6OnlyPreferred
nm_setting_ip4_config_get_dhcp_ipv6_only_preferred(NMSettingIP4Config *setting);
G_END_DECLS
#endif /* __NM_SETTING_IP4_CONFIG_H__ */

View File

@@ -6422,6 +6422,9 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER,
.property_type = &_pt_gobject_string,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED,
.property_type = &_pt_gobject_enum,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_IP4_CONFIG_LINK_LOCAL,
.property_type = &_pt_gobject_enum,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (

View File

@@ -186,6 +186,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME_FLAGS N_("Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) and \"fqdn-no-update\" (0x4). When no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is not set, the standard FQDN flags are set in the request: \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) for IPv4 and \"fqdn-serv-update\" (0x1) for IPv6. When this property is set to the default value \"none\" (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also \"none\" (0x0), then the standard FQDN flags described above are sent in the DHCP requests.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_IAID N_("A string containing the \"Identity Association Identifier\" (IAID) used by the DHCP client. The string can be a 32-bit number (either decimal, hexadecimal or as colon separated hexadecimal numbers). Alternatively it can be set to the special values \"mac\", \"perm-mac\", \"ifname\" or \"stable\". When set to \"mac\" (or \"perm-mac\"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to \"ifname\", the IAID is computed by hashing the interface name. The special value \"stable\" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be \"ifname\". For DHCPv4, the IAID is only used with \"ipv4.dhcp-client-id\" values \"duid\" and \"ipv6-duid\" to generate the client-id. For DHCPv6, note that at the moment this property is only supported by the \"internal\" DHCPv6 plugin. The \"dhclient\" DHCPv6 plugin always derives the IAID from the MAC address. The actually used DHCPv6 IAID for a currently activated interface is exposed in the lease information of the device.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED N_("Controls the \"IPv6-Only Preferred\" DHCPv4 option (RFC 8925). When set to \"yes\" (1), the host adds the option to the parameter request list; if the DHCP server sends the option back, the host stops the DHCP client for the time interval specified in the option. Enable this feature if the host supports an IPv6-only mode, i.e. either all applications are IPv6-only capable or there is a form of 464XLAT deployed. When set to \"default\" (-1), the actual value is looked up in the global configuration; if not specified, it defaults to \"no\" (0). If the connection has IPv6 method set to \"disabled\", this property does not have effect and the \"IPv6-Only Preferred\" option is always disabled.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_RELEASE N_("Whether the DHCP client will send RELEASE message when bringing the connection down. The default value is \"default\" (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as FALSE.")

View File

@@ -1410,6 +1410,10 @@
<property name="dhcp-vendor-class-identifier"
nmcli-description="The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server."
format="string" />
<property name="dhcp-ipv6-only-preferred"
nmcli-description="Controls the &quot;IPv6-Only Preferred&quot; DHCPv4 option (RFC 8925). When set to &quot;yes&quot; (1), the host adds the option to the parameter request list; if the DHCP server sends the option back, the host stops the DHCP client for the time interval specified in the option. Enable this feature if the host supports an IPv6-only mode, i.e. either all applications are IPv6-only capable or there is a form of 464XLAT deployed. When set to &quot;default&quot; (-1), the actual value is looked up in the global configuration; if not specified, it defaults to &quot;no&quot; (0). If the connection has IPv6 method set to &quot;disabled&quot;, this property does not have effect and the &quot;IPv6-Only Preferred&quot; option is always disabled."
format="choice (NMSettingIP4DhcpIpv6OnlyPreferred)"
values="default (-1), no (0), yes (1)" />
<property name="link-local"
nmcli-description="Enable and disable the IPv4 link-local configuration independently of the ipv4.method configuration. This allows a link-local address (169.254.x.y/16) to be obtained in addition to other addresses, such as those manually configured or obtained from a DHCP server. When set to &quot;auto&quot;, the value is dependent on &quot;ipv4.method&quot;. When set to &quot;default&quot;, it honors the global connection default, before falling back to &quot;auto&quot;. Note that if &quot;ipv4.method&quot; is &quot;disabled&quot;, then link local addressing is always disabled too. The default is &quot;default&quot;. Since 1.52, when set to &quot;fallback&quot;, a link-local address is obtained if no other IPv4 address is set."
format="choice (NMSettingIP4LinkLocal)"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff