I've been in the exactly same situation, trying to configure libvirt dhcp for matchbox. For reference I was working on Fedora 25
first option is impossible due to limitations of xml parsing in libvirt. Second option won't work since config will be overwritten by libvirt. You can't also configure dnsmasq to behave as a dhcp proxy for the same reasons as point one. The only way that I found to get this working, was to disable dhcp for that network completely (using virsh net-edit) and run dhcp as a separate service.
The default libvirt network will start two instances of dnsmasq, one for dns, one for dhcp. In my case that was:
# netstat -tulpn | grep dnsmasq
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN 2229/dnsmasq
udp 0 0 192.168.122.1:53 0.0.0.0:* 2229/dnsmasq
udp 0 0 0.0.0.0:67 0.0.0.0:* 2229/dnsmasq
# ps aux | grep [d]nsmasq
nobody 2229 0.0 0.0 49104 372 ? S 19:45 0:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
root 2230 0.0 0.0 49076 372 ? S 19:45 0:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
to disable dhcp open the network config:
virsh net-edit default
and remove the dhcp section
before:
<network>
<name>default</name>
<uuid>6fe7eafd-1925-4943-9596-2172bd55d1ac</uuid>
<forward mode='route'/>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:08:ed:3b'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.99'/>
</dhcp>
</ip>
</network>
after:
<network>
<name>default</name>
<uuid>6fe7eafd-1925-4943-9596-2172bd55d1ac</uuid>
<forward mode='route'/>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:08:ed:3b'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
</ip>
</network>
restart network for changes to take effect:
virsh net-destroy default
virsh net-start default
and confirm there's only one dnsmasq instance running now:
# netstat -tulpn | grep dnsmasq
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN 2431/dnsmasq
udp 0 0 192.168.122.1:53 0.0.0.0:* 2431/dnsmasq
# ps aux | grep [d]nsmasq
nobody 2431 0.0 0.0 49104 368 ? S 19:55 0:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
Now you want to start your own instance listening on 0.0.0.0:67
dnsmasq was already installed for libvirt (with disabled systemd service etc), so I just had to create the following config file (with some environment specific values, see http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html):
# cat /etc/dnsmasq.d/default_dhcp.conf
pid-file=/var/run/libvirt/network/default_dhcp.pid
bind-dynamic
port=0
except-interface=lo
interface=virbr0
dhcp-range=192.168.122.2,192.168.122.99
dhcp-no-override
enable-tftp
tftp-root=/var/lib/tftp
dhcp-lease-max=98
dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
dhcp-option=6,192.168.122.1
# if request comes from older PXE ROM, chainload to iPXE (via TFTP)
dhcp-boot=tag:!ipxe,undionly.kpxe
# if request comes from iPXE user class, set tag "ipxe"
dhcp-userclass=set:ipxe,iPXE
# point ipxe tagged requests to the matchbox iPXE boot script (via HTTP)
dhcp-boot=tag:ipxe,http://matchbox.foo:8080/boot.ipxe
# verbose
log-queries
log-dhcp
and start (and enable) the daemon with:
systemctl start dnsmasq
systemctl enable dnsmasq
which resulted in:
# netstat -tulpn | grep dnsmas
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN 1642/dnsmasq
udp 0 0 192.168.122.1:53 0.0.0.0:* 1642/dnsmasq
udp 0 0 0.0.0.0:67 0.0.0.0:* 2048/dnsmasq
and I could iPXE boot kvm vms using matchbox