all: drop dhclient code and build configuration

Drops the source code and build configurations used to maintain
dhclient support, which has been deprecated since NM 1.50 (over two
years ago).
This commit is contained in:
Jan Vaclav 2026-06-09 11:51:25 +02:00
parent 9618527fed
commit 449d6ff401
23 changed files with 7 additions and 3070 deletions

3
.gitignore vendored
View file

@ -249,7 +249,6 @@ test-*.trs
/src/core/devices/wifi/tests/test-devices-wifi
/src/core/devices/wwan/tests/test-service-providers
/src/core/dhcp/nm-dhcp-helper
/src/core/dhcp/tests/test-dhcp-dhclient
/src/core/dhcp/tests/test-dhcp-options
/src/core/dhcp/tests/test-dhcp-utils
/src/core/dnsmasq/tests/test-dnsmasq-utils
@ -427,11 +426,9 @@ test-*.trs
/src/devices/wifi/tests/test-wifi-ap-utils
/src/devices/wwan/tests/test-service-providers
/src/dhcp-manager/nm-dhcp-helper
/src/dhcp-manager/tests/test-dhcp-dhclient
/src/dhcp-manager/tests/test-dhcp-options
/src/dhcp-manager/tests/test-dhcp-utils
/src/dhcp/nm-dhcp-helper
/src/dhcp/tests/test-dhcp-dhclient
/src/dhcp/tests/test-dhcp-options
/src/dhcp/tests/test-dhcp-utils
/src/dnsmasq-manager/tests/test-dnsmasq-utils

View file

@ -61,9 +61,9 @@ variables:
# This is done by running `ci-fairy generate-template` and possibly bumping
# ".default_tag".
ALPINE_TAG: 'tag-9a076a2431b8'
CENTOS_TAG: 'tag-caf6673db1a7'
CENTOS_TAG: 'tag-e1774f6bed52'
DEBIAN_TAG: 'tag-e394e8e726e1'
FEDORA_TAG: 'tag-caf6673db1a7'
FEDORA_TAG: 'tag-e1774f6bed52'
UBUNTU_TAG: 'tag-e394e8e726e1'
ALPINE_EXEC: 'bash .gitlab-ci/alpine-install.sh'

2
NEWS
View file

@ -71,6 +71,8 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
secrets when activating a connection, matching the connection editor.
* Fix an out-of-bounds read in the internal DHCPv4 client that an on-link
attacker could trigger with a malformed UDP packet, crashing NetworkManager.
* Drop support for dhclient as a DHCP backend, which has been deprecated
since NetworkManager-1.50.
=============================================
NetworkManager-1.56

View file

@ -1,9 +1,6 @@
/* Define if building universal (internal helper macro) */
#mesondefine AC_APPLE_UNIVERSAL_BUILD
/* Define to path of dhclient binary */
#mesondefine DHCLIENT_PATH
/* Define to path of dhcpcd binary */
#mesondefine DHCPCD_PATH
@ -221,9 +218,6 @@
/* Define if you want connectivity checking support */
#mesondefine WITH_CONCHECK
/* Define if you have dhclient */
#mesondefine WITH_DHCLIENT
/* Define if you have dhcpcd */
#mesondefine WITH_DHCPCD

View file

@ -93,7 +93,6 @@ install \
# some packages don't exist in certain distributions. Install them one-by-one, and ignore errors.
install_ignore_missing \
black \
dhclient \
iproute-tc \
libasan \
libpsl-devel \

View file

@ -42,11 +42,6 @@ Release: __RELEASE_VERSION__%{?dist}
%global systemd_units_cloud_setup nm-cloud-setup.service nm-cloud-setup.timer
###############################################################################
%if 0%{?fedora} > 40 || 0%{?rhel} >= 10
%bcond_with dhclient
%else
%bcond_without dhclient
%endif
%bcond_without adsl
%bcond_without bluetooth
%bcond_without wwan
@ -585,11 +580,6 @@ Preferably use nmcli instead.
-Dnft=%{_sbindir}/nft \
-Diptables=%{_sbindir}/iptables \
-Dip6tables=%{_sbindir}/ip6tables \
%if %{with dhclient}
-Ddhclient=%{_sbindir}/dhclient \
%else
-Ddhclient=no \
%endif
-Ddhcpcd=no \
-Dcrypto=gnutls \
%if %{with debug}

View file

@ -218,7 +218,6 @@ if [[ $NO_DIST != 1 ]]; then
-Dlibaudit=yes-disabled-by-default \
-Dpolkit=true \
-Dnm_cloud_setup=true \
-Ddhclient=/usr/sbin/dhclient \
-Dconfig_dhcp_default=internal \
-Dconfig_dns_rc_manager_default=auto \
-Diptables=/usr/sbin/iptables \

View file

@ -263,11 +263,7 @@ if [ -z "$P_LOGGING_BACKEND_DEFAULT" ] ; then
fi
if [ -z "$P_DHCP_DEFAULT" ] ; then
if [ "$P_FEDORA" -ge 31 -o "$P_RHEL" -ge 8 ] ; then
P_DHCP_DEFAULT=internal
else
P_DHCP_DEFAULT=dhclient
fi
P_DHCP_DEFAULT=internal
fi
if [ -z "$P_FIREWALLD_ZONE" ] ; then
@ -377,7 +373,6 @@ meson setup\
-Dnft="${D_SBINDIR}/nft" \
-Diptables="${D_SBINDIR}/iptables" \
-Dip6tables="${D_SBINDIR}/ip6tables" \
-Ddhclient="${D_SBINDIR}/dhclient" \
-Ddhcpcd=no \
-Dconfig_dhcp_default="$P_DHCP_DEFAULT" \
"-Dcrypto=$P_CRYPTO" \

View file

@ -194,7 +194,6 @@ meson setup build \
-D ofono=true \
-D teamdctl=$_WITH_LIBTEAM \
\
-D dhclient=/bin/nowhere/dhclient \
-D dhcpcd=/bin/nowhere/dhcpd \
\
-D netconfig=/bin/nowhere/netconfig \

View file

@ -681,7 +681,7 @@ config_dhcp_default = get_option('config_dhcp_default')
config_h.set_quoted('NM_CONFIG_DEFAULT_MAIN_DHCP', config_dhcp_default)
config_dhcp_clients_enabled = [ 'internal' ]
dhcp_summary = ''
foreach client : [ 'dhcpcd', 'dhclient' ]
foreach client : [ 'dhcpcd' ]
client_path = get_option(client)
client_enable = (client_path != 'no')
if client_enable
@ -713,9 +713,6 @@ foreach client : [ 'dhcpcd', 'dhclient' ]
dhcp_summary += (' ' + client_path)
config_dhcp_clients_enabled += client
endif
if (client == 'dhclient')
dhcp_summary += ' (deprecated)'
endif
dhcp_summary += '\n'
endforeach

View file

@ -64,9 +64,8 @@ option('netconfig', type: 'string', value: '', description: 'Enable SUSE netconf
option('config_dns_rc_manager_default', type: 'combo', choices: ['auto', 'symlink', 'file', 'netconfig', 'resolvconf'], value: 'auto', description: 'Configure default value for main.rc-manager setting')
# dhcp clients
option('dhclient', type: 'string', value: 'no', description: 'Enable dhclient support (deprecated)')
option('dhcpcd', type: 'string', value: '', description: 'Enable dhcpcd support')
option('config_dhcp_default', type: 'combo', choices: ['dhclient', 'dhcpcd', 'internal', 'nettools'], value: 'internal', description: 'Default configuration option for main.dhcp setting, used as fallback if the configuration option is unset')
option('config_dhcp_default', type: 'combo', choices: ['dhcpcd', 'internal', 'nettools'], value: 'internal', description: 'Default configuration option for main.dhcp setting, used as fallback if the configuration option is unset')
# miscellaneous
option('introspection', type: 'boolean', value: true, description: 'Enable introspection for this build')

View file

@ -28,8 +28,6 @@ src/core/devices/wifi/nm-device-olpc-mesh.c
src/core/devices/wifi/nm-device-wifi.c
src/core/devices/wifi/nm-wifi-utils.c
src/core/devices/wwan/nm-modem-broadband.c
src/core/dhcp/nm-dhcp-dhclient-utils.c
src/core/dhcp/nm-dhcp-dhclient.c
src/core/dhcp/nm-dhcp-manager.c
src/core/dns/nm-dns-manager.c
src/core/main-utils.c

View file

@ -1,763 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011 Red Hat, Inc.
*/
#include "src/core/nm-default-daemon.h"
#include "nm-dhcp-dhclient-utils.h"
#include <ctype.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include "libnm-glib-aux/nm-dedup-multi.h"
#include "nm-dhcp-utils.h"
#include "nm-utils.h"
#include "libnm-platform/nm-platform.h"
#include "NetworkManagerUtils.h"
#define TIMEOUT_TAG "timeout "
#define RETRY_TAG "retry "
#define CLIENTID_TAG "send dhcp-client-identifier"
#define HOSTNAME4_TAG "send host-name"
#define HOSTNAME4_FORMAT HOSTNAME4_TAG " \"%s\"; # added by NetworkManager"
#define FQDN_TAG_PREFIX "send fqdn."
#define FQDN_TAG FQDN_TAG_PREFIX "fqdn"
#define FQDN_FORMAT FQDN_TAG " \"%s\"; # added by NetworkManager"
#define ALSOREQ_TAG "also request "
#define REQ_TAG "request "
#define MUDURLv4_DEF "option mudurl code 161 = text;\n"
#define MUDURLv4_FMT "send mudurl \"%s\";\n"
#define MUDURLv6_DEF "option dhcp6.mudurl code 112 = text;\n"
#define MUDURLv6_FMT "send dhcp6.mudurl \"%s\";\n"
static void
add_request(GPtrArray *array, const char *item)
{
guint i;
for (i = 0; i < array->len; i++) {
if (nm_streq(array->pdata[i], item))
return;
}
g_ptr_array_add(array, g_strdup(item));
}
static gboolean
grab_request_options(GPtrArray *store, const char *line)
{
gs_free const char **line_v = NULL;
gsize i;
/* Grab each 'request' or 'also request' option and save for later */
line_v = nm_strsplit_set(line, "\t ,");
for (i = 0; line_v && line_v[i]; i++) {
const char *ss = nm_str_skip_leading_spaces(line_v[i]);
gsize l;
gboolean end = FALSE;
if (!ss[0])
continue;
if (ss[0] == ';') {
/* all done */
return TRUE;
}
if (!g_ascii_isalnum(ss[0]))
continue;
l = strlen(ss);
while (l > 0 && g_ascii_isspace(ss[l - 1])) {
((char *) ss)[l - 1] = '\0';
l--;
}
if (l > 0 && ss[l - 1] == ';') {
/* Remove the EOL marker */
((char *) ss)[l - 1] = '\0';
end = TRUE;
}
if (ss[0])
add_request(store, ss);
if (end)
return TRUE;
}
return FALSE;
}
static void
add_ip4_config(GString *str,
GBytes *client_id,
const char *hostname,
gboolean use_fqdn,
NMDhcpHostnameFlags hostname_flags)
{
if (client_id) {
const char *p;
gsize l;
guint i;
p = g_bytes_get_data(client_id, &l);
nm_assert(p);
/* Allow type 0 (non-hardware address) to be represented as a string
* as long as all the characters are printable.
*/
for (i = 1; (p[0] == 0) && i < l; i++) {
if (!g_ascii_isprint(p[i]) || p[i] == '\\' || p[i] == '"')
break;
}
g_string_append(str, CLIENTID_TAG " ");
if (l == 0) {
/* An empty value effectively unsets the client-id to avoid sending it */
g_string_append(str, "\"\"");
} else if (i < l) {
/* Unprintable; convert to a hex string */
for (i = 0; i < l; i++) {
if (i > 0)
g_string_append_c(str, ':');
g_string_append_printf(str, "%02x", (guint8) p[i]);
}
} else {
/* Printable; just add to the line with type 0 */
g_string_append_c(str, '"');
g_string_append(str, "\\x00");
g_string_append_len(str, p + 1, l - 1);
g_string_append_c(str, '"');
}
g_string_append(str, "; # added by NetworkManager\n");
}
if (hostname) {
if (use_fqdn) {
g_string_append_printf(str, FQDN_FORMAT "\n", hostname);
g_string_append_printf(str,
FQDN_TAG_PREFIX "encoded %s;\n",
(hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED) ? "on"
: "off");
g_string_append_printf(
str,
FQDN_TAG_PREFIX "server-update %s;\n",
(hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE) ? "on" : "off");
g_string_append_printf(str,
FQDN_TAG_PREFIX "no-client-update %s;\n",
(hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE) ? "on"
: "off");
} else
g_string_append_printf(str, HOSTNAME4_FORMAT "\n", hostname);
}
g_string_append_c(str, '\n');
/* Define options for classless static routes */
g_string_append(
str,
"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n");
g_string_append(str,
"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n");
/* Web Proxy Auto-Discovery option (bgo #368423) */
g_string_append(str, "option wpad code 252 = string;\n");
g_string_append_c(str, '\n');
}
static void
add_hostname6(GString *str, const char *hostname, NMDhcpHostnameFlags hostname_flags)
{
if (hostname) {
g_string_append_printf(str, FQDN_FORMAT "\n", hostname);
if (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
g_string_append(str, FQDN_TAG_PREFIX "server-update on;\n");
if (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
g_string_append(str, FQDN_TAG_PREFIX "no-client-update on;\n");
g_string_append_c(str, '\n');
}
}
static void
add_mud_url_config(GString *str, const char *mud_url, int addr_family)
{
if (mud_url) {
if (addr_family == AF_INET) {
g_string_append(str, MUDURLv4_DEF);
g_string_append_printf(str, MUDURLv4_FMT, mud_url);
} else {
g_string_append(str, MUDURLv6_DEF);
g_string_append_printf(str, MUDURLv6_FMT, mud_url);
}
}
}
static GBytes *
read_client_id(const char *str)
{
gs_free char *s = NULL;
char *p;
int i = 0;
int j = 0;
gsize l;
nm_assert(NM_STR_HAS_PREFIX(str, CLIENTID_TAG));
str += NM_STRLEN(CLIENTID_TAG);
if (!g_ascii_isspace(*str))
return NULL;
while (g_ascii_isspace(*str))
str++;
if (*str == '"') {
/* Parse string literal with escape sequences */
s = g_strdup(str + 1);
p = strrchr(s, '"');
if (p)
*p = '\0';
else
return NULL;
if (!s[0])
return NULL;
while (s[i]) {
if (s[i] == '\\' && s[i + 1] == 'x' && g_ascii_isxdigit(s[i + 2])
&& g_ascii_isxdigit(s[i + 3])) {
s[j++] = (g_ascii_xdigit_value(s[i + 2]) << 4) + g_ascii_xdigit_value(s[i + 3]);
i += 4;
continue;
}
if (s[i] == '\\' && s[i + 1] >= '0' && s[i + 1] <= '7' && s[1 + 2] >= '0'
&& s[i + 2] <= '7' && s[1 + 3] >= '0' && s[i + 3] <= '7') {
s[j++] = ((s[i + 1] - '0') << 6) + ((s[i + 2] - '0') << 3) + (s[i + 3] - '0');
i += 4;
continue;
}
s[j++] = s[i++];
}
return g_bytes_new_take(g_steal_pointer(&s), j);
}
/* Otherwise, try to read a hexadecimal sequence */
s = g_strdup(str);
g_strchomp(s);
l = strlen(s);
if (l > 0 && s[l - 1] == ';')
s[l - 1] = '\0';
return nm_utils_hexstr2bin(s);
}
static gboolean
read_interface(const char *line, char *interface, guint size)
{
gs_free char *dup = g_strdup(line + NM_STRLEN("interface"));
char *ptr = dup, *end;
while (g_ascii_isspace(*ptr))
ptr++;
if (*ptr == '"') {
ptr++;
end = strchr(ptr, '"');
if (!end)
return FALSE;
*end = '\0';
} else {
end = strchr(ptr, ' ');
if (!end)
end = strchr(ptr, '{');
if (!end)
return FALSE;
*end = '\0';
}
if (ptr[0] == '\0' || strlen(ptr) + 1 > size)
return FALSE;
g_snprintf(interface, size, "%s", ptr);
return TRUE;
}
char *
nm_dhcp_dhclient_create_config(const char *interface,
int addr_family,
GBytes *client_id,
gboolean send_client_id,
const char *anycast_address,
const char *hostname,
guint32 timeout,
gboolean use_fqdn,
NMDhcpHostnameFlags hostname_flags,
const char *mud_url,
const char *const *reject_servers,
const char *orig_path,
const char *orig_contents,
GBytes **out_new_client_id)
{
nm_auto_free_gstring GString *new_contents = NULL;
gs_unref_ptrarray GPtrArray *fqdn_opts = NULL;
gs_unref_ptrarray GPtrArray *reqs = NULL;
gboolean reset_reqlist = FALSE;
int i;
g_return_val_if_fail(!anycast_address || nm_utils_hwaddr_valid(anycast_address, ETH_ALEN),
NULL);
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), NULL);
g_return_val_if_fail(!reject_servers || addr_family == AF_INET, NULL);
nm_assert(!out_new_client_id || !*out_new_client_id);
new_contents = g_string_new(_("# Created by NetworkManager\n"));
reqs = g_ptr_array_new_full(5, g_free);
if (orig_contents) {
gs_free const char **lines = NULL;
gsize line_i;
nm_auto_free_gstring GString *blocks_stack = NULL;
guint blocks_skip = 0;
gboolean in_alsoreq = FALSE;
gboolean in_req = FALSE;
char intf[IFNAMSIZ];
blocks_stack = g_string_new(NULL);
g_string_append_printf(new_contents, _("# Merged from %s\n\n"), orig_path);
intf[0] = '\0';
lines = nm_strsplit_set(orig_contents, "\n\r");
for (line_i = 0; lines && lines[line_i]; line_i++) {
const char *line = nm_str_skip_leading_spaces(lines[line_i]);
const char *p;
if (line[0] == '\0')
continue;
g_strchomp((char *) line);
p = line;
if (in_req) {
/* pass */
} else if (strchr(p, '{')) {
if (NM_STR_HAS_PREFIX(p, "lease") || NM_STR_HAS_PREFIX(p, "alias")
|| NM_STR_HAS_PREFIX(p, "interface") || NM_STR_HAS_PREFIX(p, "pseudo")) {
/* skip over these blocks, except 'interface' when it
* matches the current interface */
blocks_skip++;
g_string_append_c(blocks_stack, 'b');
if (!intf[0] && NM_STR_HAS_PREFIX(p, "interface")) {
if (read_interface(p, intf, sizeof(intf)))
continue;
}
} else {
/* allow other blocks (conditionals) */
if (!strchr(p, '}')) /* '} else {' */
g_string_append_c(blocks_stack, 'c');
}
} else if (strchr(p, '}')) {
if (blocks_stack->len > 0) {
if (blocks_stack->str[blocks_stack->len - 1] == 'b') {
g_string_truncate(blocks_stack, blocks_stack->len - 1);
nm_assert(blocks_skip > 0);
blocks_skip--;
intf[0] = '\0';
continue;
}
g_string_truncate(blocks_stack, blocks_stack->len - 1);
}
}
if (blocks_skip > 0 && !intf[0])
continue;
if (intf[0] && !nm_streq(intf, interface))
continue;
/* Some timing parameters in dhclient should not be imported (timeout, retry).
* The retry parameter will be simply not used as we will exit on first failure.
* The timeout one instead may affect NetworkManager behavior: if the timeout
* elapses before dhcp-timeout dhclient will report failure and cause NM to
* fail the dhcp process before dhcp-timeout. So, always skip importing timeout
* as we will need to add one greater than dhcp-timeout.
*/
if (NM_STR_HAS_PREFIX(p, TIMEOUT_TAG) || NM_STR_HAS_PREFIX(p, RETRY_TAG))
continue;
if (NM_STR_HAS_PREFIX(p, CLIENTID_TAG)) {
/* Skip "dhcp-client-id" if the connection has defined a custom one or "none" */
if (client_id || !send_client_id)
continue;
/* Otherwise, capture and return the existing client id */
if (out_new_client_id)
nm_clear_pointer(out_new_client_id, g_bytes_unref);
NM_SET_OUT(out_new_client_id, read_client_id(p));
/* fall-through. We keep the line... */
}
/* Override config file hostname and use one from the connection */
if (hostname) {
if (NM_STR_HAS_PREFIX(p, HOSTNAME4_TAG))
continue;
if (NM_STR_HAS_PREFIX(p, FQDN_TAG))
continue;
}
/* To let user's FQDN options (except "fqdn.fqdn") override the
* default ones set by NM, add them later
*/
if (NM_STR_HAS_PREFIX(p, FQDN_TAG_PREFIX)) {
if (!fqdn_opts)
fqdn_opts = g_ptr_array_new_full(5, g_free);
g_ptr_array_add(fqdn_opts, g_strdup(p + NM_STRLEN(FQDN_TAG_PREFIX)));
continue;
}
/* Ignore 'script' since we pass our own */
if (g_str_has_prefix(p, "script "))
continue;
/* Check for "request" */
if (NM_STR_HAS_PREFIX(p, REQ_TAG)) {
in_req = TRUE;
p += NM_STRLEN(REQ_TAG);
g_ptr_array_set_size(reqs, 0);
reset_reqlist = TRUE;
}
/* Save all request options for later use */
if (in_req) {
in_req = !grab_request_options(reqs, p);
continue;
}
/* Check for "also require" */
if (NM_STR_HAS_PREFIX(p, ALSOREQ_TAG)) {
in_alsoreq = TRUE;
p += NM_STRLEN(ALSOREQ_TAG);
}
if (in_alsoreq) {
in_alsoreq = !grab_request_options(reqs, p);
continue;
}
/* Existing configuration line is OK, add it to new configuration */
g_string_append(new_contents, line);
g_string_append_c(new_contents, '\n');
}
} else
g_string_append_c(new_contents, '\n');
/* ensure dhclient timeout is greater than dhcp-timeout: as dhclient timeout default value is
* 60 seconds, we need this only if dhcp-timeout is greater than 60.
*/
if (timeout >= 60) {
timeout = timeout < G_MAXINT32 ? timeout + 1 : G_MAXINT32;
g_string_append_printf(new_contents, "timeout %u;\n", timeout);
}
add_mud_url_config(new_contents, mud_url, addr_family);
if (reject_servers && reject_servers[0]) {
g_string_append(new_contents, "reject ");
for (i = 0; reject_servers[i]; i++) {
if (i != 0)
g_string_append(new_contents, ", ");
g_string_append(new_contents, reject_servers[i]);
}
g_string_append(new_contents, ";\n");
}
if (addr_family == AF_INET) {
nm_auto_unref_bytes GBytes *client_id_none = NULL;
client_id = send_client_id ? client_id : (client_id_none = g_bytes_new_static("", 0));
add_ip4_config(new_contents, client_id, hostname, use_fqdn, hostname_flags);
add_request(reqs, "rfc3442-classless-static-routes");
add_request(reqs, "ms-classless-static-routes");
add_request(reqs, "static-routes");
add_request(reqs, "wpad");
add_request(reqs, "ntp-servers");
add_request(reqs, "root-path");
} else {
add_hostname6(new_contents, hostname, hostname_flags);
add_request(reqs, "dhcp6.name-servers");
add_request(reqs, "dhcp6.domain-search");
/* FIXME: internal client does not support requesting client-id option. Does this even work? */
add_request(reqs, "dhcp6.client-id");
}
if (reset_reqlist)
g_string_append(new_contents, "request; # override dhclient defaults\n");
/* And add it to the dhclient configuration */
for (i = 0; i < reqs->len; i++)
g_string_append_printf(new_contents, "also request %s;\n", (char *) reqs->pdata[i]);
if (fqdn_opts) {
for (i = 0; i < fqdn_opts->len; i++) {
const char *t = fqdn_opts->pdata[i];
if (i == 0)
g_string_append_printf(new_contents, "\n# FQDN options from %s\n", orig_path);
g_string_append_printf(new_contents, FQDN_TAG_PREFIX "%s\n", t);
}
}
g_string_append_c(new_contents, '\n');
if (anycast_address) {
g_string_append_printf(new_contents,
"interface \"%s\" {\n"
" initial-interval 1; \n"
" anycast-mac ethernet %s;\n"
"}\n",
interface,
anycast_address);
}
return g_string_free(g_steal_pointer(&new_contents), FALSE);
}
/* In the lease file, dhclient will write "option dhcp6.client-id $HEXSTR". This
* function does the same. */
static char *
nm_dhcp_dhclient_escape_duid_as_hex(GBytes *duid)
{
const guint8 *s;
gsize len;
nm_assert(duid);
s = g_bytes_get_data(duid, &len);
return nm_utils_bin2hexstr_fuller(s, len, ':', FALSE, FALSE, NULL);
}
/* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */
char *
nm_dhcp_dhclient_escape_duid(GBytes *duid)
{
char *escaped;
const guint8 *s, *s0;
gsize len;
char *d;
g_return_val_if_fail(duid, NULL);
s0 = g_bytes_get_data(duid, &len);
s = s0;
d = escaped = g_malloc((len * 4) + 1);
while (s < (s0 + len)) {
if (!g_ascii_isprint(*s)) {
*d++ = '\\';
*d++ = '0' + ((*s >> 6) & 0x7);
*d++ = '0' + ((*s >> 3) & 0x7);
*d++ = '0' + (*s++ & 0x7);
} else if (*s == '"' || *s == '\'' || *s == '$' || *s == '`' || *s == '\\' || *s == '|'
|| *s == '&') {
*d++ = '\\';
*d++ = *s++;
} else
*d++ = *s++;
}
*d++ = '\0';
return escaped;
}
static gboolean
isoctal(const guint8 *p)
{
return (p[0] >= '0' && p[0] <= '3' && p[1] >= '0' && p[1] <= '7' && p[2] >= '0' && p[2] <= '7');
}
GBytes *
nm_dhcp_dhclient_unescape_duid(const char *duid)
{
GByteArray *unescaped;
const guint8 *p = (const guint8 *) duid;
guint i, len;
guint8 octal;
/* FIXME: it's wrong to have an "unescape-duid" function. dhclient
* defines a file format with escaping. So we need a general unescape
* function that can handle dhclient syntax. */
len = strlen(duid);
unescaped = g_byte_array_sized_new(len);
for (i = 0; i < len; i++) {
if (p[i] == '\\') {
i++;
if (isdigit(p[i])) {
/* Octal escape sequence */
if (i + 2 >= len || !isoctal(p + i))
goto error;
octal = ((p[i] - '0') << 6) + ((p[i + 1] - '0') << 3) + (p[i + 2] - '0');
g_byte_array_append(unescaped, &octal, 1);
i += 2;
} else {
/* FIXME: don't warn on untrusted data. Either signal an error, or accept
* it silently. */
/* One of ", ', $, `, \, |, or & */
g_warn_if_fail(p[i] == '"' || p[i] == '\'' || p[i] == '$' || p[i] == '`'
|| p[i] == '\\' || p[i] == '|' || p[i] == '&');
g_byte_array_append(unescaped, &p[i], 1);
}
} else
g_byte_array_append(unescaped, &p[i], 1);
}
return g_byte_array_free_to_bytes(unescaped);
error:
g_byte_array_free(unescaped, TRUE);
return NULL;
}
#define DEFAULT_DUID_PREFIX "default-duid \""
/* Beware: @error may be unset even if the function returns %NULL. */
GBytes *
nm_dhcp_dhclient_read_duid(const char *leasefile, GError **error)
{
gs_free char *contents = NULL;
gs_free const char **contents_v = NULL;
gsize i;
if (!g_file_test(leasefile, G_FILE_TEST_EXISTS))
return NULL;
if (!g_file_get_contents(leasefile, &contents, NULL, error))
return NULL;
contents_v = nm_strsplit_set(contents, "\n\r");
for (i = 0; contents_v && contents_v[i]; i++) {
const char *p = nm_str_skip_leading_spaces(contents_v[i]);
GBytes *duid;
if (!NM_STR_HAS_PREFIX(p, DEFAULT_DUID_PREFIX))
continue;
p += NM_STRLEN(DEFAULT_DUID_PREFIX);
g_strchomp((char *) p);
if (!NM_STR_HAS_SUFFIX(p, "\";"))
continue;
((char *) p)[strlen(p) - 2] = '\0';
duid = nm_dhcp_dhclient_unescape_duid(p);
if (duid)
return duid;
}
return NULL;
}
gboolean
nm_dhcp_dhclient_save_duid(const char *leasefile,
GBytes *duid,
gboolean enforce_duid,
GError **error)
{
gs_free char *escaped_duid = NULL;
gs_free const char **lines = NULL;
nm_auto_free_gstring GString *s = NULL;
const char *const *iter;
gs_free char *conflicting_duid_line = NULL;
gs_free char *contents = NULL;
gsize contents_len = 0;
g_return_val_if_fail(leasefile != NULL, FALSE);
if (!duid) {
nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "missing duid");
g_return_val_if_reached(FALSE);
}
escaped_duid = nm_dhcp_dhclient_escape_duid(duid);
nm_assert(escaped_duid);
if (g_file_test(leasefile, G_FILE_TEST_EXISTS)) {
if (!g_file_get_contents(leasefile, &contents, &contents_len, error)) {
g_prefix_error(error, "failed to read lease file %s: ", leasefile);
return FALSE;
}
lines = nm_strsplit_set_with_empty(contents, "\n");
}
s = g_string_sized_new(contents_len + 50);
g_string_append_printf(s, DEFAULT_DUID_PREFIX "%s\";\n", escaped_duid);
/* Preserve existing leasefile contents */
if (lines) {
for (iter = lines; *iter; iter++) {
const char *str = *iter;
const char *l;
gboolean ends_with_r;
gsize l_len;
gsize prefix_len;
l = nm_str_skip_leading_spaces(str);
l_len = strlen(l);
prefix_len = l - str;
ends_with_r = l_len > 0 && l[l_len - 1u] == '\r';
if (ends_with_r) {
((char *) l)[--l_len] = '\0';
}
if (NM_STR_HAS_PREFIX(l, DEFAULT_DUID_PREFIX)) {
/* We always add our line on top. This line can be skipped. */
continue;
}
if (enforce_duid & NM_STR_HAS_PREFIX(l, "option dhcp6.client-id ")) {
/* we want to use our duid. Skip the per-lease client-id. */
if (!conflicting_duid_line) {
gs_free char *duid_hex = nm_dhcp_dhclient_escape_duid_as_hex(duid);
conflicting_duid_line = g_strdup_printf("option dhcp6.client-id %s;", duid_hex);
}
/* We adjust the duid line and set what we want. */
l = conflicting_duid_line;
}
g_string_append_len(s, str, prefix_len);
g_string_append(s, l);
if (ends_with_r) {
g_string_append_c(s, '\r');
g_string_append_c(s, '\n');
} else if ((iter[1]) != NULL) {
/* avoid to add an extra '\n' at the end of file */
g_string_append_c(s, '\n');
}
}
}
if (contents && strlen(contents) == contents_len && nm_streq(contents, s->str)) {
/* The file is already as we want it. We are done. */
return TRUE;
}
if (!g_file_set_contents(leasefile, s->str, -1, error)) {
g_prefix_error(error, "failed to set DUID in lease file %s: ", leasefile);
return FALSE;
}
return TRUE;
}

View file

@ -1,38 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2010 Red Hat, Inc.
*/
#ifndef __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__
#define __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
char *nm_dhcp_dhclient_create_config(const char *interface,
int addr_family,
GBytes *client_id,
gboolean send_client_id,
const char *anycast_addr,
const char *hostname,
guint32 timeout,
gboolean use_fqdn,
NMDhcpHostnameFlags hostname_flags,
const char *mud_url,
const char *const *reject_servers,
const char *orig_path,
const char *orig_contents,
GBytes **out_new_client_id);
char *nm_dhcp_dhclient_escape_duid(GBytes *duid);
GBytes *nm_dhcp_dhclient_unescape_duid(const char *duid);
GBytes *nm_dhcp_dhclient_read_duid(const char *leasefile, GError **error);
gboolean nm_dhcp_dhclient_save_duid(const char *leasefile,
GBytes *duid,
gboolean enforce_duid,
GError **error);
#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */

View file

@ -1,741 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2005 - 2012 Red Hat, Inc.
*/
#include <config.h>
#define __CONFIG_H__
#define _XOPEN_SOURCE
#include <time.h>
#undef _XOPEN_SOURCE
#include "src/core/nm-default-daemon.h"
#if WITH_DHCLIENT
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "libnm-glib-aux/nm-dedup-multi.h"
#include "nm-utils.h"
#include "nm-dhcp-dhclient-utils.h"
#include "nm-dhcp-manager.h"
#include "NetworkManagerUtils.h"
#include "nm-dhcp-listener.h"
#include "nm-dhcp-client-logging.h"
/*****************************************************************************/
static const char *
_addr_family_to_path_part(int addr_family)
{
nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
return (addr_family == AF_INET6) ? "6" : "";
}
/*****************************************************************************/
#define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type())
#define NM_DHCP_DHCLIENT(obj) \
(_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient))
#define NM_DHCP_DHCLIENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
#define NM_IS_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCLIENT))
#define NM_IS_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP_DHCLIENT))
#define NM_DHCP_DHCLIENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
typedef struct _NMDhcpDhclient NMDhcpDhclient;
typedef struct _NMDhcpDhclientClass NMDhcpDhclientClass;
static GType nm_dhcp_dhclient_get_type(void);
/*****************************************************************************/
typedef struct {
char *conf_file;
const char *def_leasefile;
char *lease_file;
char *pid_file;
NMDhcpListener *dhcp_listener;
} NMDhcpDhclientPrivate;
struct _NMDhcpDhclient {
NMDhcpClient parent;
NMDhcpDhclientPrivate _priv;
};
struct _NMDhcpDhclientClass {
NMDhcpClientClass parent;
};
G_DEFINE_TYPE(NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
#define NM_DHCP_DHCLIENT_GET_PRIVATE(self) \
_NM_GET_PRIVATE(self, NMDhcpDhclient, NM_IS_DHCP_DHCLIENT)
/*****************************************************************************/
static GBytes *read_duid_from_lease(NMDhcpDhclient *self);
/*****************************************************************************/
static const char *
nm_dhcp_dhclient_get_path(void)
{
return nm_utils_find_helper("dhclient", DHCLIENT_PATH, NULL);
}
/**
* get_dhclient_leasefile():
* @addr_family: AF_INET or AF_INET6
* @iface: the interface name of the device on which DHCP will be done
* @uuid: the connection UUID to which the returned lease should belong
* @out_preferred_path: on return, the "most preferred" leasefile path
*
* Returns the path of an existing leasefile (if any) for this interface and
* connection UUID. Also returns the "most preferred" leasefile path, which
* may be different than any found leasefile.
*
* Returns: an existing leasefile, or %NULL if no matching leasefile could be found
*/
static char *
get_dhclient_leasefile(int addr_family,
const char *iface,
const char *uuid,
char **out_preferred_path)
{
gs_free char *path = NULL;
if (nm_dhcp_utils_get_leasefile_path(addr_family, "dhclient", iface, uuid, &path)) {
NM_SET_OUT(out_preferred_path, g_strdup(path));
return g_steal_pointer(&path);
}
NM_SET_OUT(out_preferred_path, g_steal_pointer(&path));
/* If the leasefile we're looking for doesn't exist yet in the new location
* (eg, /var/lib/NetworkManager) then look in old locations to maintain
* backwards compatibility with external tools (like dracut) that put
* leasefiles there.
*/
/* Old Debian, SUSE, and Mandriva location */
g_free(path);
path = g_strdup_printf(LOCALSTATEDIR "/lib/dhcp/dhclient%s-%s-%s.lease",
_addr_family_to_path_part(addr_family),
uuid,
iface);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return g_steal_pointer(&path);
/* Old Red Hat and Fedora location */
g_free(path);
path = g_strdup_printf(LOCALSTATEDIR "/lib/dhclient/dhclient%s-%s-%s.lease",
_addr_family_to_path_part(addr_family),
uuid,
iface);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return g_steal_pointer(&path);
/* Fail */
return NULL;
}
static char *
find_existing_config(NMDhcpDhclient *self, int addr_family, const char *iface, const char *uuid)
{
char *path;
/* NetworkManager-overridden configuration can be used to ship DHCP config
* with NetworkManager itself. It can be uuid-specific, device-specific
* or generic.
*/
if (uuid) {
path = g_strdup_printf(NMCONFDIR "/dhclient%s-%s.conf",
_addr_family_to_path_part(addr_family),
uuid);
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
}
path = g_strdup_printf(NMCONFDIR "/dhclient%s-%s.conf",
_addr_family_to_path_part(addr_family),
iface);
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
path = g_strdup_printf(NMCONFDIR "/dhclient%s.conf", _addr_family_to_path_part(addr_family));
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
/* Distribution's dhclient configuration is used so that we can use
* configuration shipped with dhclient (if any).
*
* This replaces conditional compilation based on distribution name. Fedora
* and Debian store the configs in /etc/dhcp while upstream defaults to /etc
* which is then used by many other distributions. Some distributions
* (including Fedora) don't even provide a default configuration file.
*/
path = g_strdup_printf(SYSCONFDIR "/dhcp/dhclient%s-%s.conf",
_addr_family_to_path_part(addr_family),
iface);
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
path = g_strdup_printf(SYSCONFDIR "/dhclient%s-%s.conf",
_addr_family_to_path_part(addr_family),
iface);
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
path =
g_strdup_printf(SYSCONFDIR "/dhcp/dhclient%s.conf", _addr_family_to_path_part(addr_family));
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
path = g_strdup_printf(SYSCONFDIR "/dhclient%s.conf", _addr_family_to_path_part(addr_family));
_LOGD("looking for existing config %s", path);
if (g_file_test(path, G_FILE_TEST_EXISTS))
return path;
g_free(path);
return NULL;
}
/* NM provides interface-specific options; thus the same dhclient config
* file cannot be used since DHCP transactions can happen in parallel.
* Since some distros don't have default per-interface dhclient config files,
* read their single config file and merge that into a custom per-interface
* config file along with the NM options.
*/
static char *
create_dhclient_config(NMDhcpDhclient *self,
int addr_family,
const char *iface,
const char *uuid,
GBytes *client_id,
gboolean send_client_id,
const char *anycast_address,
const char *hostname,
guint32 timeout,
gboolean use_fqdn,
NMDhcpHostnameFlags hostname_flags,
const char *mud_url,
const char *const *reject_servers,
GBytes **out_new_client_id)
{
gs_free char *orig_path = NULL;
gs_free char *orig_content = NULL;
char *new_path = NULL;
gs_free char *new_content = NULL;
GError *error = NULL;
g_return_val_if_fail(iface != NULL, NULL);
new_path = g_strdup_printf(NMSTATEDIR "/dhclient%s-%s.conf",
_addr_family_to_path_part(addr_family),
iface);
_LOGD("creating composite dhclient config %s", new_path);
orig_path = find_existing_config(self, addr_family, iface, uuid);
if (orig_path)
_LOGD("merging existing dhclient config %s", orig_path);
else
_LOGD("no existing dhclient configuration to merge");
if (orig_path && g_file_test(orig_path, G_FILE_TEST_EXISTS)) {
if (!g_file_get_contents(orig_path, &orig_content, NULL, &error)) {
_LOGW("error reading dhclient configuration %s: %s", orig_path, error->message);
g_error_free(error);
}
}
new_content = nm_dhcp_dhclient_create_config(iface,
addr_family,
client_id,
send_client_id,
anycast_address,
hostname,
timeout,
use_fqdn,
hostname_flags,
mud_url,
reject_servers,
orig_path,
orig_content,
out_new_client_id);
nm_assert(new_content);
if (!g_file_set_contents(new_path, new_content, -1, &error)) {
_LOGW("error creating dhclient configuration: %s", error->message);
g_error_free(error);
g_free(new_path);
return NULL;
}
return new_path;
}
static gboolean
dhclient_start(NMDhcpClient *client,
gboolean set_mode,
gboolean release,
gboolean set_duid,
pid_t *out_pid,
GError **error)
{
NMDhcpDhclient *self = NM_DHCP_DHCLIENT(client);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
gs_unref_ptrarray GPtrArray *argv = NULL;
pid_t pid;
gs_free_error GError *local = NULL;
const char *iface;
const char *uuid;
const char *system_bus_address;
const char *dhclient_path;
char *binary_name;
gs_free char *cmd_str = NULL;
gs_free char *pid_file = NULL;
gs_free char *system_bus_address_env = NULL;
gs_free char *preferred_leasefile_path = NULL;
int addr_family;
const NMDhcpClientConfig *client_config;
char pd_length_str[16];
g_return_val_if_fail(!priv->pid_file, FALSE);
client_config = nm_dhcp_client_get_config(client);
addr_family = client_config->addr_family;
NM_SET_OUT(out_pid, 0);
dhclient_path = nm_dhcp_dhclient_get_path();
if (!dhclient_path) {
nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "dhclient binary not found");
return FALSE;
}
iface = client_config->iface;
uuid = client_config->uuid;
pid_file = g_strdup_printf(NMRUNDIR "/dhclient%s-%s.pid",
_addr_family_to_path_part(addr_family),
iface);
/* Kill any existing dhclient from the pidfile */
binary_name = g_path_get_basename(dhclient_path);
nm_dhcp_client_stop_existing(pid_file, binary_name);
g_free(binary_name);
if (release) {
/* release doesn't use the pidfile after killing an old client */
nm_clear_g_free(&pid_file);
}
g_free(priv->lease_file);
priv->lease_file = get_dhclient_leasefile(addr_family, iface, uuid, &preferred_leasefile_path);
nm_assert(preferred_leasefile_path);
if (!priv->lease_file) {
/* No existing leasefile, dhclient will create one at the preferred path */
priv->lease_file = g_steal_pointer(&preferred_leasefile_path);
} else if (!nm_streq0(priv->lease_file, preferred_leasefile_path)) {
gs_unref_object GFile *src = g_file_new_for_path(priv->lease_file);
gs_unref_object GFile *dst = g_file_new_for_path(preferred_leasefile_path);
/* Try to copy the existing leasefile to the preferred location */
if (!g_file_copy(src, dst, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &local)) {
gs_free char *s_path = NULL;
gs_free char *d_path = NULL;
/* Failure; just use the existing leasefile */
_LOGW("failed to copy leasefile %s to %s: %s",
(s_path = g_file_get_path(src)),
(d_path = g_file_get_path(dst)),
local->message);
g_clear_error(&local);
} else {
/* Success; use the preferred leasefile path */
g_free(priv->lease_file);
priv->lease_file = g_file_get_path(dst);
}
}
/* Save the DUID to the leasefile dhclient will actually use */
if (set_duid && addr_family == AF_INET6) {
if (!nm_dhcp_dhclient_save_duid(priv->lease_file,
nm_dhcp_client_get_effective_client_id(client),
client_config->v6.enforce_duid,
&local)) {
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
"failed to save DUID to '%s': %s",
priv->lease_file,
local->message);
return FALSE;
}
}
argv = g_ptr_array_new();
g_ptr_array_add(argv, (gpointer) dhclient_path);
g_ptr_array_add(argv, (gpointer) "-d");
/* Be quiet. dhclient logs to syslog anyway. And we duplicate the syslog
* to stderr in case of NM running with --debug.
*/
g_ptr_array_add(argv, (gpointer) "-q");
if (release)
g_ptr_array_add(argv, (gpointer) "-r");
if (!release && client_config->addr_family == AF_INET && client_config->v4.request_broadcast) {
g_ptr_array_add(argv, (gpointer) "-B");
}
if (addr_family == AF_INET6) {
guint prefixes = client_config->v6.needed_prefixes;
const char *mode_opt;
g_ptr_array_add(argv, (gpointer) "-6");
if (!set_mode)
mode_opt = NULL;
else if (!client_config->v6.info_only)
mode_opt = "-N";
else if (prefixes == 0)
mode_opt = "-S";
else
mode_opt = NULL;
if (mode_opt)
g_ptr_array_add(argv, (gpointer) mode_opt);
if (prefixes > 0 && client_config->v6.pd_hint_length > 0) {
if (!IN6_IS_ADDR_UNSPECIFIED(&client_config->v6.pd_hint_addr)) {
_LOGW("dhclient only supports a length as prefix delegation hint, not a prefix");
}
nm_sprintf_buf(pd_length_str, "%u", client_config->v6.pd_hint_length);
g_ptr_array_add(argv, "--prefix-len-hint");
g_ptr_array_add(argv, pd_length_str);
}
while (prefixes--)
g_ptr_array_add(argv, (gpointer) "-P");
}
g_ptr_array_add(argv, (gpointer) "-sf"); /* Set script file */
g_ptr_array_add(argv, (gpointer) nm_dhcp_helper_path);
if (pid_file) {
g_ptr_array_add(argv, (gpointer) "-pf"); /* Set pid file */
g_ptr_array_add(argv, (gpointer) pid_file);
}
g_ptr_array_add(argv, (gpointer) "-lf"); /* Set lease file */
g_ptr_array_add(argv, (gpointer) priv->lease_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);
}
if (client_config->v4.dscp_explicit) {
_LOGW("dhclient does not support specifying a custom DSCP value; the TOS field will be set "
"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.
*/
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);
_LOGD("running: %s", (cmd_str = g_strjoinv(" ", (char **) argv->pdata)));
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,
"dhclient failed to start: %s",
local->message);
return FALSE;
}
_LOGI("dhclient started with pid %lld", (long long int) pid);
if (!release)
nm_dhcp_client_watch_child(client, pid);
priv->pid_file = g_steal_pointer(&pid_file);
NM_SET_OUT(out_pid, pid);
return TRUE;
}
static gboolean
ip4_start(NMDhcpClient *client, GError **error)
{
NMDhcpDhclient *self = NM_DHCP_DHCLIENT(client);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
gs_unref_bytes GBytes *new_client_id = NULL;
const NMDhcpClientConfig *client_config;
client_config = nm_dhcp_client_get_config(client);
nm_assert(client_config->addr_family == AF_INET);
priv->conf_file = create_dhclient_config(self,
AF_INET,
client_config->iface,
client_config->uuid,
client_config->client_id,
client_config->v4.send_client_id,
client_config->anycast_address,
client_config->hostname,
client_config->timeout,
client_config->use_fqdn,
client_config->hostname_flags,
client_config->mud_url,
client_config->reject_servers,
&new_client_id);
if (!priv->conf_file) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_UNKNOWN,
"error creating dhclient configuration file");
return FALSE;
}
/* Note that the effective-client-id for IPv4 here might be unknown/NULL. */
nm_assert(!new_client_id || !client_config->client_id);
nm_dhcp_client_set_effective_client_id(client, client_config->client_id ?: new_client_id);
return dhclient_start(client, FALSE, FALSE, FALSE, NULL, error);
}
static gboolean
ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
{
NMDhcpDhclient *self = NM_DHCP_DHCLIENT(client);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
const NMDhcpClientConfig *config;
gs_unref_bytes GBytes *effective_client_id = NULL;
config = nm_dhcp_client_get_config(client);
nm_assert(config->addr_family == AF_INET6);
if (config->v6.iaid_explicit)
_LOGW("dhclient does not support specifying an IAID for DHCPv6, it will be ignored");
priv->conf_file = create_dhclient_config(self,
AF_INET6,
config->iface,
config->uuid,
NULL,
TRUE,
config->anycast_address,
config->hostname,
config->timeout,
TRUE,
config->hostname_flags,
config->mud_url,
NULL,
NULL);
if (!priv->conf_file) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_UNKNOWN,
"error creating dhclient configuration file");
return FALSE;
}
nm_assert(config->client_id);
if (!config->v6.enforce_duid)
effective_client_id = read_duid_from_lease(self);
nm_dhcp_client_set_effective_client_id(client, effective_client_id ?: config->client_id);
return dhclient_start(client, TRUE, FALSE, TRUE, NULL, error);
}
static void
stop(NMDhcpClient *client, gboolean release)
{
NMDhcpDhclient *self = NM_DHCP_DHCLIENT(client);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
int errsv;
NM_DHCP_CLIENT_CLASS(nm_dhcp_dhclient_parent_class)->stop(client, release);
if (priv->conf_file)
if (remove(priv->conf_file) == -1) {
errsv = errno;
_LOGD("could not remove dhcp config file \"%s\": %d (%s)",
priv->conf_file,
errsv,
nm_strerror_native(errsv));
}
if (priv->pid_file) {
if (remove(priv->pid_file) == -1) {
errsv = errno;
_LOGD("could not remove dhcp pid file \"%s\": %s (%d)",
priv->pid_file,
nm_strerror_native(errsv),
errsv);
}
nm_clear_g_free(&priv->pid_file);
}
if (release) {
pid_t rpid = -1;
if (dhclient_start(client, FALSE, TRUE, FALSE, &rpid, NULL)) {
/* Wait a few seconds for the release to happen */
nm_dhcp_client_stop_pid(rpid, nm_dhcp_client_get_iface(client));
}
}
}
static GBytes *
read_duid_from_lease(NMDhcpDhclient *self)
{
NMDhcpClient *client = NM_DHCP_CLIENT(self);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
const NMDhcpClientConfig *client_config;
GBytes *duid = NULL;
gs_free char *leasefile = NULL;
GError *error = NULL;
client_config = nm_dhcp_client_get_config(client);
/* Look in interface-specific leasefile first for backwards compat */
leasefile = get_dhclient_leasefile(AF_INET6,
nm_dhcp_client_get_iface(client),
client_config->uuid,
NULL);
if (leasefile) {
_LOGD("looking for DUID in '%s'", leasefile);
duid = nm_dhcp_dhclient_read_duid(leasefile, &error);
if (error) {
_LOGW("failed to read leasefile '%s': %s", leasefile, error->message);
g_clear_error(&error);
}
if (duid)
return duid;
}
/* Otherwise, read the default machine-wide DUID */
_LOGD("looking for default DUID in '%s'", priv->def_leasefile);
duid = nm_dhcp_dhclient_read_duid(priv->def_leasefile, &error);
if (error) {
_LOGW("failed to read leasefile '%s': %s", priv->def_leasefile, error->message);
g_clear_error(&error);
}
return duid;
}
/*****************************************************************************/
static void
nm_dhcp_dhclient_init(NMDhcpDhclient *self)
{
static const char *const FILES[] = {
SYSCONFDIR "/dhclient6.leases", /* default */
LOCALSTATEDIR "/lib/dhcp/dhclient6.leases",
LOCALSTATEDIR "/lib/dhclient/dhclient6.leases",
};
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
int i;
priv->def_leasefile = FILES[0];
for (i = 0; i < G_N_ELEMENTS(FILES); i++) {
if (g_file_test(FILES[i], G_FILE_TEST_EXISTS)) {
priv->def_leasefile = FILES[i];
break;
}
}
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)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_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_DHCLIENT(object));
g_clear_object(&priv->dhcp_listener);
}
nm_clear_g_free(&priv->pid_file);
nm_clear_g_free(&priv->conf_file);
nm_clear_g_free(&priv->lease_file);
G_OBJECT_CLASS(nm_dhcp_dhclient_parent_class)->dispose(object);
}
static void
nm_dhcp_dhclient_class_init(NMDhcpDhclientClass *dhclient_class)
{
NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS(dhclient_class);
GObjectClass *object_class = G_OBJECT_CLASS(dhclient_class);
object_class->dispose = dispose;
client_class->ip4_start = ip4_start;
client_class->ip6_start = ip6_start;
client_class->stop = stop;
}
const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = {
.name = "dhclient",
.get_type_4 = nm_dhcp_dhclient_get_type,
.get_type_6 = nm_dhcp_dhclient_get_type,
.get_path = nm_dhcp_dhclient_get_path,
};
#endif /* WITH_DHCLIENT */

View file

@ -36,9 +36,6 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6] = {
#endif
&_nm_dhcp_client_factory_systemd,
&_nm_dhcp_client_factory_nettools,
#if WITH_DHCLIENT
&_nm_dhcp_client_factory_dhclient,
#endif
};
/*****************************************************************************/

View file

@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
test_units = [
'test-dhcp-dhclient',
'test-dhcp-utils',
]

View file

@ -1,2 +0,0 @@
#default-duid "\000\001\000\001\030y\246\023`g \354Lp";

View file

@ -1,2 +0,0 @@
default-duid "\000\001\000\001\030y\246\023`g \354Lp";

File diff suppressed because it is too large Load diff

View file

@ -128,8 +128,6 @@ libNetworkManager = static_library(
'devices/nm-device-wireguard.c',
'devices/nm-device-wpan.c',
'devices/nm-lldp-listener.c',
'dhcp/nm-dhcp-dhclient.c',
'dhcp/nm-dhcp-dhclient-utils.c',
'dhcp/nm-dhcp-dhcpcd.c',
'dhcp/nm-dhcp-listener.c',
'dns/nm-dns-dnsmasq.c',

View file

@ -204,7 +204,6 @@ RUN dnf install -y \\
cscope \\
dbus-devel \\
dbus-x11 \\
dhclient \\
dnsmasq \\
firewalld-filesystem \\
gcc-c++ \\

View file

@ -287,7 +287,6 @@ do_build() {
--delete "/etc/NetworkManager/system-connections/*" \
--append-line "/etc/bashrc:. /etc/bashrc.my" \
--run-command "updatedb" \
--append-line "/etc/dhcp/dhclient.conf:send host-name = gethostname();" \
--firstboot-command "systemctl enable --now dhcp-host"
virt-install \