From 98b02dd7e73fc42676537dab885c70c45b0375fc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 14 Jun 2025 00:27:06 +0900 Subject: [PATCH 1/5] sd-dhcp-server: reopen fd only when it is valid Fixes a bug in 11b88419ae0004547a0724aa459ddcb5d243f25c. --- src/libsystemd-network/sd-dhcp-server.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 099362d1ae3..335f01f1645 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -1605,9 +1605,13 @@ int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char if (!path_is_safe(path)) return -EINVAL; - _cleanup_close_ int fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH); - if (fd < 0) - return fd; + _cleanup_close_ int fd = AT_FDCWD; /* Unlike our usual coding style, AT_FDCWD needs to be set, + * to pass a 'valid' fd. */ + if (dir_fd >= 0) { + fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (fd < 0) + return fd; + } r = free_and_strdup(&server->lease_file, path); if (r < 0) From a145343b9059dab35c536d9e6b20b87e1217f508 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 14 Jun 2025 03:41:20 +0900 Subject: [PATCH 2/5] network/dhcp-server: save and load leases in runtime directory when PersistLeases=runtime With 9ccc369ff30138b9c2cf3ed8faf28d8fe42f8377, PersistLeases= is disabled on the host side virtual interfaces for containers. However, even it is not necessary to save the leases for containers on a persistent storage, still we should save them on somewhere. Otherwise, leases will be lost when networkd on the host is restarted or the host side interface is reconfigured. This introduce PersistLeases=runtime to save and load leases on runtime storage. --- man/networkd.conf.xml | 2 +- man/systemd.network.xml | 20 +++-- src/network/networkd-dhcp-server.c | 102 +++++++++++++++++------ src/network/networkd-dhcp-server.h | 9 ++ src/network/networkd-gperf.gperf | 2 +- src/network/networkd-manager.c | 2 +- src/network/networkd-manager.h | 2 +- src/network/networkd-network-gperf.gperf | 2 +- src/network/networkd-network.c | 2 +- src/network/networkd-network.h | 3 +- src/network/networkd.c | 5 +- 11 files changed, 112 insertions(+), 39 deletions(-) diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml index b053e3182ea..f611e244f5a 100644 --- a/man/networkd.conf.xml +++ b/man/networkd.conf.xml @@ -404,7 +404,7 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00 PersistLeases= Specifies the default value for per-network PersistLeases=. - Takes a boolean. See for details in + Takes a boolean or special value runtime. See for details in systemd.network5. Defaults to yes. diff --git a/man/systemd.network.xml b/man/systemd.network.xml index c3836b0446e..1b4d303fc83 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -443,7 +443,7 @@ Even if this is enabled, the DHCP server will not be started automatically and wait for the persistent storage being ready to load/save leases in the storage, unless - RelayTarget= or PersistLeases=no are specified in the + RelayTarget= or PersistLeases=no/runtime are specified in the [DHCPServer] section. It will be started after systemd-networkd-persistent-storage.service is started, which calls networkctl persistent-storage yes. See @@ -4140,16 +4140,24 @@ ServerAddress=192.168.0.1/24 PersistLeases= - Takes a boolean. When true, the DHCP server will load and save leases in the persistent - storage. When false, the DHCP server will neither load nor save leases in the persistent storage. - Hence, bound leases will be lost when the interface is reconfigured e.g. by + Takes a boolean or special value runtime. When yes, the + DHCP server will load and save leases in the persistent storage. When runtime, + the DHCP server will load and save leases in the runtime storage, hence bound leases will be lost + when the runtime storage is cleared by e.g. by calling + systemctl clean systemd-networkd.service or the system is rebooted. When + no, the DHCP server will neither load nor save leases in the persistent storage, + hence bound leases will be lost when the interface is reconfigured e.g. by networkctl reconfigure, or systemd-networkd.service8 - is restarted. That may cause address conflict on the network. So, please take an extra care when - disable this setting. When unspecified, the value specified in the same setting in + is restarted. Using runtime and no may cause address conflict + on the network after the leases are lost. So, please take an extra care when disable this setting. + When unspecified, the value specified in the same setting in networkd.conf5, which defaults to yes, will be used. + When RelayTarget= is specified, this setting will be ignored and no leases + will be saved, as there will be no bound lease on the server. + diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index 6679e4ed41d..e36d80e03fc 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -27,6 +27,7 @@ #include "path-util.h" #include "set.h" #include "socket-netlink.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" @@ -150,13 +151,13 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) { return 0; } -static bool dhcp_server_persist_leases(Link *link) { +static DHCPServerPersistLeases link_get_dhcp_server_persist_leases(Link *link) { assert(link); assert(link->manager); assert(link->network); if (in4_addr_is_set(&link->network->dhcp_server_relay_target)) - return false; /* On relay mode. Nothing saved in the persistent storage. */ + return DHCP_SERVER_PERSIST_LEASES_NO; /* On relay mode. Nothing saved in the persistent storage. */ if (link->network->dhcp_server_persist_leases >= 0) return link->network->dhcp_server_persist_leases; @@ -164,9 +165,47 @@ static bool dhcp_server_persist_leases(Link *link) { return link->manager->dhcp_server_persist_leases; } +static int link_get_dhcp_server_lease_file(Link *link, int *ret_dir_fd, char **ret_path) { + assert(link); + assert(link->ifname); + assert(ret_dir_fd); + assert(ret_path); + + /* This does not copy fd. Do not close fd stored in ret_dir_fd. */ + + switch (link_get_dhcp_server_persist_leases(link)) { + case DHCP_SERVER_PERSIST_LEASES_NO: + *ret_dir_fd = -EBADF; + *ret_path = NULL; + return 0; + + case DHCP_SERVER_PERSIST_LEASES_YES: { + if (link->manager->persistent_storage_fd < 0) + return -EBUSY; /* persistent storage is not ready. */ + + char *p = path_join("dhcp-server-lease", link->ifname); + if (!p) + return -ENOMEM; + + *ret_dir_fd = link->manager->persistent_storage_fd; + *ret_path = p; + return 1; + } + case DHCP_SERVER_PERSIST_LEASES_RUNTIME: { + char *p = path_join("/run/systemd/netif/dhcp-server-lease", link->ifname); + if (!p) + return -ENOMEM; + + *ret_dir_fd = AT_FDCWD; + *ret_path = p; + return 1; + } + default: + assert_not_reached(); + } +} + int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret) { - struct in_addr a; - uint8_t prefixlen; int r; assert(link); @@ -185,18 +224,18 @@ int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *addr if (!link_dhcp4_server_enabled(link)) return -ENOENT; - if (!dhcp_server_persist_leases(link)) + _cleanup_free_ char *lease_file = NULL; + int dir_fd; + r = link_get_dhcp_server_lease_file(link, &dir_fd, &lease_file); + if (r < 0) + return r; + if (r == 0) /* persistent leases is disabled */ return -ENOENT; - if (link->manager->persistent_storage_fd < 0) - return -EBUSY; /* The persistent storage is not ready, try later again. */ - - _cleanup_free_ char *lease_file = path_join("dhcp-server-lease", link->ifname); - if (!lease_file) - return -ENOMEM; - + struct in_addr a; + uint8_t prefixlen; r = dhcp_server_leases_file_get_server_address( - link->manager->persistent_storage_fd, + dir_fd, lease_file, &a, &prefixlen); @@ -234,16 +273,15 @@ int link_start_dhcp4_server(Link *link) { /* TODO: Maybe, also check the system time is synced. If the system does not have RTC battery, then * the realtime clock in not usable in the early boot stage, and all saved leases may be wrongly * handled as expired and dropped. */ - if (dhcp_server_persist_leases(link)) { - - if (link->manager->persistent_storage_fd < 0) - return 0; /* persistent storage is not ready. */ - - _cleanup_free_ char *lease_file = path_join("dhcp-server-lease", link->ifname); - if (!lease_file) - return -ENOMEM; - - r = sd_dhcp_server_set_lease_file(link->dhcp_server, link->manager->persistent_storage_fd, lease_file); + _cleanup_free_ char *lease_file = NULL; + int dir_fd; + r = link_get_dhcp_server_lease_file(link, &dir_fd, &lease_file); + if (r == -EBUSY) + return 0; /* persistent storage is not ready. */ + if (r < 0) + return r; + if (r > 0) { + r = sd_dhcp_server_set_lease_file(link->dhcp_server, dir_fd, lease_file); if (r < 0) return r; } @@ -265,7 +303,7 @@ void manager_toggle_dhcp4_server_state(Manager *manager, bool start) { HASHMAP_FOREACH(link, manager->links_by_index) { if (!link->dhcp_server) continue; - if (!dhcp_server_persist_leases(link)) + if (link_get_dhcp_server_persist_leases(link) != DHCP_SERVER_PERSIST_LEASES_YES) continue; /* Even if 'start' is true, first we need to stop the server. Otherwise, we cannot (re)set @@ -916,3 +954,19 @@ int config_parse_dhcp_server_ipv6_only_preferred( *usec = t; return 0; } + +static const char* const dhcp_server_persist_leases_table[_DHCP_SERVER_PERSIST_LEASES_MAX] = { + [DHCP_SERVER_PERSIST_LEASES_NO] = "no", + [DHCP_SERVER_PERSIST_LEASES_YES] = "yes", + [DHCP_SERVER_PERSIST_LEASES_RUNTIME] = "runtime", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN( + dhcp_server_persist_leases, + DHCPServerPersistLeases, + DHCP_SERVER_PERSIST_LEASES_YES); + +DEFINE_CONFIG_PARSE_ENUM( + config_parse_dhcp_server_persist_leases, + dhcp_server_persist_leases, + DHCPServerPersistLeases); diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index 419088f3066..e46ad3a8579 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -3,6 +3,14 @@ #include "networkd-forward.h" +typedef enum DHCPServerPersistLeases { + DHCP_SERVER_PERSIST_LEASES_NO, + DHCP_SERVER_PERSIST_LEASES_YES, + DHCP_SERVER_PERSIST_LEASES_RUNTIME, + _DHCP_SERVER_PERSIST_LEASES_MAX, + _DHCP_SERVER_PERSIST_LEASES_INVALID = -EINVAL, +} DHCPServerPersistLeases; + int network_adjust_dhcp_server(Network *network, Set **addresses); int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret); int link_request_dhcp_server(Link *link); @@ -14,3 +22,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ipv6_only_preferred); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_persist_leases); diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index 2eb847d119e..3684a696ff8 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -44,7 +44,7 @@ DHCPv4.DUIDRawData, config_parse_duid_rawdata, DHCPv6.UseDomains, config_parse_use_domains, 0, offsetof(Manager, dhcp6_use_domains) DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp6_duid) DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp6_duid) -DHCPServer.PersistLeases, config_parse_bool, 0, offsetof(Manager, dhcp_server_persist_leases) +DHCPServer.PersistLeases, config_parse_dhcp_server_persist_leases, 0, offsetof(Manager, dhcp_server_persist_leases) /* Deprecated */ DHCP.DUIDType, config_parse_manager_duid_type, 0, 0 DHCP.DUIDRawData, config_parse_manager_duid_rawdata, 0, 0 diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index fad4d82183d..a0455544cf4 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -635,7 +635,7 @@ int manager_new(Manager **ret, bool test_mode) { .dhcp_duid.type = DUID_TYPE_EN, .dhcp6_duid.type = DUID_TYPE_EN, .duid_product_uuid.type = DUID_TYPE_UUID, - .dhcp_server_persist_leases = true, + .dhcp_server_persist_leases = DHCP_SERVER_PERSIST_LEASES_YES, .serialization_fd = -EBADF, .ip_forwarding = { -1, -1, }, #if HAVE_VMLINUX_H diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index db669d170d2..d0897e76ad0 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -36,7 +36,7 @@ typedef struct Manager { bool manage_foreign_routes; bool manage_foreign_rules; bool manage_foreign_nexthops; - bool dhcp_server_persist_leases; + DHCPServerPersistLeases dhcp_server_persist_leases; Set *dirty_links; Set *new_wlan_ifindices; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 36b833ef369..577dccc9eb0 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -389,7 +389,7 @@ DHCPServer.BootServerAddress, config_parse_in_addr_non_null, DHCPServer.BootServerName, config_parse_dns_name, 0, offsetof(Network, dhcp_server_boot_server_name) DHCPServer.BootFilename, config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, offsetof(Network, dhcp_server_boot_filename) DHCPServer.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp_server_rapid_commit) -DHCPServer.PersistLeases, config_parse_tristate, 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.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0 Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 69fa1983da4..961b431a68e 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -435,7 +435,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_server_emit_router = true, .dhcp_server_emit_timezone = true, .dhcp_server_rapid_commit = true, - .dhcp_server_persist_leases = -1, + .dhcp_server_persist_leases = _DHCP_SERVER_PERSIST_LEASES_INVALID, .router_lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC, .router_dns_lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 51c9160760f..0e14cfa7b5b 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -11,6 +11,7 @@ #include "network-util.h" #include "networkd-bridge-vlan.h" #include "networkd-dhcp-common.h" +#include "networkd-dhcp-server.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" #include "networkd-dns.h" @@ -228,7 +229,7 @@ typedef struct Network { char *dhcp_server_boot_filename; usec_t dhcp_server_ipv6_only_preferred_usec; bool dhcp_server_rapid_commit; - int dhcp_server_persist_leases; + DHCPServerPersistLeases dhcp_server_persist_leases; /* link-local addressing support */ AddressFamily link_local; diff --git a/src/network/networkd.c b/src/network/networkd.c index dda1a4cf267..29ee7c52b3c 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -69,8 +69,9 @@ static int run(int argc, char *argv[]) { /* Always create the directories people can create inotify watches in. It is necessary to create the * following subdirectories after drop_privileges() to make them owned by systemd-network. */ FOREACH_STRING(p, - "/run/systemd/netif/links/", - "/run/systemd/netif/leases/") { + "/run/systemd/netif/dhcp-server-lease/", + "/run/systemd/netif/leases/", + "/run/systemd/netif/links/") { r = mkdir_safe_label(p, 0755, UID_INVALID, GID_INVALID, MKDIR_WARN_MODE); if (r < 0) log_warning_errno(r, "Could not create directory '%s': %m", p); From 009d64dddd5a85117984be8610c1c41cad0b9036 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 14 Jun 2025 03:42:24 +0900 Subject: [PATCH 3/5] test-network: add test case for PersistLeases=runtime --- .../conf/persist-leases-runtime.conf | 3 ++ test/test-network/systemd-networkd-tests.py | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 test/test-network/conf/persist-leases-runtime.conf diff --git a/test/test-network/conf/persist-leases-runtime.conf b/test/test-network/conf/persist-leases-runtime.conf new file mode 100644 index 00000000000..6f1b7374c32 --- /dev/null +++ b/test/test-network/conf/persist-leases-runtime.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[DHCPServer] +PersistLeases=runtime diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7d8a7c8f3fa..176eada5f97 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -7004,7 +7004,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): def tearDown(self): tear_down_common() - def check_dhcp_server(self, persist_leases=True): + def check_dhcp_server(self, persist_leases='yes'): output = networkctl_status('veth99') print(output) self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)') @@ -7016,11 +7016,16 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*") - if persist_leases: - with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f: - check_json(f.read()) + if persist_leases == 'yes': + path = '/var/lib/systemd/network/dhcp-server-lease/veth-peer' + elif persist_leases == 'runtime': + path = '/run/systemd/netif/dhcp-server-lease/veth-peer' else: - self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer')) + path = None + + if path: + with open(path, encoding='utf-8') as f: + check_json(f.read()) def test_dhcp_server(self): copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network') @@ -7046,7 +7051,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') - self.check_dhcp_server(persist_leases=False) + self.check_dhcp_server(persist_leases='no') remove_networkd_conf_dropin('persist-leases-no.conf') with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f: @@ -7054,7 +7059,23 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): restart_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') - self.check_dhcp_server(persist_leases=False) + self.check_dhcp_server(persist_leases='no') + + def test_dhcp_server_persist_leases_runtime(self): + copy_networkd_conf_dropin('persist-leases-runtime.conf') + copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network') + start_networkd() + self.wait_online('veth99:routable', 'veth-peer:routable') + + self.check_dhcp_server(persist_leases='runtime') + + remove_networkd_conf_dropin('persist-leases-runtime.conf') + with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f: + f.write('[DHCPServer]\nPersistLeases=runtime') + restart_networkd() + self.wait_online('veth99:routable', 'veth-peer:routable') + + self.check_dhcp_server(persist_leases='runtime') def test_dhcp_server_null_server_address(self): copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network') From b5610d636047e0c703b575c5c2af4e8ce4c8308a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 14 Jun 2025 03:46:17 +0900 Subject: [PATCH 4/5] network: use PersistLeases=runtime on container/VM/namespace network interfaces Follow-up for 9ccc369ff30138b9c2cf3ed8faf28d8fe42f8377. Then, we can safely restart networkd or reconfigure the interface. --- network/80-container-ve.network | 2 +- network/80-container-vz.network | 2 +- network/80-namespace-ns-tun.network | 2 +- network/80-namespace-ns.network | 2 +- network/80-vm-vt.network | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/network/80-container-ve.network b/network/80-container-ve.network index 69c534f4e1b..a70a24cbcb1 100644 --- a/network/80-container-ve.network +++ b/network/80-container-ve.network @@ -29,4 +29,4 @@ IPv6AcceptRA=no IPv6SendRA=yes [DHCPServer] -PersistLeases=no +PersistLeases=runtime diff --git a/network/80-container-vz.network b/network/80-container-vz.network index 2dc5d87e23b..09eadf48db1 100644 --- a/network/80-container-vz.network +++ b/network/80-container-vz.network @@ -28,4 +28,4 @@ IPv6AcceptRA=no IPv6SendRA=yes [DHCPServer] -PersistLeases=no +PersistLeases=runtime diff --git a/network/80-namespace-ns-tun.network b/network/80-namespace-ns-tun.network index fe084838a9b..658eac88d8f 100644 --- a/network/80-namespace-ns-tun.network +++ b/network/80-namespace-ns-tun.network @@ -29,4 +29,4 @@ IPv6AcceptRA=no IPv6SendRA=yes [DHCPServer] -PersistLeases=no +PersistLeases=runtime diff --git a/network/80-namespace-ns.network b/network/80-namespace-ns.network index cd1a819973e..2fb422a8bb9 100644 --- a/network/80-namespace-ns.network +++ b/network/80-namespace-ns.network @@ -29,4 +29,4 @@ IPv6AcceptRA=no IPv6SendRA=yes [DHCPServer] -PersistLeases=no +PersistLeases=runtime diff --git a/network/80-vm-vt.network b/network/80-vm-vt.network index a7c0f770893..cb968064398 100644 --- a/network/80-vm-vt.network +++ b/network/80-vm-vt.network @@ -28,4 +28,4 @@ IPv6AcceptRA=no IPv6SendRA=yes [DHCPServer] -PersistLeases=no +PersistLeases=runtime From 5509fa845056e6b7de7768a7540db918364b7be8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 14 Jun 2025 03:53:03 +0900 Subject: [PATCH 5/5] NEWS: mention PersistLeases=runtime in [DHCPServer] --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 6f4e43da4b3..54c7cdb4876 100644 --- a/NEWS +++ b/NEWS @@ -398,6 +398,10 @@ CHANGES WITH 258 in spe: networkd.conf. (Previously this had to be configured individually in each .network file.) + * PersistLeases= setting in [DHCPServer] section now also accepts + "runtime", to make the DHCP server saves and loads bound leases on + the runtime storage. + * A new Preference= setting has been added to the [IPv6RoutePrefix] section to configure the route preference field.