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
This commit is contained in:
Ad Schellevis 2022-08-15 14:48:00 +02:00
parent 45b8b1acbe
commit fcbdc44f63

View File

@ -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