From fcbdc44f6384704f8873bf91308e9d10799d4e8c Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Mon, 15 Aug 2022 14:48:00 +0200 Subject: [PATCH] Unbound - dhcp watcher : when the same fqdn is being reinjected on a different address, it would be removed previously. This commit aims to keep a consistent state when fqdn's move from one host to the other. for https://github.com/opnsense/core/issues/5952 Test data: lease 172.23.128.214 { starts 5 2022/08/12 18:17:31; ends 5 2022/12/12 18:27:31; tstp 5 2022/08/12 18:52:31; tsfp 5 2022/08/12 18:52:31; atsfp 5 2022/08/12 18:52:31; cltt 5 2022/08/12 18:17:31; binding state active; next binding state expired; hardware ethernet xx:xx:xx:xx:xx:xx; uid "\001h\327\232\206,\361"; set vendor-class-identifier = "ubnt"; client-hostname "MyDHCPClient"; } lease 172.23.128.215 { starts 5 2022/08/12 18:17:31; ends 5 2022/08/12 18:47:31; tstp 5 2022/08/12 18:52:31; tsfp 5 2022/08/12 19:02:31; atsfp 5 2022/08/12 19:02:31; cltt 5 2022/08/12 18:17:31; binding state active; next binding state expired; hardware ethernet xx:xx:xx:xx:xx:xx; uid "\001h\327\232\206,\361"; set vendor-class-identifier = "ubnt"; client-hostname "MyDHCPClient"; } When 172.23.128.215's end date surpasses "now", a removal after "end" will remove both 172.23.128.215 and 172.23.128.214 --- src/opnsense/scripts/dhcp/unbound_watcher.py | 34 ++++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/opnsense/scripts/dhcp/unbound_watcher.py b/src/opnsense/scripts/dhcp/unbound_watcher.py index 7e187baad..416fadd71 100755 --- a/src/opnsense/scripts/dhcp/unbound_watcher.py +++ b/src/opnsense/scripts/dhcp/unbound_watcher.py @@ -65,11 +65,6 @@ def unbound_control(commands, input=None, output_stream=None): class UnboundLocalData: def __init__(self): - self._map_by_address = dict() - self._map_by_fqdn = dict() - self.load() - - def load(self): self._map_by_address = dict() self._map_by_fqdn = dict() with tempfile.NamedTemporaryFile() as output_stream: @@ -83,7 +78,9 @@ class UnboundLocalData: if address not in self._map_by_address: self._map_by_address[address] = list() self._map_by_address[address].append(fqdn) - self._map_by_fqdn[fqdn] = address + if fqdn not in self._map_by_fqdn: + self._map_by_fqdn[fqdn] = list() + self._map_by_fqdn[fqdn].append(address) def all_fqdns(self, address, fqdn): result = set() @@ -97,17 +94,22 @@ class UnboundLocalData: def cleanup(self, address, fqdn): if address in self._map_by_address: for rfqdn in self._map_by_address[address]: - if rfqdn in self._map_by_fqdn: - del self._map_by_fqdn[rfqdn] + if rfqdn in self._map_by_fqdn and address in self._map_by_fqdn[rfqdn]: + self._map_by_fqdn[rfqdn].remove(address) + if len(self._map_by_fqdn[rfqdn]) == 0: + del self._map_by_fqdn[rfqdn] del self._map_by_address[address] - if fqdn in self._map_by_fqdn: - if self._map_by_fqdn[fqdn] in self._map_by_address: - del self._map_by_address[self._map_by_fqdn[fqdn]] - del self._map_by_fqdn[fqdn] + + if fqdn in self._map_by_fqdn and address in self._map_by_fqdn[fqdn]: + self._map_by_fqdn[fqdn].remove(address) + if len(self._map_by_fqdn[fqdn]) == 0: + del self._map_by_fqdn[fqdn] def is_equal(self, address, fqdn): - tmp = self.all_fqdns(address, fqdn) - return len(tmp) == 1 and fqdn in self._map_by_fqdn and self._map_by_fqdn[fqdn] == address + return fqdn in self._map_by_fqdn and address in self._map_by_fqdn[fqdn] + + def fqdn_addresses(self, fqdn): + return self._map_by_fqdn[fqdn] if fqdn in self._map_by_fqdn else [] def run_watcher(target_filename, default_domain, watch_file, config): @@ -167,6 +169,10 @@ def run_watcher(target_filename, default_domain, watch_file, config): fqdn ] if unbound_local_data.is_equal(address, fqdn): unbound_local_data.cleanup(address, fqdn) + # same fqdn may be hooked to other (new) hosts, reinject the existing ones after removal + for addr in unbound_local_data.fqdn_addresses(fqdn): + add_rr.append(f'{fqdn} IN A {addr}') + del cached_leases[address] dhcpd_changed = True