mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-25 02:12:58 +02:00
Merge remote-tracking branch 'ms/unbound-socket' into next
This commit is contained in:
@@ -19,6 +19,7 @@ usr/sbin/unbound-checkconf
|
||||
usr/sbin/unbound-control
|
||||
usr/sbin/unbound-control-setup
|
||||
usr/sbin/unbound-dhcp-leases-bridge
|
||||
usr/sbin/unbound-dhcp-leases-client
|
||||
usr/sbin/unbound-host
|
||||
#usr/share/man/man1/unbound-host.1
|
||||
#usr/share/man/man3/libunbound.3
|
||||
|
||||
@@ -28,15 +28,15 @@ import ipaddress
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
import signal
|
||||
import socket
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import inotify.adapters
|
||||
import threading
|
||||
|
||||
LOCAL_TTL = 60
|
||||
|
||||
@@ -65,121 +65,248 @@ def setup_logging(daemon=True, loglevel=logging.INFO):
|
||||
|
||||
return log
|
||||
|
||||
def ip_address_to_reverse_pointer(address):
|
||||
parts = address.split(".")
|
||||
parts.reverse()
|
||||
|
||||
return "%s.in-addr.arpa" % ".".join(parts)
|
||||
|
||||
def reverse_pointer_to_ip_address(rr):
|
||||
parts = rr.split(".")
|
||||
|
||||
# Only take IP address part
|
||||
parts = reversed(parts[0:4])
|
||||
|
||||
return ".".join(parts)
|
||||
|
||||
class UnboundDHCPLeasesBridge(object):
|
||||
def __init__(self, dhcp_leases_file, fix_leases_file, unbound_leases_file, hosts_file):
|
||||
def __init__(self, dhcp_leases_file, fix_leases_file, unbound_leases_file, hosts_file, socket_path):
|
||||
self.leases_file = dhcp_leases_file
|
||||
self.fix_leases_file = fix_leases_file
|
||||
self.hosts_file = hosts_file
|
||||
self.socket_path = socket_path
|
||||
|
||||
self.watches = {
|
||||
self.leases_file : inotify.constants.IN_MODIFY,
|
||||
self.fix_leases_file : 0,
|
||||
self.hosts_file : 0,
|
||||
}
|
||||
self.socket = None
|
||||
|
||||
# Store all known leases
|
||||
self.leases = set()
|
||||
|
||||
# Create a queue for all received events
|
||||
self.queue = queue.Queue()
|
||||
|
||||
# Initialize the worker
|
||||
self.worker = Worker(self.queue, callback=self._handle_message)
|
||||
|
||||
self.unbound = UnboundConfigWriter(unbound_leases_file)
|
||||
self.running = False
|
||||
|
||||
# Load all required data
|
||||
self.reload()
|
||||
|
||||
def run(self):
|
||||
log.info("Unbound DHCP Leases Bridge started on %s" % self.leases_file)
|
||||
self.running = True
|
||||
|
||||
i = inotify.adapters.Inotify()
|
||||
# Launch the worker
|
||||
self.worker.start()
|
||||
|
||||
# Add watches for the directories of every relevant file
|
||||
for f, mask in self.watches.items():
|
||||
i.add_watch(
|
||||
os.path.dirname(f),
|
||||
mask | inotify.constants.IN_CLOSE_WRITE | inotify.constants.IN_MOVED_TO,
|
||||
)
|
||||
# Open the server socket
|
||||
self.socket = self._open_socket(self.socket_path)
|
||||
|
||||
# Enabled so that we update hosts and leases on startup
|
||||
update_hosts = update_leases = True
|
||||
while True:
|
||||
# Accept any incoming connections
|
||||
try:
|
||||
conn, peer = self.socket.accept()
|
||||
except OSError as e:
|
||||
break
|
||||
|
||||
while self.running:
|
||||
log.debug("Wakeup of main loop")
|
||||
try:
|
||||
# Receive what the client is sending
|
||||
data, ancillary_data, flags, address = conn.recvmsg(4096)
|
||||
|
||||
# Process the entire inotify queue and identify what we need to do
|
||||
for event in i.event_gen():
|
||||
# Nothing to do
|
||||
if event is None:
|
||||
break
|
||||
# Log that we have received some data
|
||||
log.debug("Received message of %s byte(s)" % len(data))
|
||||
|
||||
# Decode the event
|
||||
header, type_names, path, filename = event
|
||||
# Decode the data
|
||||
message = self._decode_message(data)
|
||||
|
||||
file = os.path.join(path, filename)
|
||||
# Add the message to the queue
|
||||
self.queue.put(message)
|
||||
|
||||
log.debug("inotify event received for %s: %s", file, " ".join(type_names))
|
||||
conn.send(b"OK\n")
|
||||
|
||||
# Did the hosts file change?
|
||||
if self.hosts_file == file:
|
||||
update_hosts = True
|
||||
# Send ERROR to the client if something went wrong
|
||||
except Exception as e:
|
||||
log.error("Could not handle message: %s" % e)
|
||||
|
||||
# We will need to update the leases on any change
|
||||
update_leases = True
|
||||
conn.send(b"ERROR\n")
|
||||
continue
|
||||
|
||||
# Update hosts (if needed)
|
||||
if update_hosts:
|
||||
self.hosts = self.read_static_hosts()
|
||||
# Close the connection
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
# Update leases (if needed)
|
||||
if update_leases:
|
||||
self.update_dhcp_leases()
|
||||
|
||||
# Reset
|
||||
update_hosts = update_leases = False
|
||||
|
||||
# Wait a moment before we start the next iteration
|
||||
time.sleep(5)
|
||||
# Terminate the worker
|
||||
self.queue.put(None)
|
||||
self.worker.join()
|
||||
|
||||
log.info("Unbound DHCP Leases Bridge terminated")
|
||||
|
||||
def update_dhcp_leases(self):
|
||||
leases = []
|
||||
def _open_socket(self, path):
|
||||
# Allocate a new socket
|
||||
s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
|
||||
|
||||
for lease in DHCPLeases(self.leases_file):
|
||||
# Don't bother with any leases that don't have a hostname
|
||||
if not lease.fqdn:
|
||||
# Unlink any old sockets
|
||||
try:
|
||||
os.unlink(path)
|
||||
except FileNotFoundError as e:
|
||||
pass
|
||||
|
||||
# Bind the socket
|
||||
try:
|
||||
s.bind(self.socket_path)
|
||||
except OSError as e:
|
||||
log.error("Could not open socket at %s: %s" % (path, e))
|
||||
|
||||
raise SystemExit(1) from e
|
||||
|
||||
# Listen
|
||||
s.listen(128)
|
||||
|
||||
return s
|
||||
|
||||
def _decode_message(self, data):
|
||||
message = {}
|
||||
|
||||
for line in data.splitlines():
|
||||
# Skip empty lines
|
||||
if not line:
|
||||
continue
|
||||
|
||||
leases.append(lease)
|
||||
# Try to decode the line
|
||||
try:
|
||||
line = line.decode()
|
||||
except UnicodeError as e:
|
||||
log.error("Could not decode %r: %s" % (line, e))
|
||||
|
||||
raise e
|
||||
|
||||
# Split the line
|
||||
key, _, value = line.partition("=")
|
||||
|
||||
# Skip the line if it does not have a value
|
||||
if not _:
|
||||
raise ValueError("No value given")
|
||||
|
||||
# Store the attributes
|
||||
message[key] = value
|
||||
|
||||
return message
|
||||
|
||||
def _handle_message(self, message):
|
||||
log.debug("Handling message:")
|
||||
for key in message:
|
||||
log.debug(" %-20s = %s" % (key, message[key]))
|
||||
|
||||
# Extract the event type
|
||||
event = message.get("EVENT")
|
||||
|
||||
# Check if event is set
|
||||
if not event:
|
||||
raise ValueError("The message does not have EVENT set")
|
||||
|
||||
# COMMIT
|
||||
elif event == "commit":
|
||||
address = message.get("ADDRESS")
|
||||
name = message.get("NAME")
|
||||
|
||||
# Find the old lease
|
||||
old_lease = self._find_lease(address)
|
||||
|
||||
# Create a new lease
|
||||
lease = Lease(address, {
|
||||
"client-hostname" : name,
|
||||
})
|
||||
self._add_lease(lease)
|
||||
|
||||
# Can we skip the update?
|
||||
if old_lease:
|
||||
if lease.rrset == old_lease.rrset:
|
||||
log.debug("Won't update %s as nothing has changed" % lease)
|
||||
return
|
||||
|
||||
# Remove the old lease first
|
||||
self.unbound.remove_lease(old_lease)
|
||||
self._remove_lease(old_lease)
|
||||
|
||||
# Apply the lease
|
||||
self.unbound.apply_lease(lease)
|
||||
|
||||
# RELEASE/EXPIRY
|
||||
elif event in ("release", "expiry"):
|
||||
address = message.get("ADDRESS")
|
||||
|
||||
# Find the lease
|
||||
lease = self._find_lease(address)
|
||||
|
||||
if not lease:
|
||||
log.warning("Could not find lease for %s" % address)
|
||||
return
|
||||
|
||||
# Remove the lease
|
||||
self.unbound.remove_lease(lease)
|
||||
self._remove_lease(lease)
|
||||
|
||||
# Raise an error if the event is not supported
|
||||
else:
|
||||
raise ValueError("Unsupported event: %s" % event)
|
||||
|
||||
def update_dhcp_leases(self):
|
||||
# Drop all known leases
|
||||
self.leases.clear()
|
||||
|
||||
# Add all dynamic leases
|
||||
for lease in DHCPLeases(self.leases_file):
|
||||
self._add_lease(lease)
|
||||
|
||||
# Add all static leases
|
||||
for lease in FixLeases(self.fix_leases_file):
|
||||
leases.append(lease)
|
||||
|
||||
# Skip any leases that also are a static host
|
||||
leases = [l for l in leases if not l.fqdn in self.hosts]
|
||||
|
||||
# Remove any inactive or expired leases
|
||||
leases = [l for l in leases if l.active and not l.expired]
|
||||
self._add_lease(lease)
|
||||
|
||||
# Dump leases
|
||||
if leases:
|
||||
if self.leases:
|
||||
log.debug("DHCP Leases:")
|
||||
for lease in leases:
|
||||
for lease in self.leases:
|
||||
log.debug(" %s:" % lease.fqdn)
|
||||
log.debug(" State: %s" % lease.binding_state)
|
||||
log.debug(" Start: %s" % lease.time_starts)
|
||||
log.debug(" End : %s" % lease.time_ends)
|
||||
if lease.expired:
|
||||
if lease.has_expired():
|
||||
log.debug(" Expired")
|
||||
|
||||
self.unbound.update_dhcp_leases(leases)
|
||||
self.unbound.update_dhcp_leases(self.leases)
|
||||
|
||||
def _add_lease(self, lease):
|
||||
# Skip leases without an FQDN
|
||||
if not lease.fqdn:
|
||||
log.debug("Skipping lease without an FQDN: %s" % lease)
|
||||
return
|
||||
|
||||
# Skip any leases that also are a static host
|
||||
elif lease.fqdn in self.hosts:
|
||||
log.debug("Skipping lease for which a static host exists: %s" % lease)
|
||||
return
|
||||
|
||||
# Don't add expired leases
|
||||
elif lease.has_expired():
|
||||
log.debug("Skipping expired lease: %s" % lease)
|
||||
return
|
||||
|
||||
# Remove any previous leases
|
||||
self._remove_lease(lease)
|
||||
|
||||
# Store the lease
|
||||
self.leases.add(lease)
|
||||
|
||||
def _find_lease(self, ipaddr):
|
||||
"""
|
||||
Returns the lease with the specified IP address
|
||||
"""
|
||||
if not isinstance(ipaddr, ipaddress.IPv4Address):
|
||||
ipaddr = ipaddress.IPv4Address(ipaddr)
|
||||
|
||||
for lease in self.leases:
|
||||
if lease.ipaddr == ipaddr:
|
||||
return lease
|
||||
|
||||
def _remove_lease(self, lease):
|
||||
try:
|
||||
self.leases.remove(lease)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def read_static_hosts(self):
|
||||
log.info("Reading static hosts from %s" % self.hosts_file)
|
||||
@@ -219,8 +346,47 @@ class UnboundDHCPLeasesBridge(object):
|
||||
|
||||
return hosts
|
||||
|
||||
def terminate(self):
|
||||
self.running = False
|
||||
def reload(self, *args, **kwargs):
|
||||
# Read all static hosts
|
||||
self.hosts = self.read_static_hosts()
|
||||
|
||||
# Unconditionally update all leases and reload Unbound
|
||||
self.update_dhcp_leases()
|
||||
|
||||
def terminate(self, *args, **kwargs):
|
||||
# Close the socket
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
|
||||
|
||||
class Worker(threading.Thread):
|
||||
"""
|
||||
The worker is launched in a separate thread
|
||||
which allows us to perform some tasks asynchronously.
|
||||
"""
|
||||
def __init__(self, queue, callback):
|
||||
super().__init__()
|
||||
|
||||
self.queue = queue
|
||||
self.callback = callback
|
||||
|
||||
def run(self):
|
||||
log.debug("Worker %s launched" % self.native_id)
|
||||
|
||||
while True:
|
||||
message = self.queue.get()
|
||||
|
||||
# If the message is None, we have to quit
|
||||
if message is None:
|
||||
break
|
||||
|
||||
# Call the callback
|
||||
try:
|
||||
self.callback(message)
|
||||
except Exception as e:
|
||||
log.error("Callback failed: %s" % e, exc_info=True)
|
||||
|
||||
log.debug("Worker %s terminated" % self.native_id)
|
||||
|
||||
|
||||
class DHCPLeases(object):
|
||||
@@ -255,18 +421,12 @@ class DHCPLeases(object):
|
||||
if not "hardware" in properties:
|
||||
continue
|
||||
|
||||
# Skip inactive leases
|
||||
elif not properties.get("binding", "state active"):
|
||||
continue
|
||||
|
||||
lease = Lease(ipaddr, properties)
|
||||
|
||||
# Check if a lease for this Ethernet address already
|
||||
# exists in the list of known leases. If so replace
|
||||
# if with the most recent lease
|
||||
for i, l in enumerate(leases):
|
||||
if l.ipaddr == lease.ipaddr:
|
||||
leases[i] = max(lease, l)
|
||||
break
|
||||
|
||||
else:
|
||||
leases.append(lease)
|
||||
leases.append(lease)
|
||||
|
||||
return leases
|
||||
|
||||
@@ -300,12 +460,10 @@ class DHCPLeases(object):
|
||||
|
||||
|
||||
class FixLeases(object):
|
||||
cache = {}
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
self._leases = self.cache[self.path] = self._parse()
|
||||
self._leases = self._parse()
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._leases)
|
||||
@@ -313,9 +471,10 @@ class FixLeases(object):
|
||||
def _parse(self):
|
||||
log.info("Reading fix leases from %s" % self.path)
|
||||
|
||||
leases = []
|
||||
now = datetime.datetime.utcnow()
|
||||
|
||||
leases = []
|
||||
|
||||
with open(self.path) as f:
|
||||
for line in f.readlines():
|
||||
line = line.rstrip()
|
||||
@@ -333,72 +492,42 @@ class FixLeases(object):
|
||||
l = Lease(ipaddr, {
|
||||
"binding" : "state active",
|
||||
"client-hostname" : hostname,
|
||||
"hardware" : "ethernet %s" % hwaddr,
|
||||
"starts" : now.strftime("%w %Y/%m/%d %H:%M:%S"),
|
||||
"ends" : "never",
|
||||
})
|
||||
leases.append(l)
|
||||
|
||||
# Try finding any deleted leases
|
||||
for lease in self.cache.get(self.path, []):
|
||||
if lease in leases:
|
||||
continue
|
||||
|
||||
# Free the deleted lease
|
||||
lease.free()
|
||||
leases.append(lease)
|
||||
|
||||
return leases
|
||||
|
||||
|
||||
class Lease(object):
|
||||
def __init__(self, ipaddr, properties):
|
||||
if not isinstance(ipaddr, ipaddress.IPv4Address):
|
||||
ipaddr = ipaddress.IPv4Address(ipaddr)
|
||||
|
||||
self.ipaddr = ipaddr
|
||||
self._properties = properties
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s %s for %s (%s)>" % (self.__class__.__name__,
|
||||
self.ipaddr, self.hwaddr, self.hostname)
|
||||
return "<%s for %s (%s)>" % (self.__class__.__name__, self.ipaddr, self.hostname)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.ipaddr == other.ipaddr and self.hwaddr == other.hwaddr
|
||||
if isinstance(other, self.__class__):
|
||||
return self.ipaddr == other.ipaddr
|
||||
|
||||
return NotImplemented
|
||||
|
||||
def __gt__(self, other):
|
||||
if not self.ipaddr == other.ipaddr:
|
||||
return
|
||||
if isinstance(other, self.__class__):
|
||||
if not self.ipaddr == other.ipaddr:
|
||||
return NotImplemented
|
||||
|
||||
if not self.hwaddr == other.hwaddr:
|
||||
return
|
||||
return self.time_starts > other.time_starts
|
||||
|
||||
return self.time_starts > other.time_starts
|
||||
return NotImplemented
|
||||
|
||||
@property
|
||||
def binding_state(self):
|
||||
state = self._properties.get("binding")
|
||||
|
||||
if state:
|
||||
state = state.split(" ", 1)
|
||||
return state[1]
|
||||
|
||||
def free(self):
|
||||
self._properties.update({
|
||||
"binding" : "state free",
|
||||
})
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
return self.binding_state == "active"
|
||||
|
||||
@property
|
||||
def hwaddr(self):
|
||||
hardware = self._properties.get("hardware")
|
||||
|
||||
if not hardware:
|
||||
return
|
||||
|
||||
ethernet, address = hardware.split(" ", 1)
|
||||
|
||||
return address
|
||||
def __hash__(self):
|
||||
return hash(self.ipaddr)
|
||||
|
||||
@property
|
||||
def hostname(self):
|
||||
@@ -488,8 +617,10 @@ class Lease(object):
|
||||
|
||||
return self._parse_time(ends)
|
||||
|
||||
@property
|
||||
def expired(self):
|
||||
def has_expired(self):
|
||||
if not self.time_starts:
|
||||
return
|
||||
|
||||
if not self.time_ends:
|
||||
return self.time_starts > datetime.datetime.utcnow()
|
||||
|
||||
@@ -503,10 +634,10 @@ class Lease(object):
|
||||
|
||||
return [
|
||||
# Forward record
|
||||
(self.fqdn, "%s" % LOCAL_TTL, "IN A", self.ipaddr),
|
||||
(self.fqdn, "%s" % LOCAL_TTL, "IN A", "%s" % self.ipaddr),
|
||||
|
||||
# Reverse record
|
||||
(ip_address_to_reverse_pointer(self.ipaddr), "%s" % LOCAL_TTL,
|
||||
(self.ipaddr.reverse_pointer, "%s" % LOCAL_TTL,
|
||||
"IN PTR", self.fqdn),
|
||||
]
|
||||
|
||||
@@ -560,6 +691,9 @@ class UnboundConfigWriter(object):
|
||||
command = ["unbound-control"]
|
||||
command.extend(args)
|
||||
|
||||
# Log what we are doing
|
||||
log.debug("Running %s" % " ".join(command))
|
||||
|
||||
try:
|
||||
subprocess.check_output(command)
|
||||
|
||||
@@ -570,6 +704,28 @@ class UnboundConfigWriter(object):
|
||||
|
||||
raise e
|
||||
|
||||
def apply_lease(self, lease):
|
||||
"""
|
||||
This method takes a lease and updates Unbound at runtime.
|
||||
"""
|
||||
log.debug("Applying lease %s" % lease)
|
||||
|
||||
for rr in lease.rrset:
|
||||
log.debug("Adding new record %s" % " ".join(rr))
|
||||
|
||||
self._control("local_data", *rr)
|
||||
|
||||
def remove_lease(self, lease):
|
||||
"""
|
||||
This method takes a lease and removes it from Unbound at runtime.
|
||||
"""
|
||||
log.debug("Removing lease %s" % lease)
|
||||
|
||||
for name, ttl, type, content in lease.rrset:
|
||||
log.debug("Removing records for %s" % name)
|
||||
|
||||
self._control("local_data_remove", name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Bridge for DHCP Leases and Unbound DNS")
|
||||
@@ -588,6 +744,9 @@ if __name__ == "__main__":
|
||||
metavar="PATH", help="Path to the fix leases file")
|
||||
parser.add_argument("--hosts", default="/var/ipfire/main/hosts",
|
||||
metavar="PATH", help="Path to static hosts file")
|
||||
parser.add_argument("--socket-path", default="/var/run/unbound-dhcp-leases-bridge.sock",
|
||||
metavar="PATH", help="Socket Path",
|
||||
)
|
||||
|
||||
# Parse command line arguments
|
||||
args = parser.parse_args()
|
||||
@@ -602,13 +761,14 @@ if __name__ == "__main__":
|
||||
loglevel = logging.DEBUG
|
||||
|
||||
bridge = UnboundDHCPLeasesBridge(args.dhcp_leases, args.fix_leases,
|
||||
args.unbound_leases, args.hosts)
|
||||
args.unbound_leases, args.hosts, socket_path=args.socket_path)
|
||||
|
||||
with daemon.DaemonContext(
|
||||
detach_process=args.daemon,
|
||||
stderr=None if args.daemon else sys.stderr,
|
||||
signal_map = {
|
||||
signal.SIGHUP : bridge.update_dhcp_leases,
|
||||
signal.SIGHUP : bridge.reload,
|
||||
signal.SIGINT : bridge.terminate,
|
||||
signal.SIGTERM : bridge.terminate,
|
||||
},
|
||||
) as daemon:
|
||||
|
||||
75
config/unbound/unbound-dhcp-leases-client
Normal file
75
config/unbound/unbound-dhcp-leases-client
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# #
|
||||
# IPFire.org - A linux based firewall #
|
||||
# Copyright (C) 2016 Michael Tremer #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
SOCKET="/var/run/unbound-dhcp-leases-bridge.sock"
|
||||
|
||||
main() {
|
||||
local event="${1}"
|
||||
shift
|
||||
|
||||
# Check if we have received an event
|
||||
if [ -z "${event}" ]; then
|
||||
echo "${0}: Missing event" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
# Check if the socket exists
|
||||
if [ ! -S "${SOCKET}" ]; then
|
||||
echo "${0}: ${SOCKET} does not exist" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Connect to the socket
|
||||
coproc NC { nc -U "${SOCKET}"; }
|
||||
|
||||
local arg
|
||||
local response
|
||||
|
||||
# Send the message
|
||||
{
|
||||
# Send the event
|
||||
echo "EVENT=${event}"
|
||||
|
||||
# Send all arguments
|
||||
for arg in $@; do
|
||||
echo "${arg}"
|
||||
done
|
||||
} >&"${NC[1]}"
|
||||
|
||||
# Close the input part of the connection
|
||||
exec {NC[1]}>&-
|
||||
|
||||
# Capture the response
|
||||
read response <&"${NC[0]}"
|
||||
|
||||
case "${response}" in
|
||||
OK)
|
||||
return 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "${response}" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@" || exit $?
|
||||
@@ -1374,6 +1374,49 @@ sub buildconf {
|
||||
}
|
||||
}
|
||||
|
||||
# Add event handlers
|
||||
print FILE <<EOF;
|
||||
on commit {
|
||||
set ClientAddress = concat(
|
||||
"ADDRESS=",
|
||||
binary-to-ascii(10, 8, ".", leased-address)
|
||||
);
|
||||
set ClientName = concat(
|
||||
"NAME=",
|
||||
pick-first-value(option host-name, config-option-host-name, client-name, "")
|
||||
);
|
||||
|
||||
if (ClientName != "") {
|
||||
execute("/usr/sbin/unbound-dhcp-leases-client", "commit", ClientAddress, ClientName);
|
||||
}
|
||||
}
|
||||
|
||||
on release {
|
||||
set ClientAddress = concat(
|
||||
"ADDRESS=",
|
||||
binary-to-ascii(10, 8, ".", leased-address)
|
||||
);
|
||||
set ClientName = concat(
|
||||
"NAME=",
|
||||
pick-first-value(option host-name, config-option-host-name, client-name, "")
|
||||
);
|
||||
|
||||
if (ClientName != "") {
|
||||
execute("/usr/sbin/unbound-dhcp-leases-client", "release", ClientAddress, ClientName);
|
||||
}
|
||||
}
|
||||
|
||||
on expiry {
|
||||
set ClientAddress = concat(
|
||||
"ADDRESS=",
|
||||
binary-to-ascii(10, 8, ".", leased-address)
|
||||
);
|
||||
|
||||
execute("/usr/sbin/unbound-dhcp-leases-client", "expiry", ClientAddress);
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
#write fixed leases if any. Does not handle duplicates to write them elsewhere than the global scope.
|
||||
my $key = 0;
|
||||
foreach my $line (@current2) {
|
||||
|
||||
1
lfs/dhcp
1
lfs/dhcp
@@ -84,6 +84,7 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
|
||||
--sysconfdir=/etc/dhcp \
|
||||
--with-srv-conf-file=/etc/dhcp/dhcpd.conf \
|
||||
--with-srv-lease-file=/var/state/dhcp/dhcpd.leases \
|
||||
--enable-execute \
|
||||
--enable-paranoia \
|
||||
--enable-early-chroot \
|
||||
--disable-dhcpv6
|
||||
|
||||
@@ -99,6 +99,8 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
|
||||
# Install DHCP leases bridge
|
||||
install -v -m 755 $(DIR_SRC)/config/unbound/unbound-dhcp-leases-bridge \
|
||||
/usr/sbin/unbound-dhcp-leases-bridge
|
||||
install -v -m 755 $(DIR_SRC)/config/unbound/unbound-dhcp-leases-client \
|
||||
/usr/sbin/unbound-dhcp-leases-client
|
||||
|
||||
# Install key
|
||||
-mkdir -pv /var/lib/unbound
|
||||
|
||||
Reference in New Issue
Block a user