1
0
mirror of https://github.com/systemd/systemd synced 2026-03-31 20:24:50 +02:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Luca Boccassi
98ae19d9fe
integritysetup: Add support for hmac-sha512 and wrapped key HMAC algorithms phmac-sha256 and phmac-sha512 (#39719)
Currently the only supported integrity algorithm using HMAC is
`hmac-sha256`. Add `hmac-sha512` to the list of supported algorithms as
well.

Also add the `PHMAC` integrity algorithm to the list of supported
algorithms. The `PHMAC` algorithm is like the regular HMAC algorithm,
but it takes a wrapped key as input. A key for the `PHMAC` algorithm is
an opaque key blob, who's physical size has nothing to do with the
cryptographic size. Such a wrapped key can for example be a HSM
protected key. Currently PHMAC is only available for the s390x
architecture (Linux on IBM Z).

Support for PHMAC has just been added to the cryptsetup project via MR
https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/693 by commit

296eb39c60

To allow automatic opening of integrity protected volumes that use PHMAC
via `/etc/integritytab`, this change in systemd's integritysetup tool is
needed as well.
2025-11-14 00:12:34 +00:00
Chris Down
7f9c0c31d2 sd-dhcp-server: Add Hostname= option to static leases
This adds a new `Hostname=` option to the [DHCPServerStaticLease]
section in .network files, allowing an administrator to assign a
specific hostname to a client receiving a static lease.

We automatically select the correct DHCP option to use based on the
format of the provided string:

- Single DNS labels are sent as Option 12.
- Names with multiple DNS labels are sent as Option 81 in wire format.

Fixes: #39634
2025-11-14 07:50:13 +09:00
Yu Watanabe
c83f3f0837
musl: add several missing symbols (#39701) 2025-11-14 07:49:16 +09:00
Yu Watanabe
aeb5fb83ca
Three follow-ups for recent changes (#39724) 2025-11-14 07:48:47 +09:00
Mike Yuan
fa0ac03a56
stdio-bridge: remove unreachable return 2025-11-13 21:28:17 +01:00
Mike Yuan
045b8d761e
run: refuse --root-directory= in --scope mode
As discussed in #39669, let's reject this for now.
2025-11-13 21:17:32 +01:00
Mike Yuan
b0e7c6141f
efivars: insert a newline below fstat() call 2025-11-13 21:17:32 +01:00
Yu Watanabe
69dd6d9420 musl: add several missing statx macros
glibc's sys/stat.h includes linux/stat.h, and we have copy of it from
the latest kernel, hence all new flags are always defined.
However, musl's sys/stat.h does not include linux/stat.h, and moreover,
they conflict with each other, hence we cannot include both header
simultaneously. Let's define missing macros to support musl.
2025-11-14 04:59:03 +09:00
Chen Qi
dd102894e7 musl: add missing FTW_CONTINUE macro
This is to avoid build failures like below for musl.

  test-recurse-dir.c:23:24: error: ‘FTW_CONTINUE’ undeclared

Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
2025-11-14 04:59:03 +09:00
Yu Watanabe
8324ef4213 musl: replace netinet/if_ether.h with our own implementation
musl's netinet/if_ether.h conflicts with linux/if_ether.h.
The reimplementation is mostly equivalent with what glibc does.
2025-11-14 04:59:03 +09:00
Yu Watanabe
6400e8dbd9 musl: provide several missing definitions for prctl() 2025-11-14 04:59:03 +09:00
Yu Watanabe
03d0fa4e4f musl: introduce dummy function for gnu_get_libc_version()
As the header gnu/libc-version.h and gnu_get_libc_version() function
are glibc specific, and musl does not provide them.
2025-11-14 04:59:03 +09:00
Yu Watanabe
bc610c70af musl: introduce dummy functions for mallinfo(), malloc_info(), and malloc_trim()
These functions are not provided by musl.
2025-11-14 04:59:03 +09:00
Yu Watanabe
a2b7bcce27 musl: introduce dummy gshadow header file for userdb
Even 'gshadow' meson option is disabled, src/shared/userdb.c and
src/shared/user-record-nss.c include gshadow.h unconditionally.
Let's introduce dummy header to make them compiled gracefully.
2025-11-14 04:59:03 +09:00
Ingo Franzki
eb7b0d413e integritysetup: Add PHMAC algorithm to list of known algorithms
Add the PHMAC integrity algorithm to the list of supported algorithms.

The PHMAC algorithm is like the regular HMAC algorithm, but it takes a wrapped key
as input. A key for the PHMAC algorithm is an opaque key blob, who's physical size
has nothing to do with the cryptographic size. Currently PHMAC is only available
for the s390x architecture.
2025-11-13 16:14:25 +01:00
Ingo Franzki
7bf1cfe3b2 integritysetup: Add support for hmac-sha512
Currently the only supported integrity algorithm using HMAC is 'hmac-sha256'.
Add 'hmac-sha512' to the list of supported algorithms as well.
2025-11-13 10:25:08 +01:00
33 changed files with 642 additions and 50 deletions

View File

@ -55,8 +55,8 @@
<para>The third field if present contains an absolute filename path to a key file or a <literal>-</literal> <para>The third field if present contains an absolute filename path to a key file or a <literal>-</literal>
to specify none. When the filename is present, the "integrity-algorithm" defaults to <literal>hmac-sha256</literal> to specify none. When the filename is present, the "integrity-algorithm" defaults to <literal>hmac-sha256</literal>
with the key length derived from the number of bytes in the key file. At this time the only supported integrity algorithm with the key length derived from the number of bytes in the key file. At this time the only supported integrity algorithms
when using key file is hmac-sha256. The maximum size of the key file is 4096 bytes. when using key file are hmac-sha256, hmac-sha512, phmac-sha256, and hmac-sha512. The maximum size of the key file is 4096 bytes.
</para> </para>
<para>The fourth field, if present, is a comma-delimited list of options or a <literal>-</literal> to specify none. The following options are <para>The fourth field, if present, is a comma-delimited list of options or a <literal>-</literal> to specify none. The following options are
@ -125,7 +125,7 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>integrity-algorithm=[crc32c|crc32|xxhash64|sha1|sha256|hmac-sha256]</option></term> <term><option>integrity-algorithm=[crc32c|crc32|xxhash64|sha1|sha256|hmac-sha256|hmac-sha512|phmac-sha256|phmac-sha512]</option></term>
<listitem><para> <listitem><para>
The algorithm used for integrity checking. The default is crc32c. Must match option used during format. The algorithm used for integrity checking. The default is crc32c. Must match option used during format.

View File

@ -4227,6 +4227,25 @@ ServerAddress=192.168.0.1/24</programlisting>
<xi:include href="version-info.xml" xpointer="v249"/></listitem> <xi:include href="version-info.xml" xpointer="v249"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>Hostname=</varname></term>
<listitem><para>The hostname to send to the client in DHCP replies. This can be either a simple
hostname (e.g., <literal>mydevice</literal>) or a fully qualified domain name (e.g.,
<literal>mydevice.example.com</literal>), following RFC 1123 naming conventions. Each label can be
up to 63 characters, with a total maximum length of 253 characters for FQDNs. When this option is
set, the DHCP server will include the hostname in DHCP replies (both OFFER and ACK) to the client
with the matched MAC address.</para>
<para>The server automatically selects the appropriate DHCP option based on the hostname format:
simple hostnames (single DNS label) are sent via option 12 (Host Name) per RFC 2132, while FQDNs
with multiple labels are sent via option 81 (Client FQDN) per RFC 4702 using DNS wire format
encoding. The configured hostname is sent unconditionally, any hostname requested by the client in
its DHCP message is ignored.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -68,6 +68,7 @@ int efi_get_variable(
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p); return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
r = stat_verify_regular(&st); r = stat_verify_regular(&st);
if (r < 0) if (r < 0)
return log_debug_errno(r, "EFI variable '%s' is not a regular file, refusing: %m", p); return log_debug_errno(r, "EFI variable '%s' is not a regular file, refusing: %m", p);

8
src/include/musl/ftw.h Normal file
View File

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <ftw.h>
#ifndef FTW_CONTINUE
#define FTW_CONTINUE 0
#endif

View File

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stddef.h>
static inline const char* gnu_get_libc_version(void) {
return "";
}

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <errno.h>
#include <stddef.h>
struct sgrp {
char *sg_namp;
char *sg_passwd;
char **sg_adm;
char **sg_mem;
};
static inline int getsgnam_r(
const char *__name,
struct sgrp *__result_buf,
char *__buffer,
size_t __buflen,
struct sgrp **__result) {
return EOPNOTSUPP; /* this function returns positive errno in case of error. */
}

39
src/include/musl/malloc.h Normal file
View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <errno.h>
#include <stdio.h>
/* struct mallinfo2 will be defined and struct mallinfo is converted to struct mallinfo2 in
* override/malloc.h. Hence, here we define struct mallinfo. */
struct mallinfo {
int arena; /* non-mmapped space allocated from system */
int ordblks; /* number of free chunks */
int smblks; /* number of fastbin blocks */
int hblks; /* number of mmapped regions */
int hblkhd; /* space in mmapped regions */
int usmblks; /* always 0, preserved for backwards compatibility */
int fsmblks; /* space available in freed fastbin blocks */
int uordblks; /* total allocated space */
int fordblks; /* total free space */
int keepcost; /* top-most, releasable (via malloc_trim) space */
};
static inline struct mallinfo mallinfo(void) {
return (struct mallinfo) {};
}
static inline int malloc_info(int options, FILE *stream) {
if (options != 0)
errno = EINVAL;
else
errno = EOPNOTSUPP;
return -1;
}
static inline int malloc_trim(size_t pad) {
return 0;
}
#include_next <malloc.h>

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* glibc's netinet/if_ether.h does the following:
* - include linux/if_ether.h, net/ethernet.h, and net/if_arp.h,
* - define struct ether_arp, and relevant macros,
* - define ETHER_MAP_IP_MULTICAST() macro (currently we do not use it).
* However, musl's netinet/if_ether.h conflicts with linux/if_ether.h.
* Let's use the same way that glibc uses. */
#include <linux/if_ether.h> /* IWYU pragma: export */
#include <net/ethernet.h> /* IWYU pragma: export */
#include <net/if_arp.h> /* IWYU pragma: export */
/*
* Ethernet Address Resolution Protocol.
*
* See RFC 826 for protocol description. Structure below is adapted
* to resolving internet addresses. Field names used correspond to
* RFC 826.
*/
struct ether_arp {
struct arphdr ea_hdr; /* fixed-size header */
uint8_t arp_sha[ETH_ALEN]; /* sender hardware address */
uint8_t arp_spa[4]; /* sender protocol address */
uint8_t arp_tha[ETH_ALEN]; /* target hardware address */
uint8_t arp_tpa[4]; /* target protocol address */
};
#define arp_hrd ea_hdr.ar_hrd
#define arp_pro ea_hdr.ar_pro
#define arp_hln ea_hdr.ar_hln
#define arp_pln ea_hdr.ar_pln
#define arp_op ea_hdr.ar_op

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <sys/prctl.h> /* IWYU pragma: export */
/* musl's sys/prctl.h does not include linux/prctl.h, and also we cannot include with linux/prctl.h.
* Hence, we need to provide some missing definitions. */
#ifndef PR_SET_MDWE
#define PR_SET_MDWE 65
#endif
#ifndef PR_MDWE_REFUSE_EXEC_GAIN
#define PR_MDWE_REFUSE_EXEC_GAIN (1UL << 0)
#endif
#ifndef PR_SET_MEMORY_MERGE
#define PR_SET_MEMORY_MERGE 67
#endif

View File

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <sys/stat.h>
#include <assert.h>
#include <stddef.h>
/* musl's sys/stat.h does not include linux/stat.h, and unfortunately they conflict with each other.
* Hence, some relatively new macros need to be explicitly defined here. */
/* Before 23ab04a8630225371455d5f4538fd078665bb646, statx.stx_mnt_id is not defined. */
#ifndef STATX_MNT_ID
static_assert(offsetof(struct statx, __pad1) == offsetof(struct statx, stx_dev_minor) + sizeof(uint32_t), "");
#define stx_mnt_id __pad1[0]
#endif
#ifndef STATX_MNT_ID
#define STATX_MNT_ID 0x00001000U
#endif
#ifndef STATX_DIOALIGN
#define STATX_DIOALIGN 0x00002000U
#endif
#ifndef STATX_MNT_ID_UNIQUE
#define STATX_MNT_ID_UNIQUE 0x00004000U
#endif
#ifndef STATX_SUBVOL
#define STATX_SUBVOL 0x00008000U
#endif
#ifndef STATX_WRITE_ATOMIC
#define STATX_WRITE_ATOMIC 0x00010000U
#endif
#ifndef STATX_DIO_READ_ALIGN
#define STATX_DIO_READ_ALIGN 0x00020000U
#endif
#ifndef STATX_ATTR_COMPRESSED
#define STATX_ATTR_COMPRESSED 0x00000004
#endif
#ifndef STATX_ATTR_IMMUTABLE
#define STATX_ATTR_IMMUTABLE 0x00000010
#endif
#ifndef STATX_ATTR_APPEND
#define STATX_ATTR_APPEND 0x00000020
#endif
#ifndef STATX_ATTR_NODUMP
#define STATX_ATTR_NODUMP 0x00000040
#endif
#ifndef STATX_ATTR_ENCRYPTED
#define STATX_ATTR_ENCRYPTED 0x00000800
#endif
#ifndef STATX_ATTR_AUTOMOUNT
#define STATX_ATTR_AUTOMOUNT 0x00001000
#endif
#ifndef STATX_ATTR_MOUNT_ROOT
#define STATX_ATTR_MOUNT_ROOT 0x00002000
#endif
#ifndef STATX_ATTR_VERITY
#define STATX_ATTR_VERITY 0x00100000
#endif
#ifndef STATX_ATTR_DAX
#define STATX_ATTR_DAX 0x00200000
#endif
#ifndef STATX_ATTR_WRITE_ATOMIC
#define STATX_ATTR_WRITE_ATOMIC 0x00400000
#endif

View File

@ -11,7 +11,7 @@
#include "time-util.h" #include "time-util.h"
static int supported_integrity_algorithm(char *user_supplied) { static int supported_integrity_algorithm(char *user_supplied) {
if (!STR_IN_SET(user_supplied, "crc32", "crc32c", "xxhash64", "sha1", "sha256", "hmac-sha256")) if (!STR_IN_SET(user_supplied, "crc32", "crc32c", "xxhash64", "sha1", "sha256", "hmac-sha256", "hmac-sha512", "phmac-sha256", "phmac-sha512"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported integrity algorithm (%s)", user_supplied); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported integrity algorithm (%s)", user_supplied);
return 0; return 0;
} }

View File

@ -12,4 +12,7 @@ int parse_integrity_options(
char **ret_integrity_alg); char **ret_integrity_alg);
#define DM_HMAC_256 "hmac(sha256)" #define DM_HMAC_256 "hmac(sha256)"
#define DM_HMAC_512 "hmac(sha512)"
#define DM_PHMAC_256 "phmac(sha256)"
#define DM_PHMAC_512 "phmac(sha512)"
#define DM_MAX_KEY_SIZE 4096 /* Maximum size of key allowed for dm-integrity */ #define DM_MAX_KEY_SIZE 4096 /* Maximum size of key allowed for dm-integrity */

View File

@ -77,6 +77,12 @@ static const char *integrity_algorithm_select(const void *key_file_buf) {
if (arg_integrity_algorithm) { if (arg_integrity_algorithm) {
if (streq("hmac-sha256", arg_integrity_algorithm)) if (streq("hmac-sha256", arg_integrity_algorithm))
return DM_HMAC_256; return DM_HMAC_256;
if (streq("hmac-sha512", arg_integrity_algorithm))
return DM_HMAC_512;
if (streq("phmac-sha256", arg_integrity_algorithm))
return DM_PHMAC_256;
if (streq("phmac-sha512", arg_integrity_algorithm))
return DM_PHMAC_512;
return arg_integrity_algorithm; return arg_integrity_algorithm;
} else if (key_file_buf) } else if (key_file_buf)
return DM_HMAC_256; return DM_HMAC_256;

View File

@ -56,9 +56,11 @@ static int add_static_lease(sd_dhcp_server *server, uint8_t i) {
assert(server); assert(server);
return sd_dhcp_server_set_static_lease( return sd_dhcp_server_set_static_lease(
server, server,
&(struct in_addr) { .s_addr = htobe32(UINT32_C(10) << 24 | i)}, &(struct in_addr) { .s_addr = htobe32(UINT32_C(10) << 24 | i) },
id, ELEMENTSOF(id)); id,
ELEMENTSOF(id),
/* hostname= */ NULL);
} }
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

View File

@ -505,6 +505,60 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
return 0; return 0;
} }
static int lease_parse_fqdn(const uint8_t *option, size_t len, char **hostname) {
_cleanup_free_ char *name = NULL, *normalized = NULL;
int r;
assert(option);
assert(hostname);
/* RFC 4702 Section 2
*
* Byte 0: Flags (S: server should perform A RR updates, O: override existing A RR,
* E: encoding (0=ASCII, 1=Wire format), N: no server updates)
* Byte 1: RCODE1 (ignored on receipt)
* Byte 2: RCODE2 (ignored on receipt)
* Bytes 3+: Domain Name */
if (len <= 3)
return -EBADMSG;
size_t data_len = len - 3;
const uint8_t *data = option + 3;
/* In practice, many servers send DNS wire format regardless of the E flag, so ignore and try wire
* format first, then fall back to ASCII if that fails. */
r = dns_name_from_wire_format(&data, &data_len, &name);
if (r < 0) {
if (FLAGS_SET(option[0], DHCP_FQDN_FLAG_E))
return -EBADMSG;
/* Wire format failed, try ASCII format */
r = dhcp_option_parse_string(option + 3, len - 3, &name);
if (r < 0)
return r;
}
if (!name) {
*hostname = mfree(*hostname);
return 0;
}
r = dns_name_normalize(name, 0, &normalized);
if (r < 0)
return r;
if (is_localhost(normalized))
return -EINVAL;
if (dns_name_is_root(normalized))
return -EINVAL;
free_and_replace(*hostname, normalized);
return 0;
}
static int lease_parse_captive_portal(const uint8_t *option, size_t len, char **ret) { static int lease_parse_captive_portal(const uint8_t *option, size_t len, char **ret) {
_cleanup_free_ char *uri = NULL; _cleanup_free_ char *uri = NULL;
int r; int r;
@ -967,6 +1021,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break; break;
case SD_DHCP_OPTION_HOST_NAME: case SD_DHCP_OPTION_HOST_NAME:
/* FQDN option (81) always takes precedence. If it was already set, do not overwrite it. */
if (lease->hostname) {
log_debug("Hostname already set via FQDN, ignoring hostname option.");
break;
}
r = lease_parse_domain(option, len, &lease->hostname); r = lease_parse_domain(option, len, &lease->hostname);
if (r < 0) { if (r < 0) {
log_debug_errno(r, "Failed to parse hostname, ignoring: %m"); log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
@ -975,6 +1035,15 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break; break;
case SD_DHCP_OPTION_FQDN:
r = lease_parse_fqdn(option, len, &lease->hostname);
if (r < 0) {
log_debug_errno(r, "Failed to parse FQDN, ignoring: %m");
return 0;
}
break;
case SD_DHCP_OPTION_ROOT_PATH: { case SD_DHCP_OPTION_ROOT_PATH: {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;

View File

@ -7,6 +7,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "dhcp-server-lease-internal.h" #include "dhcp-server-lease-internal.h"
#include "dns-domain.h"
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fs-util.h" #include "fs-util.h"
@ -181,7 +182,8 @@ int sd_dhcp_server_set_static_lease(
sd_dhcp_server *server, sd_dhcp_server *server,
const struct in_addr *address, const struct in_addr *address,
uint8_t *client_id_raw, uint8_t *client_id_raw,
size_t client_id_size) { size_t client_id_size,
const char *hostname) {
_cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL; _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
sd_dhcp_client_id client_id; sd_dhcp_client_id client_id;
@ -203,6 +205,14 @@ int sd_dhcp_server_set_static_lease(
return 0; return 0;
} }
if (hostname) {
r = dns_name_is_valid_ldh(hostname);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
}
lease = new(sd_dhcp_server_lease, 1); lease = new(sd_dhcp_server_lease, 1);
if (!lease) if (!lease)
return -ENOMEM; return -ENOMEM;
@ -213,6 +223,12 @@ int sd_dhcp_server_set_static_lease(
.client_id = client_id, .client_id = client_id,
}; };
if (hostname) {
lease->hostname = strdup(hostname);
if (!lease->hostname)
return -ENOMEM;
}
r = dhcp_server_put_lease(server, lease, /* is_static = */ true); r = dhcp_server_put_lease(server, lease, /* is_static = */ true);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -534,6 +534,64 @@ static int server_message_init(
return 0; return 0;
} }
static int dhcp_server_append_static_hostname(
sd_dhcp_server *server,
DHCPPacket *packet,
size_t *offset,
DHCPRequest *req) {
sd_dhcp_server_lease *static_lease;
int r;
assert(server);
assert(packet);
assert(offset);
assert(req);
static_lease = dhcp_server_get_static_lease(server, req);
if (!static_lease || !static_lease->hostname)
return 0;
if (dns_name_is_single_label(static_lease->hostname))
/* Option 12 */
return dhcp_option_append(
&packet->dhcp,
req->max_optlen,
offset,
/* overload= */ 0,
SD_DHCP_OPTION_HOST_NAME,
strlen(static_lease->hostname),
static_lease->hostname);
/* Option 81 */
uint8_t buffer[DHCP_MAX_FQDN_LENGTH + 3];
/* Flags: S=0 (will not update RR), O=1 (are overriding client),
* E=1 (using DNS wire format), N=1 (will not update DNS) */
buffer[0] = DHCP_FQDN_FLAG_O | DHCP_FQDN_FLAG_E | DHCP_FQDN_FLAG_N;
/* RFC 4702: A server SHOULD set these to 255 when sending the option and MUST ignore them on
* receipt. */
buffer[1] = 255;
buffer[2] = 255;
r = dns_name_to_wire_format(static_lease->hostname, buffer + 3, sizeof(buffer) - 3, false);
if (r < 0)
return log_dhcp_server_errno(server, r, "Failed to encode FQDN for static lease: %m");
if (r > DHCP_MAX_FQDN_LENGTH)
return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EINVAL), "FQDN for static lease too long");
return dhcp_option_append(
&packet->dhcp,
req->max_optlen,
offset,
/* overload= */ 0,
SD_DHCP_OPTION_FQDN,
3 + r,
buffer);
}
static int server_send_offer_or_ack( static int server_send_offer_or_ack(
sd_dhcp_server *server, sd_dhcp_server *server,
DHCPRequest *req, DHCPRequest *req,
@ -675,6 +733,10 @@ static int server_send_offer_or_ack(
return r; return r;
} }
r = dhcp_server_append_static_hostname(server, packet, &offset, req);
if (r < 0)
return r;
return dhcp_server_send_packet(server, req, packet, type, offset); return dhcp_server_send_packet(server, req, packet, type, offset);
} }

View File

@ -139,8 +139,12 @@ static void test_message_handler(void) {
ASSERT_OK(sd_dhcp_server_new(&server, 1)); ASSERT_OK(sd_dhcp_server_new(&server, 1));
ASSERT_OK(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0)); ASSERT_OK(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0));
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &static_lease_address, static_lease_client_id, ASSERT_OK(sd_dhcp_server_set_static_lease(
ELEMENTSOF(static_lease_client_id))); server,
&static_lease_address,
static_lease_client_id,
ELEMENTSOF(static_lease_client_id),
/* hostname= */ NULL));
ASSERT_OK(sd_dhcp_server_attach_event(server, NULL, 0)); ASSERT_OK(sd_dhcp_server_attach_event(server, NULL, 0));
ASSERT_OK(sd_dhcp_server_start(server)); ASSERT_OK(sd_dhcp_server_start(server));
@ -216,8 +220,12 @@ static void test_message_handler(void) {
/* add the static lease for the client ID */ /* add the static lease for the client ID */
ASSERT_OK(sd_dhcp_server_stop(server)); ASSERT_OK(sd_dhcp_server_stop(server));
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr){ .s_addr = htobe32(INADDR_LOOPBACK + 31) }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t[7]){ 0x01, 'A', 'B', 'C', 'D', 'E', 'F' }, 7)); server,
&(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK + 31) },
(uint8_t[7]) { 0x01, 'A', 'B', 'C', 'D', 'E', 'F' },
7,
/* hostname= */ NULL));
ASSERT_OK(sd_dhcp_server_start(server)); ASSERT_OK(sd_dhcp_server_start(server));
/* discover */ /* discover */
@ -244,7 +252,12 @@ static void test_message_handler(void) {
/* drop the static lease for the client ID */ /* drop the static lease for the client ID */
ASSERT_OK(sd_dhcp_server_stop(server)); ASSERT_OK(sd_dhcp_server_stop(server));
ASSERT_OK(sd_dhcp_server_set_static_lease(server, NULL, (uint8_t[7]){ 0x01, 'A', 'B', 'C', 'D', 'E', 'F' }, 7)); ASSERT_OK(sd_dhcp_server_set_static_lease(
server,
/* address= */ NULL,
(uint8_t[7]) { 0x01, 'A', 'B', 'C', 'D', 'E', 'F' },
7,
/* hostname= */ NULL));
ASSERT_OK(sd_dhcp_server_start(server)); ASSERT_OK(sd_dhcp_server_start(server));
/* request a new non-static address */ /* request a new non-static address */
@ -324,35 +337,78 @@ static void test_static_lease(void) {
ASSERT_OK(sd_dhcp_server_new(&server, 1)); ASSERT_OK(sd_dhcp_server_new(&server, 1));
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x01020304 },
(uint8_t *) &(uint32_t) { 0x01020304 },
sizeof(uint32_t),
/* hostname= */ NULL));
/* Duplicated entry. */ /* Duplicated entry. */
ASSERT_ERROR(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 }, ASSERT_ERROR(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)), EEXIST); server,
&(struct in_addr) { .s_addr = 0x01020304 },
(uint8_t *) &(uint32_t) { 0x01020304 },
sizeof(uint32_t),
/* hostname= */ NULL),
EEXIST);
/* Address is conflicted. */ /* Address is conflicted. */
ASSERT_ERROR(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 }, ASSERT_ERROR(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)), EEXIST); server,
&(struct in_addr) { .s_addr = 0x01020304 },
(uint8_t *) &(uint32_t) { 0x01020305 },
sizeof(uint32_t),
/* hostname= */ NULL),
EEXIST);
/* Client ID is conflicted. */ /* Client ID is conflicted. */
ASSERT_ERROR(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020305 }, ASSERT_ERROR(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)), EEXIST); server,
&(struct in_addr) { .s_addr = 0x01020305 },
(uint8_t *) &(uint32_t) { 0x01020304 },
sizeof(uint32_t),
/* hostname= */ NULL),
EEXIST);
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020305 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x01020305 },
(uint8_t *) &(uint32_t) { 0x01020305 },
sizeof(uint32_t),
/* hostname= */ NULL));
/* Remove the previous entry. */ /* Remove the previous entry. */
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t *) &(uint32_t) { 0x01020305 },
sizeof(uint32_t),
/* hostname= */ NULL));
/* Then, set a different address. */ /* Then, set a different address. */
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020306 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x01020306 },
(uint8_t *) &(uint32_t) { 0x01020305 },
sizeof(uint32_t),
/* hostname= */ NULL));
/* Remove again. */ /* Remove again. */
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t *) &(uint32_t) { 0x01020305 },
sizeof(uint32_t),
/* hostname= */ NULL));
/* Try to remove non-existent entry. */ /* Try to remove non-existent entry. */
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t *) &(uint32_t) { 0x01020305 },
sizeof(uint32_t),
/* hostname= */ NULL));
/* Try to remove non-existent entry. */ /* Try to remove non-existent entry. */
ASSERT_OK(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, ASSERT_OK(sd_dhcp_server_set_static_lease(
(uint8_t*) &(uint32_t) { 0x01020306 }, sizeof(uint32_t))); server,
&(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t *) &(uint32_t) { 0x01020306 },
sizeof(uint32_t),
/* hostname= */ NULL));
} }
static void test_domain_name(void) { static void test_domain_name(void) {

View File

@ -2,6 +2,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "dns-domain.h"
#include "ether-addr-util.h" #include "ether-addr-util.h"
#include "hashmap.h" #include "hashmap.h"
#include "networkd-dhcp-server-static-lease.h" #include "networkd-dhcp-server-static-lease.h"
@ -17,6 +18,7 @@ static DHCPStaticLease* dhcp_static_lease_free(DHCPStaticLease *static_lease) {
config_section_free(static_lease->section); config_section_free(static_lease->section);
free(static_lease->client_id); free(static_lease->client_id);
free(static_lease->hostname);
return mfree(static_lease); return mfree(static_lease);
} }
@ -215,3 +217,55 @@ int config_parse_dhcp_static_lease_hwaddr(
TAKE_PTR(lease); TAKE_PTR(lease);
return 0; return 0;
} }
int config_parse_dhcp_static_lease_hostname(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(dhcp_static_lease_free_or_set_invalidp) DHCPStaticLease *lease = NULL;
Network *network = ASSERT_PTR(userdata);
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
r = lease_new_static(network, filename, section_line, &lease);
if (r < 0)
return log_oom();
if (isempty(rvalue)) {
lease->hostname = mfree(lease->hostname);
TAKE_PTR(lease);
return 0;
}
r = dns_name_is_valid_ldh(rvalue);
if (r < 0)
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
if (r == 0) {
log_syntax(unit,
LOG_WARNING,
filename,
line,
0,
"Invalid hostname for DHCPv4 static lease, ignoring assignment: %s",
rvalue);
return 0;
}
r = free_and_strdup(&lease->hostname, rvalue);
if (r < 0)
return log_oom();
TAKE_PTR(lease);
return 0;
}

View File

@ -13,9 +13,11 @@ typedef struct DHCPStaticLease {
struct in_addr address; struct in_addr address;
uint8_t *client_id; uint8_t *client_id;
size_t client_id_size; size_t client_id_size;
char *hostname;
} DHCPStaticLease; } DHCPStaticLease;
void network_drop_invalid_static_leases(Network *network); void network_drop_invalid_static_leases(Network *network);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_address); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_hostname);

View File

@ -733,7 +733,12 @@ static int dhcp4_server_configure(Link *link) {
} }
HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) { HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) {
r = sd_dhcp_server_set_static_lease(link->dhcp_server, &static_lease->address, static_lease->client_id, static_lease->client_id_size); r = sd_dhcp_server_set_static_lease(
link->dhcp_server,
&static_lease->address,
static_lease->client_id,
static_lease->client_id_size,
static_lease->hostname);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m"); return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m");
} }

View File

@ -1300,6 +1300,7 @@ static int dhcp6_client_append_json(Link *link, sd_json_variant **v) {
static int dhcp_client_lease_append_json(Link *link, sd_json_variant **v) { static int dhcp_client_lease_append_json(Link *link, sd_json_variant **v) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL;
usec_t lease_timestamp_usec = USEC_INFINITY, t1 = USEC_INFINITY, t2 = USEC_INFINITY; usec_t lease_timestamp_usec = USEC_INFINITY, t1 = USEC_INFINITY, t2 = USEC_INFINITY;
const char *hostname = NULL;
int r; int r;
assert(link); assert(link);
@ -1320,10 +1321,16 @@ static int dhcp_client_lease_append_json(Link *link, sd_json_variant **v) {
if (r < 0 && r != -ENODATA) if (r < 0 && r != -ENODATA)
return r; return r;
r = sd_json_buildo(&w, r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
JSON_BUILD_PAIR_FINITE_USEC("LeaseTimestampUSec", lease_timestamp_usec), if (r < 0 && r != -ENODATA)
JSON_BUILD_PAIR_FINITE_USEC("Timeout1USec", t1), return r;
JSON_BUILD_PAIR_FINITE_USEC("Timeout2USec", t2));
r = sd_json_buildo(
&w,
JSON_BUILD_PAIR_FINITE_USEC("LeaseTimestampUSec", lease_timestamp_usec),
JSON_BUILD_PAIR_FINITE_USEC("Timeout1USec", t1),
JSON_BUILD_PAIR_FINITE_USEC("Timeout2USec", t2),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", hostname));
if (r < 0) if (r < 0)
return r; return r;

View File

@ -395,6 +395,7 @@ DHCPServer.RapidCommit, config_parse_bool,
DHCPServer.PersistLeases, config_parse_dhcp_server_persist_leases, 0, offsetof(Network, dhcp_server_persist_leases) DHCPServer.PersistLeases, config_parse_dhcp_server_persist_leases, 0, offsetof(Network, dhcp_server_persist_leases)
DHCPServerStaticLease.Address, config_parse_dhcp_static_lease_address, 0, 0 DHCPServerStaticLease.Address, config_parse_dhcp_static_lease_address, 0, 0
DHCPServerStaticLease.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0 DHCPServerStaticLease.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0
DHCPServerStaticLease.Hostname, config_parse_dhcp_static_lease_hostname, 0, 0
Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu)
Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin) Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin)

View File

@ -866,6 +866,10 @@ static int parse_argv(int argc, char *argv[]) {
"--wait may not be combined with --scope."); "--wait may not be combined with --scope.");
} }
if (arg_scope && arg_root_directory)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--root-directory= is not supported in --scope mode.");
if (same_dir && arg_root_directory && !path_equal(arg_root_directory, "/")) if (same_dir && arg_root_directory && !path_equal(arg_root_directory, "/"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--same-dir cannot be used with a root directory other than '/'"); "--same-dir cannot be used with a root directory other than '/'");

View File

@ -351,7 +351,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("T1 timeout (lease renewal time) in microseconds"), SD_VARLINK_FIELD_COMMENT("T1 timeout (lease renewal time) in microseconds"),
SD_VARLINK_DEFINE_FIELD(Timeout1USec, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Timeout1USec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("T2 timeout (lease rebinding time) in microseconds"), SD_VARLINK_FIELD_COMMENT("T2 timeout (lease rebinding time) in microseconds"),
SD_VARLINK_DEFINE_FIELD(Timeout2USec, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); SD_VARLINK_DEFINE_FIELD(Timeout2USec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Hostname received from DHCP server"),
SD_VARLINK_DEFINE_FIELD(Hostname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
PrivateOption, PrivateOption,

View File

@ -114,8 +114,6 @@ static int bus_set_address(
const char *bus_path, const char *bus_path,
RuntimeScope runtime_scope) { RuntimeScope runtime_scope) {
int r;
assert(bus); assert(bus);
switch (transport) { switch (transport) {
@ -143,8 +141,6 @@ static int bus_set_address(
default: default:
assert_not_reached(); assert_not_reached();
} }
return r;
} }
static int run(int argc, char *argv[]) { static int run(int argc, char *argv[]) {

View File

@ -79,7 +79,12 @@ int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[],
int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v); int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v);
int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v); int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v);
int sd_dhcp_server_set_static_lease(sd_dhcp_server *server, const struct in_addr *address, uint8_t *client_id, size_t client_id_size); int sd_dhcp_server_set_static_lease(
sd_dhcp_server *server,
const struct in_addr *address,
uint8_t *client_id,
size_t client_id_size,
const char *hostname);
int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char *path); int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char *path);
int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t); int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t);

View File

@ -669,12 +669,14 @@ TEST(condition_test_version) {
condition_free(condition); condition_free(condition);
/* Test glibc version */ /* Test glibc version */
bool has = !isempty(gnu_get_libc_version());
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc > 1", false, false))); ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc > 1", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ)); ASSERT_OK_EQ(condition_test(condition, environ), has);
condition_free(condition); condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc < 2", false, false))); ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc < 2", false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ)); ASSERT_OK_EQ(condition_test(condition, environ), !has);
condition_free(condition); condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc < 9999", false, false))); ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc < 9999", false, false)));
@ -686,15 +688,27 @@ TEST(condition_test_version) {
condition_free(condition); condition_free(condition);
v = strjoina("glibc = ", gnu_get_libc_version()); v = strjoina("glibc = ", gnu_get_libc_version());
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ)); if (has)
ASSERT_OK_POSITIVE(condition_test(condition, environ));
else
ASSERT_ERROR(condition_test(condition, environ), EINVAL);
condition_free(condition); condition_free(condition);
v = strjoina("glibc != ", gnu_get_libc_version()); v = strjoina("glibc != ", gnu_get_libc_version());
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false))); ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ)); if (has)
ASSERT_OK_ZERO(condition_test(condition, environ));
else
ASSERT_ERROR(condition_test(condition, environ), EINVAL);
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc $= ?*", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), has);
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc !$= ?*", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), !has);
condition_free(condition); condition_free(condition);
} }

View File

@ -16,10 +16,12 @@ DNS=9.9.9.9
[DHCPServerStaticLease] [DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc MACAddress=12:34:56:78:9a:bc
Address=10.1.1.2 Address=10.1.1.2
Hostname=testhost
[DHCPServerStaticLease] [DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc MACAddress=12:34:56:78:9a:bc
Address=10.1.1.3 Address=10.1.1.3
Hostname=device.example.com
[DHCPServerStaticLease] [DHCPServerStaticLease]
Address=10.1.1.4 Address=10.1.1.4

View File

@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
[Network]
DHCP=ipv4
IPv6AcceptRA=no
[Link]
# This MAC overrides the default to match the second static lease
MACAddress=92:12:01:87:11:19

View File

@ -0,0 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
[Network]
DHCP=ipv4
IPv6AcceptRA=no

View File

@ -0,0 +1,27 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
[Network]
Address=10.1.1.1/24
DHCPServer=yes
IPv6AcceptRA=no
[DHCPServer]
PoolOffset=100
PoolSize=50
DefaultLeaseTimeSec=60
# Scenario 1: Option 12
# Matches veth99's default MAC (from 25-veth.netdev)
[DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc
Address=10.1.1.200
Hostname=simple-host
# Scenario 2: Option 81
# Matches the MAC set by 25-dhcp-client-fqdn-hostname.network
[DHCPServerStaticLease]
MACAddress=92:12:01:87:11:19
Address=10.1.1.201
Hostname=fqdn.example.com

View File

@ -7340,6 +7340,32 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output) self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID') self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID')
def test_dhcp_server_static_lease_hostname_simple(self):
copy_network_unit('25-veth.netdev',
'25-dhcp-client-simple-hostname.network',
'25-dhcp-server-static-hostname.network')
start_networkd()
self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_json('veth99')
check_json(output)
print(output)
data = json.loads(output)
self.assertEqual(data['DHCPv4Client']['Lease']['Hostname'], 'simple-host')
def test_dhcp_server_static_lease_hostname_fqdn(self):
copy_network_unit('25-veth.netdev',
'25-dhcp-client-fqdn-hostname.network',
'25-dhcp-server-static-hostname.network')
start_networkd()
self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_json('veth99')
check_json(output)
print(output)
data = json.loads(output)
self.assertEqual(data['DHCPv4Client']['Lease']['Hostname'], 'fqdn.example.com')
class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
def setUp(self): def setUp(self):