From: Yu Watanabe Date: Fri, 13 Jun 2025 18:41:20 +0000 (+0900) Subject: network/dhcp-server: save and load leases in runtime directory when PersistLeases... X-Git-Tag: v258-rc1~303^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a145343b9059dab35c536d9e6b20b87e1217f508;p=thirdparty%2Fsystemd.git 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. --- 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);