Remove IPv6 addresses in dnsmasq leases file

IPv6 address format in dnsmasq leases file is incorrect (correct format
is described in bug description). This bad formatting generates the
following error when initializing dnsmasq:
  dnsmasq[20603]: failed to parse lease database, invalid line: \
    1547121093 fa:16:3e:a0:3a:9a [fd5b:1fd5:8295:5339::43] * ...

This patch removes the IPv6 addresses from the leases file, as proposed
in the bug, because the DHCP agent does not have the IAID (identity
association identifier) of each IPv6 address assigned.

In case of agent restart, dnsmasq won't have any IPv6 address in the
leases file, but the hosts file and the additional hosts file will
contain all MAC/IPv6 previous assignations. When the IPv6 client sends
a DHCPDISCOVER, dnsmasq will offer the same IPv6 address to this client.
At the same time, the client will request to the server the same address:
  DHCPDISCOVER(tap2c14823a-e6) fa:16:3e:54:c6:8e
  DHCPOFFER(tap2c14823a-e6) fd5b:1fd5:8295:5339::43 fa:16:3e:54:c6:8e
  DHCPREQUEST(tap2c14823a-e6) fd5b:1fd5:8295:5339::43 fa:16:3e:54:c6:8e
  DHCPACK(tap2c14823a-e6) fd5b:1fd5:8295:5339::43 fa:16:3e:54:c6:8e \
    host-fd5b-1fd5-8295-5339--43

Once dnsmasq updates the leases database, rewrites the leases file with the
new IPv6 address (including the IAID) and the server DUID (if not present).

Change-Id: Ib1b2f284ab81f1c4af7b08b5257b45a3f6e79c3e
Closes-Bug: #1722126
This commit is contained in:
Rodolfo Alonso Hernandez 2019-01-14 18:47:21 +00:00
parent 367288e99a
commit 4747de23d8
2 changed files with 6 additions and 9 deletions

View File

@ -638,21 +638,20 @@ class Dnsmasq(DhcpLocalProcess):
timestamp = 0
else:
timestamp = int(time.time()) + self.conf.dhcp_lease_duration
dhcp_enabled_subnet_ids = [s.id for s in
self._get_all_subnets(self.network)
if s.enable_dhcp]
dhcpv4_enabled_subnet_ids = [
s.id for s in self._get_all_subnets(self.network)
if s.enable_dhcp and s.ip_version == constants.IP_VERSION_4]
for host_tuple in self._iter_hosts():
port, alloc, hostname, name, no_dhcp, no_opts = host_tuple
# don't write ip address which belongs to a dhcp disabled subnet
# or an IPv6 SLAAC/stateless subnet
if no_dhcp or alloc.subnet_id not in dhcp_enabled_subnet_ids:
# or an IPv6 subnet.
if no_dhcp or alloc.subnet_id not in dhcpv4_enabled_subnet_ids:
continue
ip_address = self._format_address_for_dnsmasq(alloc.ip_address)
# all that matters is the mac address and IP. the hostname and
# client ID will be overwritten on the next renewal.
buf.write('%s %s %s * *\n' %
(timestamp, port.mac_address, ip_address))
(timestamp, port.mac_address, alloc.ip_address))
contents = buf.getvalue()
file_utils.replace_file(filename, contents)
LOG.debug('Done building initial lease file %s with contents:\n%s',

View File

@ -1465,9 +1465,7 @@ class TestDnsmasq(TestBase):
def _test_output_init_lease_file(self, timestamp):
expected = [
'00:00:80:aa:bb:cc 192.168.0.2 * *',
'00:00:f3:aa:bb:cc [fdca:3ba5:a17a:4ba3::2] * *',
'00:00:0f:aa:bb:cc 192.168.0.3 * *',
'00:00:0f:aa:bb:cc [fdca:3ba5:a17a:4ba3::3] * *',
'00:00:0f:rr:rr:rr 192.168.0.1 * *\n']
expected = "\n".join(['%s %s' % (timestamp, l) for l in expected])
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: