From eb0eb519dbe79e81dfe55f735db5a8abff1c10e8 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Thu, 28 Apr 2022 14:30:46 +0200 Subject: [PATCH] Services: DHCPv4 : leases - Move delete action to separate script in configd and offer the ability there to cleanse leases file for duplicate static leases. Since remove a single address or a list of (mac) addresses are two sides of the same coin it seems to make sense to add a script responsible for dhcpdv4 lease cleanup. Hooking this into dhcpd_dhcp_configure before startup also closes https://github.com/opnsense/core/issues/5724 --- src/etc/inc/plugins.inc.d/dhcpd.inc | 1 + src/opnsense/scripts/dhcp/cleanup_leases4 | 117 ++++++++++++++++++ .../service/conf/actions.d/actions_dhcpd.conf | 6 + src/www/status_dhcp_leases.php | 31 +---- 4 files changed, 125 insertions(+), 30 deletions(-) create mode 100755 src/opnsense/scripts/dhcp/cleanup_leases4 diff --git a/src/etc/inc/plugins.inc.d/dhcpd.inc b/src/etc/inc/plugins.inc.d/dhcpd.inc index e15e01da0..ea3bec0fc 100644 --- a/src/etc/inc/plugins.inc.d/dhcpd.inc +++ b/src/etc/inc/plugins.inc.d/dhcpd.inc @@ -1261,6 +1261,7 @@ EOD; /* fire up dhcpd in a chroot */ if (count($dhcpdifs) > 0) { + mwexec('/usr/local/opnsense/scripts/dhcp/cleanup_leases4 -m'); mwexec('/usr/local/sbin/dhcpd -user dhcpd -group dhcpd -chroot /var/dhcpd -cf /etc/dhcpd.conf -pf /var/run/dhcpd.pid ' . join(' ', $dhcpdifs)); } diff --git a/src/opnsense/scripts/dhcp/cleanup_leases4 b/src/opnsense/scripts/dhcp/cleanup_leases4 new file mode 100755 index 000000000..41f70adcf --- /dev/null +++ b/src/opnsense/scripts/dhcp/cleanup_leases4 @@ -0,0 +1,117 @@ +#!/usr/local/bin/php + $dhcpifconf) { + if (!empty($dhcpifconf['staticmap']) && !empty($dhcpifconf['enable'])) { + foreach ($dhcpifconf['staticmap'] as $static) { + if (!empty($static['mac'])) { + $addresses[$static['mac']] = !empty($static['ipaddr']) ? $static['ipaddr'] : ""; + } + } + } + } +} +if (!empty($opts['d'])) { + $addresses[] = $opts['d']; +} + +if (isset($opts['s'])){ + killbypid('/var/dhcpd/var/run/dhcpd.pid', 'TERM', true); +} elseif (isvalidpid('/var/dhcpd/var/run/dhcpd.pid')) { + echo "dhcpd active, can't update lease file"; + exit(1); +} + + + +$removed_leases = 0; +$fin = @fopen($dhcp_lease_file, 'r+'); +$fout = @fopen($dhcp_lease_file.'.new', 'w'); +if ($fin && flock($fin, LOCK_EX)) { + $lease = ''; + $lease_ip = ''; + $lease_mac = ';'; + while (($line = fgets($fin, 4096)) !== false) { + $fields = explode(' ', trim($line)); + if (strpos($line, 'lease ') === 0) { + $lease_ip = trim($fields[1]); + } elseif (strpos($line, 'hardware ethernet ') > 0) { + $lease_mac = strtolower(trim($fields[2], ' \n;')); + } + $lease .= $line; + + if ($line == "}\n") { + // end of segment, flush when relevant + $exact_match = isset($addresses[$lease_mac]) && $addresses[$lease_mac] == $lease_ip; + if ((!isset($addresses[$lease_mac]) && !in_array($lease_ip, $addresses)) || $exact_match) { + fputs($fout, $lease); + } else { + $removed_leases++; + } + $lease = ''; + $lease_ip = ''; + $lease_mac = ';'; + } + } + flock($fin, LOCK_UN); + fclose($fin); + fclose($fout); + @unlink($dhcp_lease_file); + @rename($dhcp_lease_file.'.new', $dhcp_lease_file); +} +if (isset($opts['s'])){ + dhcpd_dhcp4_configure(); +} + +echo json_encode(["removed_leases" => $removed_leases]); diff --git a/src/opnsense/service/conf/actions.d/actions_dhcpd.conf b/src/opnsense/service/conf/actions.d/actions_dhcpd.conf index f1c12ba71..200ba2a12 100644 --- a/src/opnsense/service/conf/actions.d/actions_dhcpd.conf +++ b/src/opnsense/service/conf/actions.d/actions_dhcpd.conf @@ -16,3 +16,9 @@ parameters:%s type:script message:restarting %s dhcpd description:Restart DHCPd + +[remove.lease] +command:/usr/local/opnsense/scripts/dhcp/cleanup_leases4 +parameters:-d=%s -s +type:script_output +message:remove lease for %s diff --git a/src/www/status_dhcp_leases.php b/src/www/status_dhcp_leases.php index 69507ea6f..697c6619a 100644 --- a/src/www/status_dhcp_leases.php +++ b/src/www/status_dhcp_leases.php @@ -256,36 +256,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { ); } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!empty($_POST['deleteip']) && is_ipaddr($_POST['deleteip'])) { - killbypid('/var/dhcpd/var/run/dhcpd.pid', 'TERM', true); - $leasesfile = '/var/dhcpd/var/db/dhcpd.leases'; /* XXX needs wrapper */ - $fin = @fopen($leasesfile, "r"); - $fout = @fopen($leasesfile.".new", "w"); - if ($fin) { - $ip_to_remove = $_POST['deleteip']; - $lease = ''; - while (($line = fgets($fin, 4096)) !== false) { - $fields = explode(' ', $line); - if ($fields[0] == 'lease') { - // lease segment, record ip - $lease = trim($fields[1]); - } - - if ($lease != $ip_to_remove) { - fputs($fout, $line); - } - - if ($line == "}\n") { - // end of segment - $lease = ''; - } - } - fclose($fin); - fclose($fout); - @unlink($leasesfile); - @rename($leasesfile.".new", $leasesfile); - - dhcpd_dhcp4_configure(); - } + configdp_run('dhcpd remove lease', [$_POST['deleteip']]); } exit; }