diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 6e5a2702ba..1aa9b132df 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1623,6 +1623,15 @@
+
+ FallbackLeaseLifetimeSec=
+
+ Allows to set DHCPv4 lease lifetime when DHCPv4 server does not send the lease lifetime.
+ Takes one of forever or infinity means that the address
+ never expires. Defaults to unset.
+
+
+
SendRelease=
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 78d93382e7..ce83a86aa9 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -87,6 +87,7 @@ struct sd_dhcp_client {
char *mudurl;
char **user_class;
uint32_t mtu;
+ uint32_t fallback_lease_lifetime;
uint32_t xid;
usec_t start_time;
uint64_t attempt;
@@ -612,6 +613,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
return 0;
}
+int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
+ assert_return(client, -EINVAL);
+ assert_return(fallback_lease_lifetime > 0, -EINVAL);
+
+ client->fallback_lease_lifetime = fallback_lease_lifetime;
+
+ return 0;
+}
+
static int client_notify(sd_dhcp_client *client, int event) {
assert(client);
@@ -1405,6 +1415,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
lease->next_server = offer->siaddr;
lease->address = offer->yiaddr;
+ if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
+ lease->lifetime = client->fallback_lease_lifetime;
+
if (lease->address == 0 ||
lease->server_address == 0 ||
lease->lifetime == 0) {
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 6bfa6e6dd0..42e6b5aef2 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -1488,6 +1488,12 @@ int dhcp4_configure(Link *link) {
return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ip service type: %m");
}
+ if (link->network->dhcp_fallback_lease_lifetime > 0) {
+ r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m");
+ }
+
if (link->network->dhcp_send_decline) {
r = configure_dhcpv4_duplicate_address_detection(link);
if (r < 0)
@@ -1674,6 +1680,44 @@ int config_parse_dhcp_mud_url(
return free_and_strdup_warn(&network->dhcp_mudurl, unescaped);
}
+int config_parse_dhcp_fallback_lease_lifetime(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) {
+ Network *network = userdata;
+ unsigned k;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->dhcp_fallback_lease_lifetime = 0;
+ return 0;
+ }
+
+ /* We accept only "forever" or "infinity". */
+ if (STR_IN_SET(rvalue, "forever", "infinity"))
+ k = CACHE_INFO_INFINITY_LIFE_TIME;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid LeaseLifetime= value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ network->dhcp_fallback_lease_lifetime = k;
+
+ return 0;
+}
+
static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
[DHCP_CLIENT_ID_MAC] = "mac",
[DHCP_CLIENT_ID_DUID] = "duid",
diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h
index 93fa7d372c..a6e24be78d 100644
--- a/src/network/networkd-dhcp4.h
+++ b/src/network/networkd-dhcp4.h
@@ -27,3 +27,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 28bb705190..3749ff6b8d 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -189,6 +189,7 @@ DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type,
DHCPv4.SendOption, config_parse_dhcp_send_option, AF_INET, offsetof(Network, dhcp_client_send_options)
DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options)
DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu)
+DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0
DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns)
DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp)
DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 82810bcce1..6811658fcc 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -97,6 +97,7 @@ struct Network {
uint64_t dhcp_max_attempts;
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
+ uint32_t dhcp_fallback_lease_lifetime;
uint32_t dhcp_route_mtu;
uint16_t dhcp_client_port;
int dhcp_critical;
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 032afc9f12..85b49bae74 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -185,6 +185,9 @@ int sd_dhcp_client_get_lease(
int sd_dhcp_client_set_service_type(
sd_dhcp_client *client,
int type);
+int sd_dhcp_client_set_fallback_lease_lifetime(
+ sd_dhcp_client *client,
+ uint32_t fallback_lease_lifetime);
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 918fb3ac8a..625842a9ba 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -105,6 +105,7 @@ SendVendorOption=
SendDecline=
MUDURL=
RouteMTUBytes=
+FallbackLeaseLifetimeSec=
[DHCPv6]
UseNTP=
UseDNS=