Bug 14031

Summary: Web UI and /var/lib/knot-resolver/leases.db mismatch
Product: IPFire Reporter: barneyklarnocks <ben>
Component: ---Assignee: Michael Tremer <michael.tremer>
Status: ASSIGNED --- QA Contact:
Severity: Minor Usability    
Priority: - Unknown - CC: adolf.belka, andreas.radke, bbitsch, michael.tremer, paul.lipinski
Version: 2   
Hardware: x86_64   
OS: Other   
Attachments: A screenshot of part of the "Network" > "DHCP Server" page on the IPFire Web UI.
The output of "grep dhcpd /var/log/messages"

Description barneyklarnocks 2026-07-28 13:34:49 UTC
Created attachment 1736 [details]
A screenshot of part of the "Network" > "DHCP Server" page on the IPFire Web UI.

The forum thread https://community.ipfire.org/t/web-ui-and-var-lib-knot-resolver-leases-db-mismatch/16015 discusses issues with mismatched DHCP data presented in the IPFire Web UI and in the Knot resolver SQLite database located at /var/lib/knot-resolver/leases.db.

The issue has recurrent today. A stale entry for a host remains in the Web UI. It is not present in the SQLite database.

Compare the attached image with that of the contents of the LEASES table in the SQLite database, reproduced below.


[root@ipfire ~]# DB_PATH="/var/lib/knot-resolver/leases.db" && sqlite3 $DB_PATH <<<"select * from leases"
╭───────────────┬──────────────────────────────────────────╮
│    address    │                 hostname                 │
╞═══════════════╪══════════════════════════════════════════╡
│ 192.168.1.110 │ cube.cooley.road.                        │
│ 192.168.1.3   │ colour-duplex-laser.cooley.road.         │
│ 192.168.1.207 │ galaxy-s21-5g.cooley.road.               │
│ 192.168.1.139 │ iphone.cooley.road.                      │
│ 192.168.1.2   │ ubnt.cooley.road.                        │
│ 192.168.1.246 │ re305.cooley.road.                       │
│ 192.168.1.247 │ media.cooley.road.                       │
│ 192.168.1.235 │ heep.cooley.road.                        │
│ 192.168.1.100 │ spooky-action-at-a-distance.cooley.road. │
╰───────────────┴──────────────────────────────────────────╯
[root@ipfire ~]# 


The host 'spooky-action-at-a-distance' is listed twice in the WebUI, once at 192.168.1.100 and once at 192.168.1.245. Only the latter IP address is in use:

[root@ipfire ~]# ping 192.168.1.100
PING 192.168.1.100 (192.168.1.100) 56(84) bytes of data.
^C
--- 192.168.1.100 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2061ms

[root@ipfire ~]# ping 192.168.1.245
PING 192.168.1.245 (192.168.1.245) 56(84) bytes of data.
64 bytes from 192.168.1.245: icmp_seq=1 ttl=64 time=673 ms
64 bytes from 192.168.1.245: icmp_seq=2 ttl=64 time=287 ms
64 bytes from 192.168.1.245: icmp_seq=3 ttl=64 time=520 ms
^C
--- 192.168.1.245 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 287.084/493.332/672.776/158.594 ms


Notes:

I'm running IPFire 2.29 (x86_64) - Core-Update 203.

It may be that my default and max lease times for the GREEN interface trigger the issue; these are not the defaults provided with IPFire, AFAIR:

Default lease time (mins): 1440
Max lease time (mins):    10080


The host 'spooky-action-at-a-distance' is an Android device. The GREEN network is a mix of Linux, Android, iPhone devices, a Brother network printer and a Ubiquity Wifi AP. (The device re305.cooley.road (currently offline) may be a Windows or Mac laptop. I'll find out.) I don't have a BLUE network configured.
Comment 1 barneyklarnocks 2026-07-28 13:38:11 UTC
Typo: "recurrent" > "recurred"
Comment 2 Michael Tremer 2026-07-29 07:41:41 UTC
Hello Barney,

do you have the output of "grep dhcpd /var/log/messages" for me? DNS relies on the DHCP server updating any entries and we need to check if there has been an update issued.
Comment 3 barneyklarnocks 2026-07-29 08:59:37 UTC
I do, going back to midnight of 26 July. I logged this issue on 28 July. It's just 626 lines. I'll attached the whole thing as a gzipped tarball. One sec...
Comment 4 barneyklarnocks 2026-07-29 09:05:51 UTC
Created attachment 1737 [details]
The output of "grep dhcpd /var/log/messages"

Attached.
Comment 5 Paul Lipinski 2026-10-02 18:55:38 UTC
I believe I have a concrete reproduction of the issue described in this bug, including evidence of what appears to be the underlying cause.

My system is running:

IPFire 2.29 (x86_64)
Core Update 203
isc-dhcpd 4.4.3-P1

I have a Linux VM named "ghost" with MAC address:

bc:24:11:7e:95:b8

The VM initially received 192.168.1.222 via DHCP. IPFire's DHCP log shows:

Oct 1 20:40:32 router dhcpd: DHCPOFFER on 192.168.1.222 to bc:24:11:7e:95:b8 (ghost) via green0
Oct 1 20:40:32 router dhcpd: DHCPREQUEST for 192.168.1.222 (192.168.1.1) from bc:24:11:7e:95:b8 (ghost) via green0
Oct 1 20:40:32 router dhcpd: DHCPACK on 192.168.1.222 to bc:24:11:7e:95:b8 (ghost) via green0

At that point, dhcp-lease created the expected Knot Resolver lease:

192.168.1.222 | ghost.lan.

Later, the VM's network interface was restarted. It then received its configured fixed address, 192.168.1.212:

Oct 1 21:32:53 router dhcpd: DHCPOFFER on 192.168.1.212 to bc:24:11:7e:95:b8 (ghost) via green0
Oct 1 21:32:53 router dhcpd: DHCPREQUEST for 192.168.1.212 (192.168.1.1) from bc:24:11:7e:95:b8 (ghost) via green0
Oct 1 21:32:53 router dhcpd: DHCPACK on 192.168.1.212 to bc:24:11:7e:95:b8 (ghost) via green0

Therefore DHCP itself successfully changed the client's address, and the hostname remained "ghost".

However, /var/lib/knot-resolver/leases.db continued to contain:

192.168.1.222 | ghost.lan.

There was no:

192.168.1.212 | ghost.lan.

entry.

Consequently, DNS continued returning:

ghost.lan -> 192.168.1.222

even though the DHCP client was actually using 192.168.1.212.

I manually deleted the stale .222 row from leases.db and renewed the DHCP lease. After that, the .212 | ghost.lan. entry was successfully created and DNS immediately returned .212.

I inspected /usr/sbin/dhcp-lease and the leases database schema.

The table has a unique hostname index:

CREATE UNIQUE INDEX leases_hostname
ON leases(hostname COLLATE NOCASE);

However, the commit operation performs an UPSERT based only on the address:

INSERT INTO leases(address, hostname)
VALUES (:address, :hostname)
ON CONFLICT(address)
DO UPDATE SET hostname = excluded.hostname;

This appears to create a problem when a hostname moves from one address to another.

For example, if the database contains:

192.168.1.222 | ghost.lan.

and DHCP subsequently commits:

192.168.1.212 | ghost.lan.

the address does not conflict, so the ON CONFLICT(address) clause does not apply. SQLite instead attempts to insert a second ghost.lan. row, which conflicts with the unique hostname index.

The old row therefore remains and the new row is not created.

The script also does not appear to check/propagate the exit status of the SQLite operation in the commit path, so this failure can occur without an obvious DHCP error.

The important distinction in this case is that the DHCP server did issue the address/hostname update.

The DHCP log proves the same client/hostname moved:

192.168.1.222 -> ghost
192.168.1.212 -> ghost

The failure appears to occur specifically while synchronizing that change into:

/var/lib/knot-resolver/leases.db

This also seems to explain the observed Web UI/DNS/database mismatch described in this bug.

A possible fix would be for dhcp-lease commit to remove an existing entry for the same hostname at another address before inserting/updating the new address, or otherwise perform an UPSERT based on hostname as well as address.

For example, conceptually:

BEGIN;

DELETE FROM leases
WHERE hostname = :hostname
AND address != :address;

INSERT INTO leases(address, hostname)
VALUES (:address, :hostname)
ON CONFLICT(address)
DO UPDATE SET hostname = excluded.hostname;

COMMIT;

The SQLite return status should also be checked so that a failed database update is not silently reported as successful.