diff options
Diffstat (limited to 'src/core/dhcp')
| -rw-r--r-- | src/core/dhcp/nm-dhcp-client.c | 80 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-client.h | 7 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-dhclient.c | 5 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-dhcpcanon.c | 239 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-listener.c | 3 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-nettools.c | 82 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-options.c | 5 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-options.h | 3 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-systemd.c | 2 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-utils.c | 35 | ||||
| -rw-r--r-- | src/core/dhcp/nm-dhcp-utils.h | 6 | ||||
| -rw-r--r-- | src/core/dhcp/tests/test-dhcp-dhclient.c | 2 | ||||
| -rw-r--r-- | src/core/dhcp/tests/test-dhcp-utils.c | 12 |
13 files changed, 201 insertions, 280 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index cd6e67e2..18ad4024 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -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; @@ -336,7 +341,7 @@ _emit_notify_data(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data) #define _emit_notify(self, _notify_type, ...) \ _emit_notify_data( \ (self), \ - &((const NMDhcpClientNotifyData){.notify_type = (_notify_type), __VA_ARGS__})) + &((const NMDhcpClientNotifyData) {.notify_type = (_notify_type), __VA_ARGS__})) /*****************************************************************************/ @@ -684,7 +689,7 @@ _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state) now_msec = nm_utils_get_monotonic_timestamp_msec(); g_array_append_val(priv->v4.acd.reglist, - ((AcdRegListData){ + ((AcdRegListData) { .l3cd = nm_l3_config_data_ref(priv->l3cd_next), .addr = addr, .expiry_msec = now_msec + ACD_REGLIST_GRACE_PERIOD_MSEC, @@ -1375,6 +1380,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) { @@ -1416,6 +1423,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) { @@ -1488,7 +1540,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; @@ -1934,6 +1989,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: @@ -1943,7 +2000,8 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /* I know, this is technically not necessary. It just feels nicer to * explicitly initialize the respective union member. */ if (NM_IS_IPv4(priv->config.addr_family)) { - priv->v4 = (typeof(priv->v4)){ + priv->v4 = (typeof(priv->v4)) { + .ipv6_only_min_wait = NM_DHCP_MIN_V6ONLY_WAIT_DEFAULT, .acd = { .addr = INADDR_ANY, @@ -1952,8 +2010,16 @@ 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)){ + priv->v6 = (typeof(priv->v6)) { .lladdr_timeout_source = NULL, }; } @@ -1990,13 +2056,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); diff --git a/src/core/dhcp/nm-dhcp-client.h b/src/core/dhcp/nm-dhcp-client.h index 8c685faf..a7b6ae98 100644 --- a/src/core/dhcp/nm-dhcp-client.h +++ b/src/core/dhcp/nm-dhcp-client.h @@ -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, gboolean igno 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); @@ -305,7 +311,6 @@ typedef struct { GType nm_dhcp_nettools_get_type(void); -extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd; extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal; diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c index 043c2264..7e00599c 100644 --- a/src/core/dhcp/nm-dhcp-dhclient.c +++ b/src/core/dhcp/nm-dhcp-dhclient.c @@ -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. diff --git a/src/core/dhcp/nm-dhcp-dhcpcanon.c b/src/core/dhcp/nm-dhcp-dhcpcanon.c deleted file mode 100644 index cd42b692..00000000 --- a/src/core/dhcp/nm-dhcp-dhcpcanon.c +++ /dev/null @@ -1,239 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2017 juga <juga at riseup dot net> - */ - -#include "src/core/nm-default-daemon.h" - -#if WITH_DHCPCANON - -#include <stdlib.h> -#include <unistd.h> - -#include "nm-utils.h" -#include "nm-dhcp-manager.h" -#include "NetworkManagerUtils.h" -#include "nm-dhcp-listener.h" -#include "nm-dhcp-client-logging.h" - -#define NM_TYPE_DHCP_DHCPCANON (nm_dhcp_dhcpcanon_get_type()) -#define NM_DHCP_DHCPCANON(obj) \ - (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanon)) -#define NM_DHCP_DHCPCANON_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanonClass)) -#define NM_IS_DHCP_DHCPCANON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCPCANON)) -#define NM_IS_DHCP_DHCPCANON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP_DHCPCANON)) -#define NM_DHCP_DHCPCANON_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanonClass)) - -typedef struct _NMDhcpDhcpcanon NMDhcpDhcpcanon; -typedef struct _NMDhcpDhcpcanonClass NMDhcpDhcpcanonClass; - -static GType nm_dhcp_dhcpcanon_get_type(void); - -/*****************************************************************************/ - -typedef struct { - char *conf_file; - const char *def_leasefile; - char *lease_file; - char *pid_file; - NMDhcpListener *dhcp_listener; -} NMDhcpDhcpcanonPrivate; - -struct _NMDhcpDhcpcanon { - NMDhcpClient parent; - NMDhcpDhcpcanonPrivate _priv; -}; - -struct _NMDhcpDhcpcanonClass { - NMDhcpClientClass parent; -}; - -G_DEFINE_TYPE(NMDhcpDhcpcanon, nm_dhcp_dhcpcanon, NM_TYPE_DHCP_CLIENT) - -#define NM_DHCP_DHCPCANON_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMDhcpDhcpcanon, NM_IS_DHCP_DHCPCANON) - -/*****************************************************************************/ - -static const char * -nm_dhcp_dhcpcanon_get_path(void) -{ - return nm_utils_find_helper("dhcpcanon", DHCPCANON_PATH, NULL); -} - -static gboolean -dhcpcanon_start(NMDhcpClient *client, - const char *mode_opt, - GBytes *duid, - gboolean release, - pid_t *out_pid, - guint needed_prefixes, - GError **error) -{ - NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON(client); - NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE(self); - gs_unref_ptrarray GPtrArray *argv = NULL; - pid_t pid; - gs_free_error GError *local = NULL; - const char *iface; - const char *system_bus_address; - const char *dhcpcanon_path; - gs_free char *binary_name = NULL; - gs_free char *pid_file = NULL; - gs_free char *system_bus_address_env = NULL; - int addr_family; - - g_return_val_if_fail(!priv->pid_file, FALSE); - - iface = nm_dhcp_client_get_iface(client); - - addr_family = nm_dhcp_client_get_addr_family(client); - - dhcpcanon_path = nm_dhcp_dhcpcanon_get_path(); - if (!dhcpcanon_path) { - nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "dhcpcanon binary not found"); - return FALSE; - } - - _LOGD("dhcpcanon_path: %s", dhcpcanon_path); - - pid_file = g_strdup_printf(RUNSTATEDIR "/dhcpcanon%c-%s.pid", - nm_utils_addr_family_to_char(addr_family), - iface); - _LOGD("pid_file: %s", pid_file); - - /* Kill any existing dhcpcanon from the pidfile */ - binary_name = g_path_get_basename(dhcpcanon_path); - nm_dhcp_client_stop_existing(pid_file, binary_name); - - argv = g_ptr_array_new(); - g_ptr_array_add(argv, (gpointer) dhcpcanon_path); - - g_ptr_array_add(argv, (gpointer) "-sf"); /* Set script file */ - g_ptr_array_add(argv, (gpointer) nm_dhcp_helper_path); - - g_ptr_array_add(argv, (gpointer) "-pf"); /* Set pid file */ - g_ptr_array_add(argv, (gpointer) pid_file); - - if (priv->conf_file) { - g_ptr_array_add(argv, (gpointer) "-cf"); /* Set interface config file */ - g_ptr_array_add(argv, (gpointer) priv->conf_file); - } - - /* Usually the system bus address is well-known; but if it's supposed - * to be something else, we need to push it to dhcpcanon, since dhcpcanon - * sanitizes the environment it gives the action scripts. - */ - system_bus_address = getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (system_bus_address) { - system_bus_address_env = g_strdup_printf("DBUS_SYSTEM_BUS_ADDRESS=%s", system_bus_address); - g_ptr_array_add(argv, (gpointer) "-e"); - g_ptr_array_add(argv, (gpointer) system_bus_address_env); - } - - g_ptr_array_add(argv, (gpointer) iface); - g_ptr_array_add(argv, NULL); - - if (!g_spawn_async(NULL, - (char **) argv->pdata, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL - | G_SPAWN_STDERR_TO_DEV_NULL, - nm_utils_setpgid, - NULL, - &pid, - &local)) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "dhcpcanon failed to start: %s", - local->message); - return FALSE; - } - - nm_assert(pid > 0); - _LOGI("dhcpcanon started with pid %d", pid); - nm_dhcp_client_watch_child(client, pid); - priv->pid_file = g_steal_pointer(&pid_file); - return TRUE; -} - -static gboolean -ip4_start(NMDhcpClient *client, GError **error) -{ - return dhcpcanon_start(client, NULL, NULL, FALSE, NULL, 0, error); -} - -static void -stop(NMDhcpClient *client, gboolean release) -{ - NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON(client); - NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE(self); - int errsv; - - NM_DHCP_CLIENT_CLASS(nm_dhcp_dhcpcanon_parent_class)->stop(client, release); - - if (priv->pid_file) { - if (remove(priv->pid_file) == -1) { - errsv = errno; - _LOGD("could not remove dhcp pid file \"%s\": %d (%s)", - priv->pid_file, - errsv, - nm_strerror_native(errsv)); - } - g_free(priv->pid_file); - priv->pid_file = NULL; - } -} - -/*****************************************************************************/ - -static void -nm_dhcp_dhcpcanon_init(NMDhcpDhcpcanon *self) -{ - NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE(self); - - priv->dhcp_listener = g_object_ref(nm_dhcp_listener_get()); - g_signal_connect(priv->dhcp_listener, - NM_DHCP_LISTENER_EVENT, - G_CALLBACK(nm_dhcp_client_handle_event), - self); -} - -static void -dispose(GObject *object) -{ - NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE(object); - - if (priv->dhcp_listener) { - g_signal_handlers_disconnect_by_func(priv->dhcp_listener, - G_CALLBACK(nm_dhcp_client_handle_event), - NM_DHCP_DHCPCANON(object)); - g_clear_object(&priv->dhcp_listener); - } - - nm_clear_g_free(&priv->pid_file); - - G_OBJECT_CLASS(nm_dhcp_dhcpcanon_parent_class)->dispose(object); -} - -static void -nm_dhcp_dhcpcanon_class_init(NMDhcpDhcpcanonClass *dhcpcanon_class) -{ - NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS(dhcpcanon_class); - GObjectClass *object_class = G_OBJECT_CLASS(dhcpcanon_class); - - object_class->dispose = dispose; - - client_class->ip4_start = ip4_start; - client_class->stop = stop; -} - -const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = { - .name = "dhcpcanon", - .get_type_4 = nm_dhcp_dhcpcanon_get_type, - .get_path = nm_dhcp_dhcpcanon_get_path, -}; - -#endif /* WITH_DHCPCANON */ diff --git a/src/core/dhcp/nm-dhcp-listener.c b/src/core/dhcp/nm-dhcp-listener.c index 05e428f8..095131cc 100644 --- a/src/core/dhcp/nm-dhcp-listener.c +++ b/src/core/dhcp/nm-dhcp-listener.c @@ -31,9 +31,6 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6] = { * the first available plugin. */ &_nm_dhcp_client_factory_internal, -#if WITH_DHCPCANON - &_nm_dhcp_client_factory_dhcpcanon, -#endif #if WITH_DHCPCD &_nm_dhcp_client_factory_dhcpcd, #endif diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index f4f244c6..27bb136b 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -169,6 +169,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, NML3ConfigData *l3cd, @@ -305,7 +326,7 @@ lease_parse_address(NMDhcpNettools *self /* for logging context only */, } nm_l3_config_data_add_address_4(l3cd, - &((const NMPlatformIP4Address){ + &((const NMPlatformIP4Address) { .address = a_address.s_addr, .peer_address = a_address.s_addr, .plen = a_plen, @@ -366,7 +387,7 @@ lease_parse_address_list(NDhcp4ClientLease *lease, nm_inet4_ntop(addr, addr_str)); continue; } - nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &addr, NULL); + nm_l3_config_data_add_nameserver_addr(l3cd, AF_INET, &addr); break; case NM_DHCP_OPTION_DHCP4_NIS_SERVERS: nm_l3_config_data_add_nis_server(l3cd, addr); @@ -445,7 +466,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, m = 0; nm_l3_config_data_add_route_4(l3cd, - &((const NMPlatformIP4Route){ + &((const NMPlatformIP4Route) { .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = dest, .plen = plen, @@ -489,7 +510,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, } nm_l3_config_data_add_route_4(l3cd, - &((const NMPlatformIP4Route){ + &((const NMPlatformIP4Route) { .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = dest, .plen = plen, @@ -533,7 +554,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, m = default_route_metric_offset++; nm_l3_config_data_add_route_4(l3cd, - &((const NMPlatformIP4Route){ + &((const NMPlatformIP4Route) { .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .gateway = gateway, .pref_src = lease_address, @@ -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, diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c index f89237c5..ce03c607 100644 --- a/src/core/dhcp/nm-dhcp-options.c +++ b/src/core/dhcp/nm-dhcp-options.c @@ -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 }; diff --git a/src/core/dhcp/nm-dhcp-options.h b/src/core/dhcp/nm-dhcp-options.h index 1c61c74d..c8ab1dae 100644 --- a/src/core/dhcp/nm-dhcp-options.h +++ b/src/core/dhcp/nm-dhcp-options.h @@ -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 * diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index 5ede0df9..e1761523 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -157,7 +157,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro for (i = 0; i < num; i++) { nm_inet6_ntop(&dns[i], addr_str); g_string_append(nm_gstring_add_space_delimiter(str), addr_str); - nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &dns[i], NULL); + nm_l3_config_data_add_nameserver_addr(l3cd, AF_INET6, &dns[i]); } nm_dhcp_option_add_option(options, TRUE, diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index ca1c0482..15293fa3 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -92,7 +92,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, nm_l3_config_data_add_route_4( l3cd, - &((const NMPlatformIP4Route){ + &((const NMPlatformIP4Route) { .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = nm_ip4_addr_clear_host_address(rt_addr, rt_cidr), .plen = rt_cidr, @@ -147,7 +147,7 @@ process_dhclient_rfc3442_route(const char *const **p_octets, NMPlatformIP4Route if (inet_pton(AF_INET, next_hop, &tmp_addr) <= 0) return FALSE; - *route = (NMPlatformIP4Route){ + *route = (NMPlatformIP4Route) { .network = v_network, .plen = v_plen, .gateway = tmp_addr, @@ -316,7 +316,7 @@ process_classful_routes(const char *iface, // FIXME: ensure the IP address and route are sane - route = (NMPlatformIP4Route){ + route = (NMPlatformIP4Route) { .network = rt_addr, }; @@ -409,7 +409,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, now = nm_utils_get_monotonic_timestamp_sec(); - address = (NMPlatformIP4Address){ + address = (NMPlatformIP4Address) { .timestamp = now, }; @@ -499,7 +499,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, for (s = dns; dns && *s; s++) { if (inet_pton(AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { - nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &tmp_addr, NULL); + nm_l3_config_data_add_nameserver_addr(l3cd, AF_INET, &tmp_addr); _LOG2I(LOGD_DHCP4, iface, " nameserver '%s'", *s); } } else @@ -655,7 +655,7 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx, now = nm_utils_get_monotonic_timestamp_sec(); - address = (NMPlatformIP6Address){ + address = (NMPlatformIP6Address) { .plen = 128, .timestamp = now, }; @@ -704,7 +704,7 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx, for (s = dns; dns && *s; s++) { if (inet_pton(AF_INET6, *s, &tmp_addr) > 0) { if (!IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) { - nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &tmp_addr, NULL); + nm_l3_config_data_add_nameserver_addr(l3cd, AF_INET6, &tmp_addr); _LOG2I(LOGD_DHCP6, iface, " nameserver '%s'", *s); } } else @@ -935,6 +935,27 @@ nm_dhcp_lease_data_parse_u16(const guint8 *data, } 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, uint16_t *out_val, diff --git a/src/core/dhcp/nm-dhcp-utils.h b/src/core/dhcp/nm-dhcp-utils.h index 00199b02..99898524 100644 --- a/src/core/dhcp/nm-dhcp-utils.h +++ b/src/core/dhcp/nm-dhcp-utils.h @@ -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, diff --git a/src/core/dhcp/tests/test-dhcp-dhclient.c b/src/core/dhcp/tests/test-dhcp-dhclient.c index 0edcc296..6a7b7185 100644 --- a/src/core/dhcp/tests/test-dhcp-dhclient.c +++ b/src/core/dhcp/tests/test-dhcp-dhclient.c @@ -1023,7 +1023,7 @@ _check_duid_impl(const guint8 *duid_bin, g_assert_cmpint(contents_len, ==, strlen(contents)); } -#define _DUID(...) ((const guint8[]){__VA_ARGS__}) +#define _DUID(...) ((const guint8[]) {__VA_ARGS__}) #define _check_duid(duid, enforce_duid, old_content, new_content) \ _check_duid_impl((duid), sizeof(duid), (enforce_duid), (old_content), (new_content)) diff --git a/src/core/dhcp/tests/test-dhcp-utils.c b/src/core/dhcp/tests/test-dhcp-utils.c index 1c6d6302..b81523e1 100644 --- a/src/core/dhcp/tests/test-dhcp-utils.c +++ b/src/core/dhcp/tests/test-dhcp-utils.c @@ -194,16 +194,16 @@ test_parse_search_list(void) guint8 *data; char **domains; - data = (guint8[]){0x05, 'l', 'o', 'c', 'a', 'l', 0x00}; + data = (guint8[]) {0x05, 'l', 'o', 'c', 'a', 'l', 0x00}; domains = nm_dhcp_lease_data_parse_search_list(data, 7, NULL, 0, 0); g_assert(domains); g_assert_cmpint(g_strv_length(domains), ==, 1); g_assert_cmpstr(domains[0], ==, "local"); g_strfreev(domains); - data = (guint8[]){0x04, 't', 'e', 's', 't', 0x07, 'e', 'x', 'a', 'm', 'p', 'l', - 'e', 0x03, 'c', 'o', 'm', 0x00, 0xc0, 0x05, 0x03, 'a', 'b', 'c', - 0xc0, 0x0d, 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x00}; + data = (guint8[]) {0x04, 't', 'e', 's', 't', 0x07, 'e', 'x', 'a', 'm', 'p', 'l', + 'e', 0x03, 'c', 'o', 'm', 0x00, 0xc0, 0x05, 0x03, 'a', 'b', 'c', + 0xc0, 0x0d, 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x00}; domains = nm_dhcp_lease_data_parse_search_list(data, 34, NULL, 0, 0); g_assert(domains); g_assert_cmpint(g_strv_length(domains), ==, 4); @@ -213,7 +213,7 @@ test_parse_search_list(void) g_assert_cmpstr(domains[3], ==, "foobar"); g_strfreev(domains); - data = (guint8[]){ + data = (guint8[]) { 0x40, 'b', 'a', @@ -222,7 +222,7 @@ test_parse_search_list(void) domains = nm_dhcp_lease_data_parse_search_list(data, 4, NULL, 0, 0); g_assert(!domains); - data = (guint8[]){ + data = (guint8[]) { 0x04, 'o', 'k', |