David Härdeman [Thu, 20 Nov 2025 12:51:25 +0000 (13:51 +0100)]
dhcpv4: remove iface->dhcpv4_bcast
This is also covered by iface->dhcpv4_own_ip
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Thu, 20 Nov 2025 12:47:13 +0000 (13:47 +0100)]
dhcpv4: remove iface->dhcpv4_mask
This is already covered by iface->dhcpv4_own_ip
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 19 Nov 2025 23:29:18 +0000 (00:29 +0100)]
dhcpv4: rename iface->dhcpv4_local -> iface->dhcpv4_own_ip
"local" isn't very informative, the local what?
In addition, use a struct odhpd_ipaddr instead of a struct in_addr, the
former can already store the prefix_len, broadcast and netmask address,
this will allow the separate iface->dhcpv4_bcast and iface->dhcpv4_mask
members to be removed (see subsequent patches).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 19 Nov 2025 18:43:36 +0000 (19:43 +0100)]
dhcpv4: simplify pool determination
This simplifies the logic to determine a dynamic dhcp pool range based
on the prefix length (when "start" and "limit" aren't set).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Tue, 18 Nov 2025 07:55:41 +0000 (08:55 +0100)]
netlink: add netmask for IPv4
Add a netmask member to struct odhcpd_ipaddr, this is mostly for
convenience.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Tue, 18 Nov 2025 07:53:37 +0000 (08:53 +0100)]
netlink: variable naming cleanup
Correct some comments, use boolean where a boolean value is expected,
rename more "struct odhcpd_ipaddr" variables to indicate that it is a
struct odhcpd_ipaddr and indicate plural/IPv4/IPv6 in the variable
names.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 17 Nov 2025 23:32:21 +0000 (00:32 +0100)]
dhcpv4: fix no_dynamic_dhcp
This fixes the support for running with no_dynamic_dhcp in the DHCPv4
server.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 17 Nov 2025 22:58:48 +0000 (23:58 +0100)]
odhcpd: rename iface->addr4 -> iface->oaddrs4
Rename iface->addr4 to iface->oaddrs4, to make it clearer from a cursory
glance that this isn't e.g. "struct in_addr" addresses but a number of
"struct odhcpd_ipaddr" addresses.
At the same time, rename iface->addr4_len to iface->oaddrs4_cnt, to make
it clear that this is a count and not e.g. an allocation length.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 17 Nov 2025 22:37:50 +0000 (23:37 +0100)]
odhcpd: rename prefix -> prefix_len
Change a bunch of places where a prefix length is stored as "prefix" to
instead call the length "prefix_len", to make it clearer that it is not
a prefix that is stored.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 17 Nov 2025 22:11:08 +0000 (23:11 +0100)]
odhcpd: rename union if_addr -> in46_addr
Anyone reading "union if_addr" might be tempted to think it's got
something to do with an interface addr (e.g. a MAC, or something). In
reality, it's an IPv4/IPv6 addr (i.e. struct in_addr or struct
in6_addr), so rename it to make things clearer.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 17 Nov 2025 21:51:34 +0000 (22:51 +0100)]
dhcpv4: improve pool var naming
The uci cfg options might have somewhat confusing names, but
that doesn't mean we have to perpetuate those names throughout the
source.
Also, move the checking of whether the pool parameters from when
we are setting up a pool to where the cfg variables are actually set
from config.
Finally, there's no need to store the pool start and end as "struct
in_addr" in struct interface, because the start and end actually aren't
IPv4 addresses.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 17 Oct 2025 13:32:08 +0000 (15:32 +0200)]
ndp: correctness fix for BPF filter
Note that AF_PACKET sockets start receiving packets as soon as they are
created. Thus, a packet can arrive between the creation of the socket
and the time the real filter is installed. Fix this using the same
technique as used in libpcap, i.e. by installing a drop-all filter,
removing any packets from the socket, then installing the real filter
later (an atomic operation which replaces the drop filter).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/319
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 17 Oct 2025 12:49:25 +0000 (14:49 +0200)]
dhcpv4: add BPF to dhcpv4_setup_interface()
This means the kernel won't even pass packets which we are not
interested in.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/318
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 17 Oct 2025 11:59:37 +0000 (13:59 +0200)]
dhcpv4: define val where it is used in dhcpv4_setup_interface()
This makes it a tiny bit easier to see immediately what "val" is.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/318
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 17 Oct 2025 11:57:14 +0000 (13:57 +0200)]
dhcpv4: simplify error handling in dhcpv4_setup_interface()
Remove the "ret" variable which can anyway always just be -1 on error,
and rename the "out" label to "error" to clarify that it's only used on
error.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/318
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 17 Oct 2025 11:54:50 +0000 (13:54 +0200)]
dhcvp4: use tmp fd in dhcpv4_setup_interface()
This makes the code a little bit easier to read.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/318
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 17 Oct 2025 11:43:17 +0000 (13:43 +0200)]
dhcpv4: replace ToS precedence
The IPTOS_PREC_* values are deprecated since RFC2474 §4.2.2.1 (Dec 1998)
replaced them with class selector codepoints.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/318
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 23 Nov 2025 18:34:51 +0000 (19:34 +0100)]
statefiles: fix off-by-one-bug
The string with the final hostsfile name is "%s.%s", so we need two
extra bytes for the separating "." and for the trailing null byte.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/322
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Mon, 17 Nov 2025 19:43:15 +0000 (20:43 +0100)]
all: implement RFC8910 captive portal (CP) option for DHCPv4
https://www.rfc-editor.org/rfc/rfc8910.html
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/315
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Sun, 16 Nov 2025 12:47:34 +0000 (13:47 +0100)]
all: implement RFC8910 captive portal (CP) option
https://www.rfc-editor.org/rfc/rfc8910.html
RFC8910 defines a captive portal API URI for a CP client to consume.
With captive_portal_uri set to 'https://test.example.com'
Produces in RA:
ICMPv6 Option (DHCP Captive-Portal)
Type: DHCP Captive-Portal (37)
Length: 4 (32 bytes)
Captive Portal: https://test.example.com
And in DHCPv6 Reply:
Captive Portal
Option: Captive Portal (103)
Length: 24
Captive Portal: https://test.example.com
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/315
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Mon, 17 Nov 2025 14:58:49 +0000 (15:58 +0100)]
github: ci: add MIPS64, PowerPC64 and RISCV64
MIPS64, PowerPC64 and RISCV64 are popular OpenWrt archs.
Refactor the sizes build step to generate the table programatically.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Fri, 7 Nov 2025 18:54:41 +0000 (19:54 +0100)]
router: Modify relayed RA PIO L and RA M/O flags according to interface policy
Effectively:
-clear the L flag
-apply local M and O flags if we are not DHCPv6 relay mode
h/t user @Shine-
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/300
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Mon, 17 Nov 2025 01:15:51 +0000 (02:15 +0100)]
dhcpv6: prevent network loop scenario
It's possible for odhcpd to talk with its (odhcp6c) self in a network loop
scenario with strange results. Prevent this by returning early.
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/314
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 11 Oct 2025 09:25:26 +0000 (11:25 +0200)]
dhcpv4: simplify random address selection
Simplify the generation of random addresses, and make the code easier
to follow, by using the same approach as in dhcpv6.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/313
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 16 Nov 2025 20:10:40 +0000 (21:10 +0100)]
odhcpd: update devel-build.sh
Now that ubus can be disabled at runtime, we can default to building
it in, making it easier to catch compile-time errors in ubus.c.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/312
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 16 Nov 2025 17:55:59 +0000 (18:55 +0100)]
odhcpd: make ubus optional at runtime
This is mostly for developer convenience, allowing ubus to be built in, but
disabled at will when launching odhcpd.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/312
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 16 Nov 2025 13:00:36 +0000 (14:00 +0100)]
odhcpd: simplify signal handling
Currently, odhcpd's main() function will:
- call uloop_init()
- set up some signal handlers for SIGUSR1/SIGINT/SIGTERM
(SIGUSR1 is ignored, the other handlers call uloop_end())
- call odhcpd_run(), which will:
- set up some signal handlers for SIGTERM/SIGINT/SIGHUP
(the first two call uloop_end(), SIGHUP calls odhcpd_reload())
Note that uloop_init() will call uloop_setup_signals() which will call
uloop_setup_signals() which will install handlers for SIGINT/SIGTERM
which will set uloop_cancelled to true (e.g. the same thing that
uloop_end() does).
In other words, the signal handlers are modified three times, and since
the default "exit" action is to call uloop_end(), there's nothing that
will actually react to signals until uloop_run() is called, meaning that
the startup will be uninterruptible if e.g. ubus_init() fails since
it'll just retry in a loop with no handling of signals.
So, simplify and fix this by letting uloop handle the signals and
checking if uloop has been told to exit, removing a bunch of
special-purpose code.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/312
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Thu, 13 Nov 2025 10:28:12 +0000 (11:28 +0100)]
src: improve attributes
- Properly guard odhcp6c.h attributes with `#ifndef` to avoid redefining
them when including external headers.
- Also convert the remaining __attribute__ usages to the custom
declarations.
- Consolidate custom declarations with _o_ prefix.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/307
Álvaro Fernández Rojas [Fri, 14 Nov 2025 06:57:21 +0000 (07:57 +0100)]
src: consolidate and improve fallthrough
- Enable fallthrough warnings.
- Use fallthrough attribute instead of comments.
- Drop unneeded fallthroughs (no code between two case statements).
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/307
Álvaro Fernández Rojas [Fri, 14 Nov 2025 16:42:35 +0000 (17:42 +0100)]
dhcpv6-ia: add missing limits header
Fix the following build error by including <limits.h>:
src/dhcpv6-ia.c: In function 'handle_addrlist_change':
src/dhcpv6-ia.c:495:53: error: 'INT_MAX' undeclared (first use in this function)
495 | a->fr_cnt = INT_MAX;
| ^~~~~~~
src/dhcpv6-ia.c:33:1: note: 'INT_MAX' is defined in header '<limits.h>'; did you forget to '#include <limits.h>'?
32 | #include <libubox/md5.h>
+++ |+#include <limits.h>
33 |
src/dhcpv6-ia.c:495:53: note: each undeclared identifier is reported only once for each function it appears in
495 | a->fr_cnt = INT_MAX;
| ^~~~~~~
Fixes: 7136fbe390a5 ("dhcpv6-ia: split statefile handling to separate file")
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/311
Fei Lv [Fri, 14 Nov 2025 07:31:22 +0000 (15:31 +0800)]
config: fix memleak during odhcpd reload
- The memset in close_interface reset the pios pointer before it
could be freed, causing a memory leak. Relocate the free call
to clean_interface to ensure proper deallocation.
- Use realloc instead of malloc in config_load_ra_pio()
This function may be called multiple times during odhcpd reload,
and using malloc without freeing the previous allocation was
causing memory leaks.
Signed-off-by: Fei Lv <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/309
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 10 Nov 2025 22:36:27 +0000 (23:36 +0100)]
dhcpv4: update ubus DHCPv4 events/methods
This is based on the assumption that we don't really have any consumers
of ubus DHCPv4 events (yet).
With that in mind, rename and simplify the event function (yes, we
should add a sibling function for DHCPv6 leases later).
Also, take the chance to improve some naming, and introduce additional
attributes to the ubus event/method (the interface name and the
DUID/IAID, if known).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/306
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 10 Nov 2025 22:19:31 +0000 (23:19 +0100)]
odhcpd: rename dhcpv6_lease->clid[_data|_len]
The option is called clientid, but the option carries a DUID. So what
we're storing is a DUID, not a CLID.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/306
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Mon, 10 Nov 2025 22:03:25 +0000 (23:03 +0100)]
odhcpd: rename [lease_cfg|dhcpv4_lease]->ipaddr to ipv4
"ipaddr" doesn't say if it's IPv4 or IPv6, and we want to be clear in a
dual-stack codebase.
And at the same time, use a struct in_addr to store the IPv4 address,
which is just less confusing.
Finally, remove all uses of "inet_ntoa()", which is deprecated.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/306
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 11 Oct 2025 06:29:07 +0000 (08:29 +0200)]
dhcpv4: use an AVL to store leases
An AVL has O(log n) complexity vs O(n) for a linked list in all the
operations we care about (insertion and search).
Also, it makes the code simpler by not having to care about details like
how to sort the leases.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/306
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Fri, 14 Nov 2025 13:16:16 +0000 (14:16 +0100)]
github: ci: add powerpc arch
PowerPC is another popular OpenWrt arch.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Fri, 14 Nov 2025 13:14:34 +0000 (14:14 +0100)]
github: ci: add cmake build and source directories
Add cmake build and source directories to suppress the following warning:
CMake Warning:
No source or binary directory provided. Both will be assumed to be the
same as the current working directory, but note that this warning will
become a fatal error in future CMake releases.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Fri, 14 Nov 2025 07:36:51 +0000 (08:36 +0100)]
github: ci: disable json-c tests
Disable BUILD_TESTING to save time when building json-c.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Fri, 14 Nov 2025 07:35:18 +0000 (08:35 +0100)]
scripts: devel-build: disable json-c tests
Disable BUILD_TESTING to save time when building json-c.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Tue, 11 Nov 2025 12:08:22 +0000 (13:08 +0100)]
netlink: fix typo in debug msg
As noted by @CasperVector in #115, there's a typo in netlink.c,
checking if true is true isn't very useful :)
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/305
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Mon, 10 Nov 2025 12:36:33 +0000 (13:36 +0100)]
src: fix whitespace issues
- Remove double whitespaces.
- Convert whitespace alignments to tabs.
- Fix code comments using whitespaces.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Tue, 11 Nov 2025 11:44:57 +0000 (12:44 +0100)]
vscode: enable indentation detection
Fixes issues with VSCode forcing whitespaces instead of tabs for indentation.
Fixes: df1824aec66c ("vscode: add tab settings")
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 9 Nov 2025 17:36:03 +0000 (18:36 +0100)]
odhcpd: add support for "ignore"
This is inspired by @Kasoo's work in PR #234, but slightly different.
The "ip" option now takes "ignore" as a value , but it'll only disable
DHCPv4 for a matching client.
Similarly, the "hostid" option now also understands "ignore", which will
disable DHCPv6 for a matching client.
Of course, this is all based on client-reported MAC
addresses/DUIDs/client IDs/etc, so it's not actually a security feature.
Closes: https://github.com/openwrt/odhcpd/pull/234
Closes: https://github.com/openwrt/odhcpd/issues/198
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/303
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 18:52:44 +0000 (19:52 +0100)]
statefiles: support per-interface hosts files
With this change, the "hostsfile" option is changed to a "hostsdir"
option. If set, per-interface hosts files will be written to the
directory (this requires some changes to the UCI defaults in the OpenWrt
repo). This prevents the hostnames from one interface "leaking" into
other interfaces, which might not be desirable e.g. when running several
instances of dnsmasq.
dnsmasq already supports reading several hosts files from a directory
(via the "--hostsdir=" option), so some changes to dnsmasq's init script
will also be necessary (depending on whether the user wants dnsmasq to
support resolution of all hosts or just the host on a given interface).
Making hosts files (rather than odhcpd's state file) the primary
interface for dnsmasq to be made aware of active leases/hostnames is
also an important first step towards making the statefile properly
private (the other important consumer is LuCI, via its rpcd plugin),
which is necessary if we want to support persisting leases across
restarts of odhcpd (or even reboots, if writing to flash is deemed
acceptable) in the future (since I want to extend the state file to
capture the full state of leases so that they can be properly
reconstructed).
In addition, the use of "--hostsdir=" in dnsmasq would make the
"leasetrigger" unnecessary, since it just restarts dnsmasq to make it
aware of the changes to the hostsfile. That still leaves the case when
dnsmasq uses multiple instances and only wants to read a specific
hostsfile (which, AFAIK, doesn't support automatic detection of when
that specific file changes).
Closes: https://github.com/openwrt/odhcpd/issues/237
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 17:33:07 +0000 (18:33 +0100)]
statefiles: don't make hostsfile dependent on statefile
Right now, the hosts file will only be created if the statefile is also
configured (and that fact isn't documented in the README.md, only in
commit logs (see
4bbc6e74248feeb756bd03dc500fb4f446ccfc49).
With this patch, the hostsfile is generated whether the statefile is
configured or not.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 17:17:34 +0000 (18:17 +0100)]
statefiles: don't write expired leases
This also aligns the behaviour of state and host files.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 17:09:45 +0000 (18:09 +0100)]
statefiles: simplify statefiles_write_state6()
By collecting the addresses first and writing the hosts entries, we can
write the state line straight to the file, without having to use a
buffer as an intermediary. This also makes the ordering in the function
more logical, as it reflects the order things get written to file.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 16:48:00 +0000 (17:48 +0100)]
statefiles: write straight to file in statefiles_write_state4()
Instead of writing to a buffer, then to a file, just write straight to
file. This also makes the order of the steps in the function more
logical (i.e. matches the order in which things are actually written to
file).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 15:17:56 +0000 (16:17 +0100)]
statefiles: correct some comments/variable names
Correct some comments and variable names in statefiles_write_state[46].
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 14:56:30 +0000 (15:56 +0100)]
statefiles: unify host4 writing
The statefile contains host entries and state comments. Reuse the host
writing functionality for IPv4 hosts, and remove some duplication.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 14:46:14 +0000 (15:46 +0100)]
statefiles: unify host6 writing
The statefile is a mix of state comments and hostsfile entries, reuse the
hosts writing logic when writing the statefile to reduce duplication.
(note that this has a tiny extra cost in that inet_ntop() is called
twice, that'll go away later when we drop the hostsfile style lines from
the statefile).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 14:28:49 +0000 (15:28 +0100)]
statefiles: group functions
Move statefiles_write_state6() so that all host/state functions are
grouped together.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 14:24:13 +0000 (15:24 +0100)]
statefiles: add function to write IPv4 hosts
Analogous to statefiles_write_host6().
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 14:08:17 +0000 (15:08 +0100)]
statefiles: simplify state/host file writing
Simplify and rename:
dhcpv6_write_ia_addrhosts() -> statefiles_write_host6()
dhcpv6_write_ia_addr() -> statefiles_write_state6()
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 13:40:39 +0000 (14:40 +0100)]
statefiles: move dhcpv6_ia_enum_addrs() to odhcpd.c
dhcpv6_ia_enum_addrs() is used in several different places (ubus,
statesfile, dhcpv6-ia), so move it to a more central location. At the
same time, rename it to odhcpd_enum_addr6().
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 13:22:23 +0000 (14:22 +0100)]
statefiles: add dhcpv6_lease to dhcpv6_log_ia_addr() args
"lease" is more obvious than "ctxt->c" (which is a struct dhcpv6_lease).
This also means that the lease can be moved out from "struct
write_ctxt", so that the dhcpv4 and dhcpv6 code looks more similar.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 8 Nov 2025 12:17:58 +0000 (13:17 +0100)]
statefiles: create helper functions to write leases
Break statesfiles_write_state() into smaller parts, making it easier to
read and understand the logic of each part.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 7 Nov 2025 16:58:05 +0000 (17:58 +0100)]
statefiles: introduce statefiles_write()
First, the name makes it clearer that several files might be written
(and it doesn't refer to dhcpv6 anymore). This also lays the ground for
the coming patches.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 7 Nov 2025 16:53:22 +0000 (17:53 +0100)]
statefiles: use dirfd in dhcpv6_ia_write_statefile()
Similarly to the hostsfile, use a dirfd to keep track of the directory
where the statefile is stored, simplifying the code a bit and avoiding
some more allocations.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 7 Nov 2025 16:30:51 +0000 (17:30 +0100)]
statefiles: bail early in dhcpv6_ia_write_statefile()
Bail early if config.dhcp_statefile isn't set, thus reducing the amount
of indentation in the function. The diff looks pretty gnarly, but that's
diff's fault, the actual code isn't changed at all.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 7 Nov 2025 16:25:40 +0000 (17:25 +0100)]
statefiles: simplify dhcpv6_ia_write_hostsfile()
Use a dirfd to keep track of the hostsfile directory, also make sure
that the directory is created if necessary (this is in preparation for
writing per-interface hostsfiles).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 7 Nov 2025 15:43:26 +0000 (16:43 +0100)]
dhcpv6-ia: split statefile handling to separate file
dhcpv6-ia.c is already quite long, which makes it harder to work with.
In addition, the whole statefile machinery isn't specific to the dhcpv6
server, but shared by the dhcpv4 and dhcpv6 servers, so split it out to
its own file.
This is in preparation for some more hacking on the statefile handling.
The copyright headers on statefiles.[ch] are the result of some git
archeology, and I've taken the liberty of putting them in REUSE format,
using only the initial copyright year for each contributor (one of the
recommendations from the REUSE project).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Mon, 10 Nov 2025 12:43:48 +0000 (13:43 +0100)]
src: replace #pragma once with defines
Replace #pragma once with defines since it's not standard.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Mon, 10 Nov 2025 12:12:46 +0000 (13:12 +0100)]
src: remove whitespaces at EOL
These whitespaces do nothing and can be removed.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Mon, 10 Nov 2025 12:38:04 +0000 (13:38 +0100)]
vscode: add tab settings
Add tag settings for Visual Studio Code.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Sun, 9 Nov 2025 23:00:05 +0000 (00:00 +0100)]
odhcpd: Implement RFC9762 DHCPv6 PD Preferred flag for PIOs
The RFC defines the P flag for use within PIOs, and defines its meaning
as "purely a positive indicator, telling nodes that DHCPv6 PD is
available and the network prefers that nodes use it".
The RFC abstract lays out:
The (P) flag is used to indicate that the network prefers that clients
use the deployment model in RFC 9663 instead of using individual
addresses in the on-link prefix assigned using SLAAC, or DHCPv6 address
assignment.
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/301
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Thu, 9 Oct 2025 08:38:23 +0000 (10:38 +0200)]
dhcpv4: add support for RFC4361-style clientid
This adds support for DHCPv6-style client identifiers to the dhcpv4 codebase,
as per RFC4361. It also ensures RFC6842-compliance by including the client
identifier (no matter which kind it is) in replies.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/299
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 26 Oct 2025 09:19:14 +0000 (10:19 +0100)]
odhcpd: remove the "legacy" option
LuCI has already been updated so that the "dhcpv4" option is exposed in
the UI and the option also gets set when the user clicks the "Set up
DHCP Server" button in the "DHCP Server" tab in the interfaces view.
Furthermore, there are instructions in the UI to enable the DHCPv4
server on a per-interface basis.
Finally, the odhcp defaults script (in the openwrt repo) also sets the dhcpv4
option, as necessary.
People who don't use LuCI but prefer the cmdline should presumably be able to
figure out that the dhcpv4 option needs to be set (if it isn't already), so
remove the "legacy" option.
This supersedes this PR:
https://github.com/openwrt/odhcpd/pull/275
Note that the "maindhcp" option is used by both dnsmasq and odhcpd to control
which of them handles DHCPv4, and is now exposed in the LuCI UI as well, so it
cannot be removed.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 26 Oct 2025 09:12:15 +0000 (10:12 +0100)]
odhcpd: document the "upstream" option
I'm not 100% sure about whether this option will get automagically set e.g. by
the data that is returned from the netifd ubus call, but this documentation is
better than nothing.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 26 Oct 2025 08:57:03 +0000 (09:57 +0100)]
odhcpd: document the "ra_advrouter" option
Might see little practical use in the field, but the option is there, so let's
document it. Also align the documentation for the "ra_slaac" option.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 26 Oct 2025 08:33:17 +0000 (09:33 +0100)]
odhcpd: remove the "ra_management" option
The option is marked as deprecated since 2019 in the odhcpd code base.
It has been superseded by the "ra_flags" and "ra_slaac" options, which provide
more fine-grained control, and having several different configuration options
controlling the same thing is a potential source of confusion.
The option is, and has always been, undocumented.
Furthermore, I've grepped through the LuCI source tree, and it contains logic
to control "ra_flags" and "ra_slaac", but no references to "ra_management" at
all.
The only references in the openwrt tree is inside the dnsmasq init script, but
that is a dnsmasq-internal thing and, again, the option cannot be set from
LuCI, meaning I think we can assume that it has limited usage.
So, remove it.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 25 Oct 2025 22:50:18 +0000 (00:50 +0200)]
odhcpd: remove the "pd_manager" and "pd_cer" options
As noted in the previous commit, Homenet is defunct.
The only users of these two options are Homenet/hnetd, so remove the options,
which results in a nice cleanup to src/dhcpv6-ia.c.
We also need to update the Makefile in the main OpenWrt repo.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 25 Oct 2025 22:49:22 +0000 (00:49 +0200)]
odhcpd: remove the "filter_class" option
The "filter_class" option was first introduced as part of the effort to support
the Homenet Control Protocol (HNCP, RFC7368, RFC7788). At first it was
hardcoded to filter out the user class "HOMENET", and later it was made
configurable, but there appears to be no other use-cases than Homenet. There's
also legacy comments in the odhcpd code pointing at filtering out homenet user
classes (the comments are misleading since "filter_class" is not set by
default).
Homenet is effectively dead. The "hnetd" daemon last saw any code changes in
2018, doesn't compile with current versions of GCC, and was removed from
OpenWrt altogether in 2025 [1].
The OpenWrt wiki also notes [2] that HNCP is effectively dead, and that the
upstream website [3] is gone.
Searching for the Homenet Control Protocol mostly returns results pointing
either:
a) at the standards
b) at the OpenWrt/hnetd project
c) at various posts stating that it is dead
The IETF working group for Homenet is no more, and Éric Vyncke (Area Director
for the Internet Area of the IETF) mentioned wanting to avoid Homenet pitfalls
[4] (page 11) in the future of IPv6 autoconfig.
The current implementation is also contrary to the spirit of the RFCs
introducing the option (RFC3004 - DHCPv4; RFC8415, §21.15 - DHCPv6) where it is
described (example from RFC8415) as follows:
The information contained in the data area of this option is
contained in one or more opaque fields that represent the user class
or classes of which the client is a member. A server selects
configuration information for the client based on the classes
identified in this option. For example, the User Class option can be
used to configure all clients of people in the accounting department
with a different printer than clients of people in the marketing
department.
I.e., it's meant to be used in a manner similar to "tags" in dnsmasq.
Finally, the option has been undocumented for the whole existance of odhcpd.
So, remove this option.
[1] https://github.com/openwrt/routing/commit/
85b868b3413a29da0bd6ecd3518c2d34a6ffb788
[2] https://openwrt.org/docs/guide-user/network/zeroconfig/hncp_configuration
[3] https://web.archive.org/web/
20180831161552/http://homewrt.org/start
[4] https://ripe87.ripe.net/wp-content/uploads/presentations/102-
20231130-RIPE-87-IPv6-from-IETF.pdf
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 24 Oct 2025 22:19:09 +0000 (00:19 +0200)]
config: drop iface_attr_info
BLOBMSG_TYPE_STRING is the default type for BLOBMSG_TYPE_ARRAY, so this
doesn't add anything.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 22 Oct 2025 00:54:46 +0000 (02:54 +0200)]
README.md: add missing options
I did a quick dig through the source, and these are all the options that are
supported but which do not have any corresponding documentation. Subsequent
patches will go through the options one by one.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 10 Oct 2025 17:55:36 +0000 (19:55 +0200)]
dhcpv4: lazy store statefiles
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]>
David Härdeman [Thu, 9 Oct 2025 05:26:00 +0000 (07:26 +0200)]
dhcpv6: create struct dhcpv6_lease
This concludes the lease/assignment struct saga. "struct dhcp_assignment" is
converted to "struct dhcpv6_lease" and various function names, etc are updated.
I intentionally did *not* change variable names here to keep the patch as
simple as possible. Therefore, the DHCPv6 codebase is still full of lots of
variables named things like "a". I expect that to change over time when I
tackle DHCPv6 code in ernest. But it'll have to do for now.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/292
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Thu, 9 Oct 2025 05:13:58 +0000 (07:13 +0200)]
dhcpv4: create struct dhcpv4_lease
This makes it easier to understand what is actually going on, makes the
structs clearer and avoids layers of indirection like using free
callbacks. I understand the original motivation for unifying the struct
for DHCPv4 and DHCPv6, but it's more hassle than it is worth.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/292
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 19 Oct 2025 07:47:37 +0000 (09:47 +0200)]
odhcpd: struct lease -> struct lease_cfg
Both the DHCPv4 and DHCPv6 RFCs use the term "lease" to refer to what in the
odhcpd codebase is "struct assignment".
Some examples:
RFC2131, §2.2:
In this document, the period over which a network address
is allocated to a client is referred to as a "lease" [11]. The
client may extend its lease with subsequent requests. The client may
issue a message to release the address back to the server when the
client no longer needs the address. The client may ask for a
permanent assignment by asking for an infinite lease. Even when
assigning "permanent" addresses, a server may choose to give out
lengthy but non-infinite leases to allow detection of the fact that
the client has been retired.
RFC8415, §4.2:
lease A contract by which the server grants the
use of an address or delegated prefix to
the client for a specified period of time.
For example in §7.3:
RENEW (5) A client sends a Renew message to the
server that originally provided the
client's leases and configuration
parameters to extend the lifetimes on the
leases assigned to the client and to update
other configuration parameters.
There's also a bit of confusion in the odhcpd codebase, where the two are
sometimes used interchangeably, see e.g. handle_*_leases() in ubus.c which
correspond to the ubus methods "ipv4leases" and "ipv6leases" but which then
returns "assignments", or dhcpv4_lease() in dhcpv4.c, which returns an
"assignment". There's more examples.
To put it differently, "assignment" when used in the RFCs is essentially
the verb, the server assigns, and "lease" is the noun - the server
assigns a lease.
So rename "struct lease" to "struct lease_cfg" to better describe what it is
used for. I.e. it contains the cfg for a host which can provide the parameters
for a static lease (and it comes from a "host" uci section) but it isn't a
*lease* per se.
This is a bit a of pet peeve of mine, but I found it quite confusing when I
first started hacking on odhcpd. It also lays the foundation for later patches
which rework/split/rename "struct assignment".
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/292
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Thu, 9 Oct 2025 04:35:26 +0000 (06:35 +0200)]
dhcpv4: use leasetime from a->lease
Note that a->leasetime can ever only be zero or == a->lease->leasetime,
so simplify this a bit in preparation for the next patch.
src/config.c also changes a->leasetime, but always to match
a->lease->leasetime.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/292
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 2 Nov 2025 18:25:13 +0000 (19:25 +0100)]
router: always use link-local src address for RAs
This is a follow-up to:
https://github.com/openwrt/odhcpd/pull/242
As noted by @willmo [1], RAs are currently not strictly limited to using a
link-local source address, which they should according to RFC4861, §4.2.
This is usually not an issue, since router solicitations typically
originate from link-local source addresses or the undefined address,
meaning that odhcpd will reply with its own link-local address
(auto-selected by the kernel).
But if a solicitation is sent from e.g. a GUA/ULA address, odhpcd will
currently reply using it's own GUA/ULA address.
While fixing this, correct some misleading comments.
[1] https://github.com/openwrt/odhcpd/pull/242#issuecomment-
3475020864
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/297
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Tue, 28 Oct 2025 18:28:42 +0000 (19:28 +0100)]
router: Rewrite the ingress MTU to one configured for the interface
There are edge cases where routers forward ICMPv6 RA which contain an MTU which
is too large/small for the receiving client. Override the ingress MTU with the
value configured on the interface.
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/296
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Thu, 30 Oct 2025 00:53:44 +0000 (01:53 +0100)]
router: utilize interface ra_mtu for RA
MTU is now set at configure time.
Change ra_mtu to uint32 since it never stores a negative value.
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/296
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Thu, 30 Oct 2025 00:52:20 +0000 (01:52 +0100)]
config: clamp ra_mtu to interface MTU, and default ra_mtu to interface MTU
Set ra_mtu max to that of its interface. We also default ra_mtu if the user
did not configure it.
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/296
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Sun, 2 Nov 2025 16:40:14 +0000 (17:40 +0100)]
netlink: Store interface MTU at link change
This is used for clamping MTU values.
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/296
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Mon, 3 Nov 2025 06:38:52 +0000 (07:38 +0100)]
github: fix CI apt dependencies
We need to run 'apt update' before installing the APT packages.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sun, 26 Oct 2025 13:59:46 +0000 (14:59 +0100)]
odhcpd: more fixes for IID calculations
This is the followup to pull request #290.
First, note that in dhcpv6_ia_enum_addrs(), the prefix + IID are currently
combined based on iface->dhcpv6_hostid_len, which is wrong. The latter only
defines the number of bits (typically 12) which should be used to
generate/constrain IIDs, but not the actual IID length.
The actual length of the IID is defined by the length of the prefix
(i.e. 128 - prefix length), the reason this hasn't been noticed is
probably that the higher bits of the IID area in the prefix (the struct
odhcpd_ipdaddr which corresponds to the DHCP server's own IPv6 address
on the interface) are often anyway zero (i.e. people often use an
address like fd00:1234:5678:abcd::1/64 for the server if they have
received a prefix like fd00:1234:5678::/48 from the upstream router).
Second, build_ia() unconditionally writes 64 bits of struct
dhcp_assignment->assigned_host_id (IID) to the struct in6_addr to use for a
given client (i.e. assumes the prefix to be at least /64 or shorter).
This is a first stab at fixing it. I've tried it, and successfully obtained a
lease using a /96 prefix.
There are still some murky corners here though, for example, if
"dhcpv6_hostidlength" is set to 64, there are no real checks that the prefix
length is compatible with that (so we might end up generating an IID of 64
bits, making sure that it is unique and not taken, and then truncate it
silently to a 32 bit IID in case the prefix is /96). But that's a topic for a
different day (and can only be triggered by non-standard configurations).
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/295
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 10 Oct 2025 07:38:38 +0000 (09:38 +0200)]
dhcpv6: support a configurable DUID
Allow the use of a (stable) globally configured DUID as the server identifier.
Currently, odhcpd generates a per-interface DUID-LL, meaning that the DUID is
not stable across interfaces and will change if/when hardware changes.
Supporting a stable DUID brings odhcpd's behaviour in line with RFC8415, §11:
...The DUID is
designed to be unique across all DHCP clients and servers, and stable
for any specific client or server. That is, the DUID used by a
client or server SHOULD NOT change over time if at all possible; for
example, a device's DUID should not change as a result of a change in
the device's network hardware or changes to virtual interfaces...
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/274
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 10 Oct 2025 07:32:33 +0000 (09:32 +0200)]
dhcpv6-ia: split reconf_msg struct into partial structs
Change send_reconf() so that the various options are each in separate struct.
This makes it easy to support a variable-length server DUID in the future.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/274
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Fri, 10 Oct 2025 07:05:02 +0000 (09:05 +0200)]
dhcpv6: split clientid to a separate struct
Change handle_client_request() so that the serverid and clientid are in
separate structs. This makes it easy to support a variable-length server DUID
in the future.
Note that some of the variables (like "dest") have a bit misleading names now,
but that is something that I plan to address later, this patch was
intentionally kept as simple as possible.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/274
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 25 Oct 2025 10:25:33 +0000 (12:25 +0200)]
dhcpv6-ia: simplify/fix IID calculations
This is a first stab at simplifying the IID calculations introduced with
commit
7000557cd8f6396b82cb35e0cdf9b72b18288fbc.
See the discussion in:
https://github.com/openwrt/openwrt/issues/20523
Note that a "cold" jrand48() doesn't have much of an avalance effect in
case of two very similar DUIDs, but it shouldn't matter because we're
not looking for any kind of cryptographic randomness...just a
predictable PRNG where the same DUID will (usually) get the same IPv6
addr (if available).
Closes: https://github.com/openwrt/odhcpd/issues/293
Closes: https://github.com/openwrt/openwrt/issues/20523
Fixes: 7000557cd8f6 ("dhcpv6-ia: respect prefix assigned to interface (>= /64)")
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/290
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Sat, 25 Oct 2025 13:32:06 +0000 (15:32 +0200)]
odhcpd: change "-c" cmd line arg to take a dir
After the TZ support was added, odhcpd now reads two cfg files
potentially. Instead of adding a separate switch for the system UCI
file, I've changed the "-c" argument to take a directory. This is more
future-proof and also serves as a preparation for the global DUID PR
(which will necessitate reading from the UCI "network" file as well).
While I was at it, I also made some tiny improvements to the
odhcpd_reload() function to avoid an unnecessary allocation.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/291
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 8 Oct 2025 15:50:47 +0000 (17:50 +0200)]
dhcpv4: dhcpv4_lease() - clarify which variables are requests
By consistently using the "req_" prefix, it's clearer which variables
relate to things the client has requested or which come from the client
request.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/286
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 8 Oct 2025 15:28:39 +0000 (17:28 +0200)]
dhcpv4: dhcpv4_lease() - simplification
Reduce indentation, check some conditions and bail early, break it into clearer
chunks and add some comments.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/286
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 8 Oct 2025 15:23:46 +0000 (17:23 +0200)]
dhcpv4: dhcpv4_lease() - convert to switch statement
And move the simple cases first, also bail if there's nothing to do.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/286
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
David Härdeman [Wed, 8 Oct 2025 13:46:24 +0000 (15:46 +0200)]
dhcpv4: dhcpv4_assign() - explain address assignment
Add some debug statements that also serve as comments explaining the logic
of dhcpv4_assign() and which make it easier to understand why a
requested address was not provided.
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/286
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Sat, 25 Oct 2025 00:24:58 +0000 (02:24 +0200)]
dhcpv6: send timezone only when client requests
Commit
7956f4271b4e added support for RFC4833 timezones but didn't check
if clients had actually requested them.
Fixes: 7956f4271b4e ("dhcpv6: RFC4833 timezones")
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/289
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Wed, 22 Oct 2025 14:36:22 +0000 (16:36 +0200)]
dhcpv4: bump problem scenario up to warn
This way a potential failure scenario is visible with odhcpds default log
level of warn.
Closes https://github.com/openwrt/odhcpd/issues/228
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/287
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas [Wed, 22 Oct 2025 14:32:23 +0000 (16:32 +0200)]
config: properly set log level from uci
The condition to set the log level from the uci config if it hadn't been
set from the command line should be inverted to work properly.
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Paul Donald [Wed, 22 Oct 2025 00:17:06 +0000 (02:17 +0200)]
dhcpv6: RFC4833 timezones
This implements RFC4833 - supplying timezone information to clients that
request them. Both forms are possible, when timezone is configured in
the uci system settings (the luci GUI saves both forms to the config).
e.g.
```
config system
option zonename 'America/Puerto Rico'
option timezone 'AST4'
```
There is also an odhcpd flag to disable their use, set in uci dhcp.
```
config odhcpd 'odhcpd'
option enable_tzdb '0'
```
Once enabled, the options, when requested, are sent:
NEW_POSIX_TIMEZONE 41 // 'AST4'
NEW_TZDB_TIMEZONE 42 // 'America/Puerto_Rico'
Wireshark disassemble of options sent to client:
```
...
Time Zone Database
Option: Time Zone Database (42)
Length: 19
TZ-database: America/Puerto_Rico
Time Zone
Option: Time Zone (41)
Length: 4
Time-zone: AST4
```
Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/284
Signed-off-by: Álvaro Fernández Rojas <[email protected]>