dhcpv4: lazy store statefiles
authorDavid Härdeman <[email protected]>
Fri, 10 Oct 2025 17:55:36 +0000 (19:55 +0200)
committerÁlvaro Fernández Rojas <[email protected]>
Wed, 5 Nov 2025 14:58:40 +0000 (15:58 +0100)
Currently, the dhcpv4 server saves the statefile on every change and
also calls the leasetrigger script every time the statefile is updated
(which triggers a dnsmasq reload).

In addition, odhcpd wakes up every second (see dhcpv4_valid_until_cb())
to go through all existing leases to see if any lease has expired.

With this change, the wakeups are reduced to every 5 seconds, and the
statefile is only written (if necessary) during that wakeup.

Before this change (without any leasetrigger, test on my laptop, not a
real OpenWrt device):

$ time sudo ./build/dhcpdig -4 benchmark foo-client

Thread[**]: req 10000101/0 rel 10000101/0 rep 10000101/0

real 55m29.406s
user 0m0.005s
sys 0m0.010s

(This is a simple benchmark tool I wrote, it runs 10 threads which
divide the DHVPv4 pool into 10 chunks and then performs 1 million random
addr req/release per thread in a loop).

After this change:

$ time sudo ./build/dhcpdig -4 benchmark foo-client

Thread[**]: req 10000101/0 rel 10000101/0 rep 10000101/0

real 1m48.123s
user 0m0.005s
sys 0m0.005s

A 3082% speedup.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/298
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/config.c
src/dhcpv4.c
src/odhcpd.h

index 63e3952f666d6924d3c85641fee56fc56bca0b52..6917f9adaf07007f64b6bf121eed756810b582ee 100644 (file)
@@ -344,6 +344,7 @@ static void set_interface_defaults(struct interface *iface)
        iface->ra_lifetime = 3 * iface->ra_maxinterval; /* RFC4861: AdvDefaultLifetime: Default: 3 * MaxRtrAdvInterval */
        iface->ra_dns = true;
        iface->pio_update = false;
+       iface->update_statefile = true;
 }
 
 static void clean_interface(struct interface *iface)
index aaf6821946f354aae6760e9aadde9854c9e52ee0..d5177c61b50c6c32bd5f8fa73874dbc4deee220b 100644 (file)
@@ -336,6 +336,9 @@ void dhcpv4_free_lease(struct dhcpv4_lease *lease)
        if (lease->fr_ip)
                dhcpv4_fr_stop(lease);
 
+       if (lease->iface)
+               lease->iface->update_statefile = true;
+
        list_del(&lease->head);
        if (lease->lease_cfg)
                lease->lease_cfg->dhcpv4_lease = NULL;
@@ -407,6 +410,8 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcpv4_lease *lease,
 
                debug("Assigned static IP address: %s",
                      inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
+
+               iface->update_statefile = true;
                return true;
        }
 
@@ -423,6 +428,7 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcpv4_lease *lease,
        } else {
                debug("Assigned the requested IP address: %s",
                      inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
+               iface->update_statefile = true;
                return true;
        }
 
@@ -448,6 +454,8 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcpv4_lease *lease,
                        debug("Assigned IP adress from pool: %s (succeeded on attempt %u of %u)",
                              inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)),
                              i + 1, count);
+
+                       iface->update_statefile = true;
                        return true;
                }
        }
@@ -624,7 +632,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
                return NULL;
        }
 
-       dhcpv6_ia_write_statefile();
+       iface->update_statefile = true;
        return lease;
 }
 
@@ -1498,6 +1506,7 @@ static void dhcpv4_valid_until_cb(struct uloop_timeout *event)
 {
        struct interface *iface;
        time_t now = odhcpd_time();
+       bool update_statefile = false;
 
        avl_for_each_element(&interfaces, iface, avl) {
                struct dhcpv4_lease *lease, *tmp;
@@ -1511,10 +1520,20 @@ static void dhcpv4_valid_until_cb(struct uloop_timeout *event)
                                                      (struct in_addr *)&lease->addr,
                                                      lease->hostname, iface->ifname);
                                dhcpv4_free_lease(lease);
+                               update_statefile = true;
                        }
                }
+
+               if (iface->update_statefile) {
+                       update_statefile = true;
+                       iface->update_statefile = false;
+               }
        }
-       uloop_timeout_set(event, 1000);
+
+       if (update_statefile)
+               dhcpv6_ia_write_statefile();
+
+       uloop_timeout_set(event, 5000);
 }
 
 /* Create socket and register events */
index 41c0099116f29bee5447c4de31d8f3473ac33fa8..2682e4437830394f60ecbbb5519f22db4574d7b8 100644 (file)
@@ -338,6 +338,7 @@ struct interface {
        char *ifname;
        const char *name;
        uint32_t if_mtu;
+       bool update_statefile;
 
        // IPv6 runtime data
        struct odhcpd_ipaddr *addr6;