<feed xmlns='http://www.w3.org/2005/Atom'>
<title>odhcp6c, branch master</title>
<subtitle>Openwrt DHCPv6 Client</subtitle>
<id>https://git.openwrt.org/project/odhcp6c/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/odhcp6c/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/'/>
<updated>2026-06-19T23:10:19Z</updated>
<entry>
<title>odhcp6c: fix handling of RFC6603 Prefix Exclude Option</title>
<updated>2026-06-19T23:10:19Z</updated>
<author>
<name>Mark H. Spatz</name>
</author>
<published>2026-01-17T01:05:19Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=07d324ee7222c0e15b9975281f18236fdccc11bd'/>
<id>urn:sha1:07d324ee7222c0e15b9975281f18236fdccc11bd</id>
<content type='text'>
Several bugs in the encoding and (more importantly) decoding of
DHCPV6_OPT_PD_EXCLUDE lead to the option being ignored in some received
messages, or the excluded subnet id being mangled to an incorrect value.

Fix both encoding and decoding, being more explicit and slightly more
verbose for clarity.

Signed-off-by: Mark H. Spatz &lt;mark.h.spatz@gmail.com&gt;
Link: https://github.com/openwrt/odhcp6c/pull/151
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>example: write all DNS servers to resolv.conf</title>
<updated>2026-06-03T23:16:53Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T23:25:08Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=daf4ec3054e753c99fdcc3ac5464926548b38351'/>
<id>urn:sha1:daf4ec3054e753c99fdcc3ac5464926548b38351</id>
<content type='text'>
setup_interface() built a 'dnspart' list that was never used, then
called update_resolv() with '$dns' — the leftover iteration variable
from the 'for dns in $RDNSS' loop, which holds only the last RDNSS
entry (or is empty when RDNSS is empty). Only that single nameserver
was written to resolv.conf; the remaining servers were dropped.

Drop the dead 'dnspart' block and pass the full '$RDNSS' list (already
merged with $RA_DNS just above) to update_resolv().

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>odhcp6c: do not treat DHCPv6 option type 0 as end-of-list</title>
<updated>2026-06-03T23:14:16Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-23T12:46:55Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=9177f236c2d74144a8a2cbdc7da0fcf2f582bc2c'/>
<id>urn:sha1:9177f236c2d74144a8a2cbdc7da0fcf2f582bc2c</id>
<content type='text'>
dhcpv6_for_each_option was structured as a chain of '&amp;&amp;' expressions
where each link both assigned a loop variable (otype, odata, olen)
and gated iteration on the assigned value being truthy. The middle
link, '((otype) = _o[0] &lt;&lt; 8 | _o[1])', made iteration stop on
otype == 0.

DHCPv6 has no end-of-list sentinel; option code 0 is just reserved
(RFC 8415 §21, IANA registry). A server (broken or hostile) that emits
an option with code 0 anywhere in the response would silently truncate
the client's view of the option list, masking everything that follows
— including STATUS, IA_NA / IA_PD, AUTH, etc.

Switch the assignments inside the for-loop condition to use the comma
operator so all three (otype, odata, olen) are unconditionally
written, and gate iteration purely on the bounds check
'odata + olen &lt;= end'. Callers see the same variables, in the same
order, just without the spurious type-0 truncation.

Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv6: enforce monotonic replay counter on Reconfigure RKAP</title>
<updated>2026-06-03T23:14:12Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-23T12:42:13Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=df27a49c98d89cb945767763a47bfafe196165c8'/>
<id>urn:sha1:df27a49c98d89cb945767763a47bfafe196165c8</id>
<content type='text'>
RFC 8415 §20.4.3 requires the client to track the replay-detection
field of an authenticated Reconfigure message and to reject any
incoming Reconfigure whose replay value does not exceed the highest
one already accepted. Without this check, a captured Reconfigure can
be replayed at will: the HMAC is still valid because the message
itself has not changed, and the client will happily re-trigger
Renew / Rebind / Information-Request cycles on demand.

Add a per-binding (reconf_replay, reconf_replay_seen) tuple,
initialised in dhcpv6_promote_server_cand() alongside reconf_key so a
fresh binding starts with a fresh window. Compare the wire replay
field (network byte order, decoded with be64toh()) against the stored
value before running the HMAC verification; only commit the value on
a verified HMAC so a forged message cannot move the counter forward.

Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv6: reject Reconfigure with malformed or duplicate Message option</title>
<updated>2026-06-03T23:14:06Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-23T12:40:32Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=afc3c85348640b0c01adb88482fc0802e7a47f31'/>
<id>urn:sha1:afc3c85348640b0c01adb88482fc0802e7a47f31</id>
<content type='text'>
RFC 8415 §21.19 requires the Reconfigure Message option to appear
exactly once in a Reconfigure message and carry a one-byte payload of
RENEW, REBIND or INFO_REQ. The previous handler iterated the entire
option list, silently ignored malformed (olen != 1) RECONF_MESSAGE
options, accepted any number of well-formed ones, and processed the
side-effects (zeroing t1/t2) for each one in turn via fall-through.

Two consequences:

  * A Reconfigure carrying two RECONF_MESSAGE options of different
    types (REBIND then RENEW) would fire the REBIND fall-through
    (zero t2 then t1), and then the subsequent RENEW iteration would
    re-zero t1 — leaving the client in a state determined by the
    last option rather than rejecting the malformed message.

  * The fall-through made the per-option work look like state
    machine transitions when it was really just a one-shot side
    effect of the chosen response type.

Walk the options, hard-reject duplicate RECONF_MESSAGE / wrong olen /
unknown response type, and apply the t1/t2 reset once at the end
based on the (validated) chosen response type. Behaviour for a
spec-compliant Reconfigure is unchanged.

Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv6: require known SERVERID when validating Reconfigure</title>
<updated>2026-06-03T23:14:01Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-23T12:39:46Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=0a4e51db30d5a13175cdda7830a5fcb882e88659'/>
<id>urn:sha1:0a4e51db30d5a13175cdda7830a5fcb882e88659</id>
<content type='text'>
dhcpv6_response_is_valid() previously set serverid_ok = true whenever
STATE_SERVER_ID was empty, on the assumption that an empty stored
server-id only happens for Solicit/Advertise where the client has not
yet bound to a server. The same code path is taken for Reconfigure
messages (req_msg_type == DHCPV6_MSG_UNKNOWN), so a Reconfigure
message that arrived while STATE_SERVER_ID was somehow empty (state
corruption, an early Reconfigure delivered before binding completes,
or a server-side bug) would be accepted without server identification.

RFC 8415 §18.2.11 requires the SERVERID in a Reconfigure to match the
server the client received the lease from. Only set serverid_ok = true
on an empty stored server-id when we are not validating a Reconfigure;
for DHCPV6_MSG_UNKNOWN we leave it false so the response is dropped.

Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ra: clear captive-portal state when router signals unrestricted URI</title>
<updated>2026-06-03T23:13:57Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-23T12:38:19Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=86a6665e4bb0dc83364a15f5099b77877472fe4b'/>
<id>urn:sha1:86a6665e4bb0dc83364a15f5099b77877472fe4b</id>
<content type='text'>
RFC 8910 §2 lets a router advertise that there is no captive portal
by carrying the IANA-assigned unrestricted-portal URI in the option.
The handler previously did the right thing when a real URI arrived
(replace the stored value) but silently did nothing when the
unrestricted URI arrived, so any URI cached from an earlier RA was
retained forever and the state script kept reporting a captive portal
that the network had since cleared.

Move the odhcp6c_clear_state() call ahead of the unrestricted-URI
check so an explicit "no captive portal" RA also drops any previously
stored URI. The subsequent add_state() only runs for real URIs.

Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>odhcp6c: remove pidfile on exit</title>
<updated>2026-06-03T23:13:52Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-23T12:37:43Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=1797d2bca3c04e7201e2bf784515dec3bd24d735'/>
<id>urn:sha1:1797d2bca3c04e7201e2bf784515dec3bd24d735</id>
<content type='text'>
Until now the pidfile was created on daemonize but never removed on
clean shutdown. After a normal exit a stale pidfile sat in /var/run
holding the pid of a process that no longer exists; the next time
something pid-checked it (e.g. an init script doing 'kill -0 $(cat
…)') it would either hit ESRCH or, in the worst case, signal an
unrelated process that had recycled the pid.

Remember the path we successfully opened, strdup() it so it remains
valid through atexit handlers regardless of where main()'s buffer
lives, and unlink() it from odhcp6c_cleanup(). We only register the
path after a successful open(), so we do not unlink something we
never wrote.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubus: don't leak an open table on malformed S46 rule/bind in s46_to_blob</title>
<updated>2026-06-03T23:13:47Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T21:22:59Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=6c1c4c48d4a24d83cb0923f440f5cb648f2c59cf'/>
<id>urn:sha1:6c1c4c48d4a24d83cb0923f440f5cb648f2c59cf</id>
<content type='text'>
s46_to_blob() opens a per-option table with blobmsg_open_table() and
only closes it at the bottom of the outer loop iteration. The length
checks for an S46 rule and for an S46 v4v6bind, however, "continue" the
outer loop when the embedded IPv6 prefix length is bogus
(prefix6_len &gt; 128, i.e. prefix6len &gt; sizeof(in6)), skipping that
blobmsg_close_table().

Because blob nesting must be balanced (blob_nest_end patches the parent
length from buf-&gt;head), the dangling open table corrupts the emitted
blob. The S46 container options are stored verbatim from the DHCPv6
reply, so a malicious server can trigger this and feed garbled state to
every ubus subscriber and get_state caller.

Close the table before continuing, matching the empty-table handling
already used for options that match neither branch. The inner-loop DMR
check is left untouched: it only continues the inner option loop, which
does not own the table.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ubus: walk entries correctly and don't leak an open table in entry_to_blob</title>
<updated>2026-06-03T23:13:42Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T21:21:30Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcp6c/commit/?id=df4f199c02fca874293c49a277704f29cfeaaa08'/>
<id>urn:sha1:df4f199c02fca874293c49a277704f29cfeaaa08</id>
<content type='text'>
entry_to_blob() had two defects in its per-entry loop:

1. It iterated the state buffer with a fixed stride of
   sizeof(struct odhcp6c_entry) (e[i]), but odhcp6c_entry is a flexible
   record whose real size is odhcp6c_entry_size() - the head rounded up
   past a variable-length auxtarget area. This is the same bug already
   fixed for the script path in commit ("script: walk entries
   with odhcp6c_next_entry in entry_to_env"); the ubus serializer was
   left walking entries at the wrong offset whenever an entry carries a
   non-zero auxlen.

2. blobmsg_open_table() was called before the validity check, so the
   "continue" taken for an invalid (valid == 0) non-prefix entry skipped
   the matching blobmsg_close_table(). blob nesting requires balanced
   open/close (blob_nest_end patches the parent length from buf-&gt;head),
   so a dangling open table corrupts the emitted blob. A DHCPv6 server
   handing out an IA Address with a valid lifetime of 0 reaches this:
   the entry is parsed and stored just before ubus_dhcp_event() -&gt;
   states_to_blob(), with no intervening odhcp6c_expire(), so the
   malformed table is sent to every ubus subscriber.

Walk the buffer with odhcp6c_next_entry() (matching search_to_blob() and
entry_to_env()) and perform the validity check before opening the table.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcp6c/pull/160
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
</feed>
