Skip to content

fix: refuse a dhcp/hosts reservation IP another host already holds - #186

Merged
raspbeguy merged 1 commit into
mainfrom
fix/dhcp-hosts-unique-ip
Aug 13, 2026
Merged

fix: refuse a dhcp/hosts reservation IP another host already holds#186
raspbeguy merged 1 commit into
mainfrom
fix/dhcp-hosts-unique-ip

Conversation

@raspbeguy

Copy link
Copy Markdown
Member

Closes #185. Also changes the code-of-conduct contact to openwrt-iac@gugod.fr, which is unrelated but a one-line docs edit and not worth its own cycle.

The defect

Two dhcp/hosts reservations on one IP were accepted, and dnsmasq treats that as fatal rather than ignoring the second entry: it logs duplicate dhcp-host IP address and refuses to start. The box loses DNS and DHCP until someone removes one by hand, from a write that answered 200.

On a device, before:

POST ip=192.168.1.95  -> 200
POST ip=192.168.1.95  -> 200      dnsmasq procs: 0

after:

POST ip=192.168.1.95  -> 200
POST ip=192.168.1.95  -> 422      errors[0] = { field: "ip", code: "conflict" }
                                  dnsmasq procs: 2, section not created
POST ip=192.168.1.96  -> 200      dnsmasq procs: 2

The caveat in the issue, resolved by measurement

I filed #185 warning that unique_field is a single-field mechanism while dnsmasq's rule might be subtler, and that two reservations could legitimately share an address for a host with several NICs. That turns out not to apply, because uapi already models that case as one reservation carrying several macs. Measured both shapes on a device:

one reservation, two macs, one ip   -> 200, dnsmasq running
two reservations, one ip            -> dnsmasq dead

So nothing legitimate needs two reservations on one address, and a plain uniqueness check matches what dnsmasq actually rejects. dnsmasq's own source is not in the SDK feeds, so this is empirical rather than read off the implementation, which is worth knowing if its behaviour ever changes.

Reservations with no ip are untouched: check_unique_field skips a value that is not a non-empty string, and several address-less reservations are normal. Covered by a test.

One correction to the issue text

I wrote there that unique_field "produces a 409 conflict before anything is written". It produces 422 with field code conflict, consistent with every other validation failure. My unit test asserted 409 and failed, which is how I found out. The write is still refused before the commit, so the "before anything is written" half was right.

Does this change the wire surface? Yes, and it is the breaking direction: a payload that was accepted is now refused. It is a fix rather than a feat! because the accepted payload never worked, it took the DNS server down. Anyone relying on it had a broken box. Worth a note in the next release's changelog rather than a major.

Was it verified against a real device, or only by CI? On a device, with the tables above, including that dnsmasq survives the refused write and that a different address still succeeds. Four unit tests cover the refusal, the field and code, a different address, several macs on one reservation, and address-less reservations. Mutation-checked: removing unique_field fails the refusal test with 200.

Was LuCI checked? Yes. luci-mod-network's dhcp view has no duplicate-IP guard, so this is a deliberate improvement on the baseline rather than a correction of an accidental divergence, as #185 records.

@raspbeguy
raspbeguy merged commit 73f57e7 into main Aug 13, 2026
6 checks passed
@raspbeguy
raspbeguy deleted the fix/dhcp-hosts-unique-ip branch August 13, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dhcp/hosts accepts a duplicate reservation IP, and dnsmasq then refuses to start

1 participant