project/odhcpd.git
4 weeks agodhcpv4: use leasetime from a->lease
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]>
4 weeks agorouter: always use link-local src address for RAs
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]>
4 weeks agorouter: Rewrite the ingress MTU to one configured for the interface
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]>
4 weeks agorouter: utilize interface ra_mtu for RA
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]>
4 weeks agoconfig: clamp ra_mtu to interface MTU, and default ra_mtu to interface MTU
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]>
4 weeks agonetlink: Store interface MTU at link change
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]>
4 weeks agogithub: fix CI apt dependencies
Á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]>
5 weeks agoodhcpd: more fixes for IID calculations
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]>
5 weeks agodhcpv6: support a configurable DUID
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]>
5 weeks agodhcpv6-ia: split reconf_msg struct into partial structs
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]>
5 weeks agodhcpv6: split clientid to a separate struct
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]>
5 weeks agodhcpv6-ia: simplify/fix IID calculations
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]>
6 weeks agoodhcpd: change "-c" cmd line arg to take a dir
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]>
6 weeks agodhcpv4: dhcpv4_lease() - clarify which variables are requests
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]>
6 weeks agodhcpv4: dhcpv4_lease() - simplification
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]>
6 weeks agodhcpv4: dhcpv4_lease() - convert to switch statement
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]>
6 weeks agodhcpv4: dhcpv4_assign() - explain address assignment
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]>
6 weeks agodhcpv6: send timezone only when client requests
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]>
6 weeks agodhcpv4: bump problem scenario up to warn
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]>
6 weeks agoconfig: properly set log level from uci
Á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]>
6 weeks agodhcpv6: RFC4833 timezones
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]>
6 weeks agodhcpv6-ia: respect prefix assigned to interface (>= /64)
Matthias Riegler [Mon, 12 Jun 2023 19:01:08 +0000 (21:01 +0200)]
dhcpv6-ia: respect prefix assigned to interface (>= /64)

When odhcpd hands out DHCPv6 leases on an interface with a prefix >= /64
(e.g. /96), leases can be outside of the allocated /96 on the interface.
Fixes https://github.com/openwrt/odhcpd/issues/199.

Signed-off-by: Matthias Riegler <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/219
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agoodhcpd: improve odhcpd_urandom()
David Härdeman [Mon, 20 Oct 2025 20:57:52 +0000 (22:57 +0200)]
odhcpd: improve odhcpd_urandom()

First, note that not a single caller checks the return value - which is
quite reasonable. What are they supposed to do with a failure?

Second, none of the callers do anything that's *really*
security-sensitive, the closest we have is the force reconf nonce, and
that is blorted out over the network, so it's really a best-effort kind
of thing.

Third, odhcpd_urandom() currently doesn't check if it e.g. got
interrupted by a signal.

So, simplify and modernize this a bit by using getrandom(), which allows
us to skip one fd, and which avoids syscalls by using the vDSO approach
instead. Also, check for things like signal interrupts (don't really
happen on calls for entropy < 256 bytes, but still). And make a
reasonable effort, but not much more.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/285
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agoconfig: fix erroneous clamp message if clamp value == max
Paul Donald [Tue, 21 Oct 2025 13:46:29 +0000 (15:46 +0200)]
config: fix erroneous clamp message if clamp value == max

If the clamp value is equal to and only equal to PD_MIN_LEN_MAX then the warn
is emitted, when it shouldn't be since PD_MIN_LEN_MAX is a valid max value.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/283
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: bail earlier on release/decline
David Härdeman [Wed, 8 Oct 2025 14:21:22 +0000 (16:21 +0200)]
dhcpv4: bail earlier on release/decline

And make it clearer that the return value from dhcpv4_lease isn't used.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: don't hardcode options array length
David Härdeman [Tue, 7 Oct 2025 08:09:38 +0000 (10:09 +0200)]
dhcpv4: don't hardcode options array length

Instead of copying around the same data on the stack, keep two arrays with
mandatory and requested options, then loop over both of them.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: shrink struct dhcpv4_message
David Härdeman [Tue, 7 Oct 2025 07:49:10 +0000 (09:49 +0200)]
dhcpv4: shrink struct dhcpv4_message

The options padding can now be removed.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for forcereconf messages, fix hash
David Härdeman [Tue, 7 Oct 2025 04:37:22 +0000 (06:37 +0200)]
dhcpv4: use iovec for forcereconf messages, fix hash

Do the same iovec conversion for dhcpv4_fr_send(), and remove dhcpv4_put() now
that the last user is gone. Yay.

Note that there was a bug in dhcpv4_fr_send() in that it would first
perform:
md5_hash(&fr_msg, sizeof(fr_msg), &md5);
And later:
sendto(/* fd */, &fr_msg, PACKET_SIZE(&fr_msg, cursor), ...)

But PACKET_SIZE is much smaller than sizeof(fr_msg), meaning that the hash is
not computed for the actual packet that gets sent.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: fix padding of iovec message in dhcpv4_handle_msg()
David Härdeman [Mon, 6 Oct 2025 17:17:10 +0000 (19:17 +0200)]
dhcpv4: fix padding of iovec message in dhcpv4_handle_msg()

Fix the FIXME added earlier by calculating the length of the iovec package and
adding padding as necessary to comply with the BOOTP specification (which
defines the minimum length of a message as 300 bytes).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: some minor cleanups post-iovec
David Härdeman [Mon, 6 Oct 2025 14:45:38 +0000 (16:45 +0200)]
dhcpv4: some minor cleanups post-iovec

Do some cleanups, remove some magic numbers, make sure structs are defined with
the members in their actual order and without missing members.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for forcerenew opts
David Härdeman [Mon, 6 Oct 2025 14:29:12 +0000 (16:29 +0200)]
dhcpv4: use iovec for forcerenew opts

That's the last bunch of options :)

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for router and DNS server
David Härdeman [Mon, 6 Oct 2025 14:06:32 +0000 (16:06 +0200)]
dhcpv4: use iovec for router and DNS server

And some more options converted.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for DNS search and MTU
David Härdeman [Mon, 6 Oct 2025 13:40:23 +0000 (15:40 +0200)]
dhcpv4: use iovec for DNS search and MTU

Some more options converted.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for netmask/hostname/broadcast
David Härdeman [Mon, 6 Oct 2025 13:00:59 +0000 (15:00 +0200)]
dhcpv4: use iovec for netmask/hostname/broadcast

More simple conversions.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for leasetime/renew/rebind
David Härdeman [Mon, 6 Oct 2025 12:03:34 +0000 (14:03 +0200)]
dhcpv4: use iovec for leasetime/renew/rebind

Move some more options over to use an iovec. Note that the FIXME will be
addressed in a later patch.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: introduce a reply_opts array
David Härdeman [Mon, 6 Oct 2025 11:39:25 +0000 (13:39 +0200)]
dhcpv4: introduce a reply_opts array

Add a simple array to record the options we want to send back. This is just a
placeholder in preparation for later patches.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for DNR
David Härdeman [Mon, 6 Oct 2025 11:21:21 +0000 (13:21 +0200)]
dhcpv4: use iovec for DNR

Another simple conversion to use iovec.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for NTP
David Härdeman [Mon, 6 Oct 2025 10:23:18 +0000 (12:23 +0200)]
dhcpv4: use iovec for NTP

Note that the previous code would send out several NTP options if
the client requested the option more than once, and would also send
out zero-sized options (against RFC2132, §8.3).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: use iovec for message and serverid
David Härdeman [Mon, 6 Oct 2025 09:39:20 +0000 (11:39 +0200)]
dhcpv4: use iovec for message and serverid

Start converting options over to an iovec based solution.

Also change the name of "type" in struct dhcpv4_option, both the DHCPv4 and
DHCPv6 RFCs call these "option codes", not types.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: reorder some more variables in dhcpv4_handle_msg()
David Härdeman [Mon, 6 Oct 2025 09:05:54 +0000 (11:05 +0200)]
dhcpv4: reorder some more variables in dhcpv4_handle_msg()

Sort the variables into request and response variables for clarity.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: remove one more variable from dhcpv4_handle_msg()
David Härdeman [Mon, 6 Oct 2025 08:47:35 +0000 (10:47 +0200)]
dhcpv4: remove one more variable from dhcpv4_handle_msg()

It's only used once, and there's no lack of variables to keep track of in
dhcpv4_handle_msg().

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: move dest handling from dhcpv4_handle_msg()
David Härdeman [Mon, 6 Oct 2025 08:44:42 +0000 (10:44 +0200)]
dhcpv4: move dest handling from dhcpv4_handle_msg()

dhcpv4_handle_msg() is already over 300 lines of code, break out the
destination handling into a separate function to make the former function a bit
more manageable in size.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: don't copy reqopts around
David Härdeman [Mon, 6 Oct 2025 07:48:43 +0000 (09:48 +0200)]
dhcpv4: don't copy reqopts around

Ok, this is a bit weird, dhcpv4_handle_msg() passes reqopts to dhcpv4_lease(),
which stashes a copy of the reqopts in the struct dhcp_assignment, and then
dhcpv4_handle_msg() uses the stashed copy instead.

The only reason a copy is made seems to be so that the requested options can
later be reported via ubus (in "ubus call dhcp ipv4leases"), but it is hard to
see a use-case for that (the DHCP server has already replied to the client by
the time the lease is available over ubus) and we don't do anything similar for
DHCPv6, so remove the extra copying.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: more refactoring of dhcpv4_handle_msg()
David Härdeman [Sun, 5 Oct 2025 19:21:50 +0000 (21:21 +0200)]
dhcpv4: more refactoring of dhcpv4_handle_msg()

Move some gnarly conditionals into a switch in preparation of later patches.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: clarify variable names in dhcpv4_handle_msg()
David Härdeman [Sun, 5 Oct 2025 19:04:17 +0000 (21:04 +0200)]
dhcpv4: clarify variable names in dhcpv4_handle_msg()

Make it clearer which variables relate to the request, and which ones relate to
the reply that we're constructing.

Also fix the missing endianness conversion for req_leasetime (not so severe
though, a bogus leasetime value will be overridden with limits that are set
per-lease or per-interface).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: some more cleanups to dhcpv4_handle_msg()
David Härdeman [Sun, 5 Oct 2025 18:35:23 +0000 (20:35 +0200)]
dhcpv4: some more cleanups to dhcpv4_handle_msg()

Most notably, neither hostname, nor reqopts actually need to be copied from
req, they are used in a read-only fashion throughout the rest of the function
(and any functions it calls). Also switch some complicated if-else constructs
over to switch-case.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv4: preparations for iovec usage
David Härdeman [Thu, 25 Sep 2025 15:09:08 +0000 (17:09 +0200)]
dhcpv4: preparations for iovec usage

dhcpv4.c currently builds DHCPv4 messages by lots of repeated calls to the
dhcpv4_put() method, which basically does a whole lot of copies to the stack.
The following set of patches convert dhcpv4.c over to use iovecs, which avoids
almost all of the stack allocations and copies.

The first step is to lay the groundwork by changing dhcpv4_send_reply() and the
corresponding dhcpv6_4o6_send_reply() in dhcpv6.c so that they both take an
iovec instead of a plain buffer. Also introduce the first (very very simple)
iovec for dhcpv4_handle_msg() which just contains the whole packet, plus the
end marker as a separate vector element. It will be filled out further in
subsequent patches.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/278
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agoodhcdp: use a more suitable clock
David Härdeman [Fri, 10 Oct 2025 20:57:08 +0000 (22:57 +0200)]
odhcdp: use a more suitable clock

In my testing, CLOCK_MONOTONIC_COARSE uses about 1/3 of the CPU cycles.

And the accuracy is completely irrelevant.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/282
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv6: change dhcpv6 message type check in relay
Paul Donald [Fri, 3 Oct 2025 12:59:55 +0000 (14:59 +0200)]
dhcpv6: change dhcpv6 message type check in relay

When compiled, the switch instruction jump table O(1) is more efficient
than multiple logical comparisons in the if block (4-5 checks).

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/279
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agodhcpv6: move dhcpv6 message type check for early exit
Paul Donald [Sun, 19 Oct 2025 17:30:38 +0000 (19:30 +0200)]
dhcpv6: move dhcpv6 message type check for early exit

Avoid doing a bunch of work if the message type we received is not a
client type and does not warrant a reply. Exit early.

Clarify and comment message type handling.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/279
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
6 weeks agoodhcpd: add a simple build script
David Härdeman [Fri, 17 Oct 2025 19:25:10 +0000 (21:25 +0200)]
odhcpd: add a simple build script

Inspired from the ci script and the scripts I've been using. Should make it a
little bit easier for people who want to contribute to odhcpd.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/280
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agogithub: improve CI
Álvaro Fernández Rojas [Sat, 18 Oct 2025 08:13:26 +0000 (10:13 +0200)]
github: improve CI

- Remove unneeded CMAKE_SYSTEM_PROCESSOR.
- Add summary table with sizes (in bytes) for each arch/variant.
- Upload generated binaries as artifacts.
- Add OpenWrt formalities.

Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoodhcpd: shrink binary size by creating a logging function
David Härdeman [Fri, 17 Oct 2025 19:45:38 +0000 (21:45 +0200)]
odhcpd: shrink binary size by creating a logging function

Moving the logging to a real function helps shrink the binary size back to what
it was before the logging patches.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/273
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoodhcpd: support stderr logging
David Härdeman [Wed, 8 Oct 2025 05:44:56 +0000 (07:44 +0200)]
odhcpd: support stderr logging

This is just for debugging purposes. Also, make sure that a cmdline loglevel
doesn't get overwritten by what's in the cfg file (it's customary that
command-line options take precedence over configuration file settings).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/273
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoodhcpd: add log helpers
David Härdeman [Wed, 8 Oct 2025 05:27:08 +0000 (07:27 +0200)]
odhcpd: add log helpers

These a just a bit more succinct than their syslog equivalents. Also, this lays
the ground for the next patch.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/273
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: cap dhcpv6_pd_min_len to max instead of only logging error
Paul Donald [Fri, 10 Oct 2025 11:44:28 +0000 (13:44 +0200)]
config: cap dhcpv6_pd_min_len to max instead of only logging error

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: clamp dhcpv6_hostid_len instead of only logging an error
Paul Donald [Fri, 10 Oct 2025 11:44:05 +0000 (13:44 +0200)]
config: clamp dhcpv6_hostid_len instead of only logging an error

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: clamp ra_mtu into 1280-65535 range
Paul Donald [Fri, 10 Oct 2025 11:42:11 +0000 (13:42 +0200)]
config: clamp ra_mtu into 1280-65535 range

(ipv6 packets can be up to 4GB in size...)

The old logic had a logic error of || instead of &&

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: cap ra_retranstime and warn instead of only logging an error
Paul Donald [Fri, 10 Oct 2025 11:43:23 +0000 (13:43 +0200)]
config: cap ra_retranstime and warn instead of only logging an error

Set to the currently defined maximum of 60,000msec via a define.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: cap ra_hoplimit to maximum and warn instead of logging an error
Paul Donald [Fri, 10 Oct 2025 11:46:44 +0000 (13:46 +0200)]
config: cap ra_hoplimit to maximum and warn instead of logging an error

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: cap ra_reachabletime to RFC maximum instead of logging error
Paul Donald [Fri, 10 Oct 2025 11:45:48 +0000 (13:45 +0200)]
config: cap ra_reachabletime to RFC maximum instead of logging error

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: drop double size lease times; they are all UINT32_MAX;
Paul Donald [Fri, 3 Oct 2025 14:36:03 +0000 (16:36 +0200)]
config: drop double size lease times; they are all UINT32_MAX;

This now prevents implicit 64 bit->32 bit truncation which may flag
compiler errors later on down the road.

All of the variables receiving from parse_leasetime() are uint32_t
anyway, so max 136 years of valid lease time will have to suffice :)

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agorouter: redefine ra_mininterval and ra_maxinterval as uint32_t
Paul Donald [Fri, 3 Oct 2025 14:35:52 +0000 (16:35 +0200)]
router: redefine ra_mininterval and ra_maxinterval as uint32_t

They never store negative values.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: clamp ra_mininterval, ra_maxinterval, ra_lifetime at load time
Paul Donald [Fri, 3 Oct 2025 14:34:15 +0000 (16:34 +0200)]
config: clamp ra_mininterval, ra_maxinterval, ra_lifetime at load time

clamp values to RFC defined limits.

First set ra_maxinterval, then ra_mininterval adjusts based on max.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agorouter: refactor calc_ra_lifetime; redefine ra_lifetime as uint32_t
Paul Donald [Thu, 9 Oct 2025 21:35:31 +0000 (23:35 +0200)]
router: refactor calc_ra_lifetime; redefine ra_lifetime as uint32_t

ra_lifetime no longer holds negative values, because we no longer do
'init' when we do calc_ra_lifetime, instead we do init at init time.

Now ra_lifetime holds only >0 values.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: do MaxRtrAdvInterval init at (ra_maxinterval) init time
Paul Donald [Fri, 3 Oct 2025 14:33:57 +0000 (16:33 +0200)]
config: do MaxRtrAdvInterval init at (ra_maxinterval) init time

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agorouter: Apply updated values from RFC9096 (updates RFC4861) to RA/ND
Paul Donald [Fri, 3 Oct 2025 14:33:49 +0000 (16:33 +0200)]
router: Apply updated values from RFC9096 (updates RFC4861) to RA/ND

https://datatracker.ietf.org/doc/html/rfc9096#section-3.4

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agorouter: Apply updated values from RFC8319 (updates RFC4861) to RA/ND
Paul Donald [Fri, 3 Oct 2025 14:33:41 +0000 (16:33 +0200)]
router: Apply updated values from RFC8319 (updates RFC4861) to RA/ND

https://www.rfc-editor.org/rfc/rfc8319#section-4

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agoconfig: refactor parse_leasetime() - branch amount remains same
Paul Donald [Fri, 3 Oct 2025 14:33:32 +0000 (16:33 +0200)]
config: refactor parse_leasetime() - branch amount remains same

Also make 's' value a noop.

Signed-off-by: Paul Donald <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/225
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agogithub: fix CMAKE_SYSTEM_PROCESSOR copy&paste
Álvaro Fernández Rojas [Sun, 12 Oct 2025 20:10:44 +0000 (22:10 +0200)]
github: fix CMAKE_SYSTEM_PROCESSOR copy&paste

CMAKE_SYSTEM_PROCESSOR was incorrectly defined as matrix.packages instead
of matrix.arch.

Fixes: 288206c9a2ed ("github: add CI build")
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
7 weeks agogithub: add CI build github-ci
Álvaro Fernández Rojas [Mon, 6 Oct 2025 07:48:31 +0000 (09:48 +0200)]
github: add CI build

Add Github CI supporting different architectures and odhcpd build options.

Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: fix a compilation error
David Härdeman [Thu, 9 Oct 2025 09:08:14 +0000 (11:08 +0200)]
odhcpd: fix a compilation error

odhcpd_get_interface_dns_addr() calls odhcpd_get_interface_linklocal_addr(),
the former takes a const struct interface ptr, the latter takes a non-const
struct interface ptr.

The end result is an unhappy compiler.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/272
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: allow assignments to be reassigned
David Härdeman [Sat, 20 Sep 2025 21:19:29 +0000 (23:19 +0200)]
odhcpd: allow assignments to be reassigned

In a nutshell, this allows static leases which have only been configured
with a DUID (so no IAID specified) to be reassigned to any client with
the same DUID (but potentially different IAID). This matches how
multiple MAC addresses are handled in the DHCPv4 case, and supports the
use case for e.g. moving from WiFi to ethernet.

On the other hand, it is probably not what one would want for e.g. a
server that has several NICs connected to the same network. For those
cases, defining static leases with DUID and IAID anyway seems
preferrable (and it will also ensure a static interface<->IPv6 addr
mapping).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/255
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: support multiple per-client DUIDs
David Härdeman [Fri, 1 Aug 2025 17:39:17 +0000 (19:39 +0200)]
odhcpd: support multiple per-client DUIDs

This patch allows for multiple per-client DHCPv6 DUIDs. One use-case for
multiple DUIDs is dual-boot hosts, where e.g. Windows creates it's own DUID and
Linux (say, systemd-networkd) creates it's own DUID, and the two cannot be
synced in a simple manner. This also makes DUIDs similar to multiple per-host
MAC addresses (which is already supported by dnsmasq for DHCPv4 leases).

Note that the "old" behaviour is still kept, in other words, where a
simple DUID (as opposed to a "DUID%IAID") static association is defined,
the address will be given out on a first-come-first-serve basis.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/255
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: support assignments on the basis of IAID
David Härdeman [Sat, 20 Sep 2025 17:48:13 +0000 (19:48 +0200)]
odhcpd: support assignments on the basis of IAID

With this patch, IAIDs are actually taken into account when creating
assignments, which allows per-IAID assignments.

The old odhcpd behaviour is still kept for assignments which only
specify a DUID. That behaviour is a first-come-first-serve basis, where
the first request (no matter what the IAID is) will get the static
address, and later requests with different IAIDs will get a NoAddrsAvail
error message (in other words, assignments are not re-assigned and
random adresses are not provided when the "main" address is already
assigned, if the DUID is known in a static config).

The old odhcpd behaviour can be described as follows:

Imagine a "dhcp" configuration file with a single static assignment:

config host
option name 'test'
option ip '192.168.44.2'
option mac '00:00:00:00:ca:fe'
option duid '0003000100000000cafe'
option hostid '02'

And odhcpd running on an interface with IPv6 addr fd00:aaaa::1/48, using
a simple test client (command line parameters should be self-evident
here, foo-client is the client part of a veth interface pair, odhcpd is
listening to the peer interface foo-server):

$ sudo dhcpdig --iaid=0x01 --duid=f000 foo-client
    IPv6: fd00:aaaa::9c5
$ sudo dhcpdig --iaid=0x01 --duid=f000 foo-client
    IPv6: fd00:aaaa::9c5
$ sudo dhcpdig --iaid=0x02 --duid=f000 foo-client
    IPv6: fd00:aaaa::817
$ sudo dhcpdig --iaid=0x02 --duid=f000 foo-client
    IPv6: fd00:aaaa::817
$ sudo dhcpdig --iaid=0x01 --duid=f000 foo-client
    IPv6: fd00:aaaa::9c5
$ sudo dhcpdig --iaid=0x01 --duid=0003000100000000cafe foo-client
    IPv6: fd00:aaaa::2
$ sudo dhcpdig --iaid=0x01 --duid=0003000100000000cafe foo-client
    IPv6: fd00:aaaa::2
$ sudo dhcpdig --iaid=0x02 --duid=0003000100000000cafe foo-client
  STATUS: NoAddrsAvail (2)

IOW, unknown DUIDs get randomly assigned per-IAID IPv6 addresses. Known
statically determined DUIDs will be awarded to the first client
attempting a lease, no matter what the IAID is, but the IAID will be
remembered and subsequent requests with other IAIDs will fail.

With this patch applied, assume a "dhcp" configuration file with the
following assignments:

config host
option name 'testcafe'
option ip '192.168.44.2'
option mac '00:00:00:00:ca:fe'
option hostid '02'
option duid '0003000100000000cafe%123'

config host
option name 'testbeef'
option ip '192.168.44.3'
option mac '00:00:00:00:be:ef'
option hostid '03'
option duid '0003000100000000beef'

config host
option name 'testfood'
option ip '192.168.44.4'
option mac '00:00:00:00:f0:0d'
option hostid '04'
option duid '0003000100000000f00d'

config host
option name 'testfood2'
option ip '192.168.44.5'
option mac '00:00:00:00:f0:0d'
option hostid '05'
option duid '0003000100000000f00d%123'

Now, using the same test client:

$ sudo ./build/dhcpdig --iaid=0x000 --duid=0003000100000000cafe foo-client
    IPv6: fd00:aaaa::9c4
$ sudo ./build/dhcpdig --iaid=0x123 --duid=0003000100000000cafe foo-client
    IPv6: fd00:aaaa::2
$ sudo ./build/dhcpdig --iaid=0x000 --duid=0003000100000000cafe foo-client
    IPv6: fd00:aaaa::9c4
$ sudo ./build/dhcpdig --iaid=0xabc --duid=0003000100000000beef foo-client
    IPv6: fd00:aaaa::3
$ sudo ./build/dhcpdig --iaid=0x123 --duid=0003000100000000beef foo-client
  STATUS: NoAddrsAvail (2)
$ sudo ./build/dhcpdig --iaid=0xabc --duid=0003000100000000f00d foo-client
    IPv6: fd00:aaaa::4
$ sudo ./build/dhcpdig --iaid=0xabc --duid=0003000100000000f00d foo-client
    IPv6: fd00:aaaa::4
$ sudo ./build/dhcpdig --iaid=0x123 --duid=0003000100000000f00d foo-client
    IPv6: fd00:aaaa::5

See also: https://github.com/openwrt/odhcpd/issues/175

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/255
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: support IAIDs for static DHCPv6 leases
David Härdeman [Sat, 20 Sep 2025 16:19:06 +0000 (18:19 +0200)]
odhcpd: support IAIDs for static DHCPv6 leases

Extend the string format for duids to alternatively support
"<duid>%<iaid>", which makes it possible to define separate static
leases, e.g. for a client which is connected to the same network with
more than one interface (which will both have the same DUID, as it is a
per-host identifier, but different IAIDs).

Note that this only wires up the new format, actually using it to make
DHCPv6-IA decisions will be implemented in subsequent patches.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/255
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: break up complex matching logic
David Härdeman [Sat, 20 Sep 2025 14:40:32 +0000 (16:40 +0200)]
odhcpd: break up complex matching logic

Like Winnie the Pooh, “I am a Bear of Very Little Brain, and long words
Bother me”.

Simplify the assignment checking logic by breaking it up a bit. This is
in preparation for subsequent patches.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/255
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: document the ubus interface
David Härdeman [Tue, 7 Oct 2025 12:41:44 +0000 (14:41 +0200)]
odhcpd: document the ubus interface

I guess this might be subject to change (for example, we probably want to
also broadcast events for DHCPv6 leases in the future, and I guess the
ack/release/expire events that are currently broadcast might benefit from
being renamed to something that makes it clearer that they are DHCPv4
specific events), but this is the current ubus interface.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/270
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: generate dbus events on lease expiry
David Härdeman [Tue, 7 Oct 2025 12:16:11 +0000 (14:16 +0200)]
dhcpv4: generate dbus events on lease expiry

We already generate events when a lease is acquired and released, so it seems
consistent to also generate events when a lease expires.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/270
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: fix ubus events
David Härdeman [Tue, 7 Oct 2025 11:50:47 +0000 (13:50 +0200)]
dhcpv4: fix ubus events

Note that req->ciaddr which was used to generate the broadcast message is
completely under client control and isn't checked, meaning that a
buggy/malicious client could cause broadcast messages containing an arbitrary
IP address.

While addressing this, move the broadcast message generation into
dhcpv4_lease(), so that the function can release the assignment straight away
and return NULL (it seems to return the released assignment just so that it can
be broadcast and later reaped by the expiry timer).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/270
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: remove mac_len argument to ubus_bcast_dhcp_event()
David Härdeman [Tue, 7 Oct 2025 11:44:08 +0000 (13:44 +0200)]
odhcpd: remove mac_len argument to ubus_bcast_dhcp_event()

All the places that call ubus_bcast_dhcp_event() have already checked that the
hardware adress is a MAC address, and it's not like we support anything else,
so remove the length argument.

The function is actually DHCPv4 specific, but that's a topic for another time.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/270
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: fix ubus support flag in help msg
David Härdeman [Tue, 7 Oct 2025 11:39:25 +0000 (13:39 +0200)]
odhcpd: fix ubus support flag in help msg

The cmake definition is a bit sneaky, the cmake variable is "UBUS", but it
leads to "WITH_UBUS" being defined for the compilation.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/270
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: reduce use of WITH_UBUS defines in code
David Härdeman [Tue, 7 Oct 2025 11:18:21 +0000 (13:18 +0200)]
odhcpd: reduce use of WITH_UBUS defines in code

By defining some dummy functions in the header file, we can avoid sprinkling
WITH_UBUS defines through the code, which helps readability and also ensures
that compilation errors (e.g. when a variable is renamed) is caught in builds
with and without ubus.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/270/
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agondp: fix macOS IPv6 compatibility by using link-local source addresses
Stephen Groat [Wed, 8 Oct 2025 18:54:51 +0000 (11:54 -0700)]
ndp: fix macOS IPv6 compatibility by using link-local source addresses

macOS ignores NDP packets that don't originate from link-local addresses,
causing IPv6 connectivity issues with odhcpd. This change ensures NDP
packets (Neighbor Advertisements and ICMP Echo Requests) are sent using
link-local source addresses for RFC 4861 compliance.

Changes:
* Add ndp_from_link_local configuration flag (defaults to true)
* Add odhcpd_send_with_src() to allow explicit source address control
* Add odhcpd_try_send_with_src() helper to eliminate code duplication
* Add odhcpd_get_interface_linklocal_addr() with caching for performance
* Update send_na() and ping6() to use link-local source addresses when
  enabled
* Add RFC 4861, §4.2 comments explaining the mandated behavior
* Maintain backward compatibility with fallback behavior

Fixes: openwrt/openwrt#7561 #202
Signed-off-by: Stephen Groat <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/242
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agorouter: fix SLAAC on subnets > 64
Álvaro Fernández Rojas [Tue, 7 Oct 2025 08:30:15 +0000 (10:30 +0200)]
router: fix SLAAC on subnets > 64

Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: simplify dhcpv4_setup_interface()
David Härdeman [Thu, 25 Sep 2025 13:45:07 +0000 (15:45 +0200)]
dhcpv4: simplify dhcpv4_setup_interface()

In the disable case, the function would close the interface uloop fd, free
assignments, check if the interface uloop fd should be closed, and return 0.

Simplify the function by bailing early in the disable case, which also removes
one level of indentation for the gist of the function body.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: make the cookie explicit in struct dhcpv4_message
David Härdeman [Thu, 25 Sep 2025 13:33:31 +0000 (15:33 +0200)]
dhcpv4: make the cookie explicit in struct dhcpv4_message

This makes the struct more explicit while allowing a number of magic offsets to
be removed.

Note that adding the packed attribute to struct dhcpv4_message is necessary
because the incoming data is cast directly to the struct. Adding the attribute
will give the struct an alignment of 1 instead of 4 (the normal alignment of
struct in_addr and uint32_t).

In the beginning of dhcpv4_handle_msg(), we cast "void *data" to struct
dhcpv4_message, but we can't know the alignment of "*data" (meaning it has
alignment 1). And by casting "*data" to a struct, we're actually promising the
compiler that "*data" has the same alignment as the struct.

Later, the address of members of struct dhcpv4_message are passed to other
functions, but since struct dhcpv4_message can be at any memory location, it's
not a given that the pointers are aligned. This issue only becomes visible once
the packed attribute is added, which is why the temporary (aligned) struct
in_addr variables are added in the WITH_UBUS blocks.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: simplify dhcpv4_fr_send() a bit
David Härdeman [Thu, 25 Sep 2025 13:16:53 +0000 (15:16 +0200)]
dhcpv4: simplify dhcpv4_fr_send() a bit

Some minor cleanups to dhcpv4_fr_send()

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: rename cookie variable
David Härdeman [Thu, 25 Sep 2025 13:10:50 +0000 (15:10 +0200)]
dhcpv4: rename cookie variable

The name is potentially confusing given that DHCPv4 includes the concept of a
magic cookie.  The variable is actually used as a cursor/write ptr, so rename
it to something more descriptive.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: remove some magic numbers
David Härdeman [Thu, 25 Sep 2025 09:44:43 +0000 (11:44 +0200)]
dhcpv4: remove some magic numbers

Add a bunch of #define's to dhcpv4.h and use the defined constants to reduce
the number of magic constants.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: update dhcpv4_msg_to_string()
David Härdeman [Wed, 24 Sep 2025 14:08:35 +0000 (16:08 +0200)]
dhcpv4: update dhcpv4_msg_to_string()

Simplify and make it cover all currently known message types.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agodhcpv4: dhcpv4_fr_rand_delay() fixups
David Härdeman [Wed, 24 Sep 2025 13:44:13 +0000 (15:44 +0200)]
dhcpv4: dhcpv4_fr_rand_delay() fixups

Move constants to dhcpv4.h, and make their names more descriptive. Also, use
abs() instead of labs(), since "msecs" is an int.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/266
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoubus: improve correspondence between DHCPv[46]
David Härdeman [Sat, 4 Oct 2025 17:51:44 +0000 (19:51 +0200)]
ubus: improve correspondence between DHCPv[46]

The same parameter is called "accept-reconf-nonce" in the "ipv4leases" case and
"accept-reconf" in the "ipv6leases". I couldn't find anything in the OpenWrt
trees which depended on either naming, but renaming the IPv4 case seems to be
the safer bet since it is not part of the standard installation.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/267
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoubus: minor correctness fix
David Härdeman [Sat, 4 Oct 2025 17:50:17 +0000 (19:50 +0200)]
ubus: minor correctness fix

Now, blobmsg_close_table() and blobmsg_close_array() happen to be identical, so
the actual code doesn't change at all, but this still caused me some
headscratching when I first saw it.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/267
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoubus: don't expose ipv4leases if not supported
David Härdeman [Sat, 4 Oct 2025 17:48:02 +0000 (19:48 +0200)]
ubus: don't expose ipv4leases if not supported

This risks confusing other clients, since the "ipv4leases" method will return
an empty list. Better to not have the method available on dbus at all, so
clients will get an error when odhcpd is built without IPv4 support.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/267
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
8 weeks agoodhcpd: print compiled-in features in help message
David Härdeman [Sun, 5 Oct 2025 15:32:52 +0000 (17:32 +0200)]
odhcpd: print compiled-in features in help message

This is mostly useful for hacking, as a way to quickly determine from the command-line
how odhcpd has been built. But I also plan to use it in LuCI [1].

[1] https://github.com/openwrt/luci/blob/master/modules/luci-base/root/usr/share/rpcd/ucode/luci

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/268
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
2 months agodhcpv4: rename a variable in dhcpv4_assign()
David Härdeman [Fri, 3 Oct 2025 10:19:50 +0000 (12:19 +0200)]
dhcpv4: rename a variable in dhcpv4_assign()

Rename "buf" to something more descriptive.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/264
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
2 months agodhcpv4: move the remaining forward declaration
David Härdeman [Wed, 24 Sep 2025 13:42:02 +0000 (15:42 +0200)]
dhcpv4: move the remaining forward declaration

Down to where it is clearer why it is needed (there's a circular dependency
between dhcpv4_fr_delay_timer() and dhcpv4_fr_rand_delay()).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/264
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
2 months agodhcpv4: move find_assignment_by_hwaddr()
David Härdeman [Wed, 24 Sep 2025 13:38:12 +0000 (15:38 +0200)]
dhcpv4: move find_assignment_by_hwaddr()

Move find_assignment_by_hwaddr() down to just before the sole user
(dhcpv4_lease()).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/264
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
2 months agodhcpv4: reorder some more functions
David Härdeman [Thu, 2 Oct 2025 14:15:42 +0000 (16:15 +0200)]
dhcpv4: reorder some more functions

Move dhcpv4_free_assignment(), dhcpv4_insert_assignment(), dhcpv4_assign() and
dhcpv4_lease() further up, allowing more forward declarations to be removed.

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