diff --git a/plist b/plist index d3bfc00f9..7c15b258b 100644 --- a/plist +++ b/plist @@ -281,6 +281,9 @@ /usr/local/opnsense/mvc/app/controllers/OPNsense/Cron/IndexController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Cron/ItemController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Cron/forms/dialogEdit.xml +/usr/local/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/Leases4Controller.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/ServiceController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/DHCP/Leases4Controller.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/ActivityController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/ActivityController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/DnsController.php @@ -711,6 +714,7 @@ /usr/local/opnsense/mvc/app/views/OPNsense/Core/reboot.volt /usr/local/opnsense/mvc/app/views/OPNsense/Core/service.volt /usr/local/opnsense/mvc/app/views/OPNsense/Cron/index.volt +/usr/local/opnsense/mvc/app/views/OPNsense/DHCP/leases4.volt /usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/arp.volt /usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/dns_diagnostics.volt /usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/fw_log.volt @@ -2007,7 +2011,6 @@ /usr/local/www/services_ntpd_pps.php /usr/local/www/services_opendns.php /usr/local/www/services_router_advertisements.php -/usr/local/www/status_dhcp_leases.php /usr/local/www/status_dhcpv6_leases.php /usr/local/www/status_habackup.php /usr/local/www/status_interfaces.php diff --git a/src/etc/inc/plugins.inc.d/dhcpd.inc b/src/etc/inc/plugins.inc.d/dhcpd.inc index 27fd8d7ba..7eaa475d0 100644 --- a/src/etc/inc/plugins.inc.d/dhcpd.inc +++ b/src/etc/inc/plugins.inc.d/dhcpd.inc @@ -1856,7 +1856,7 @@ function dhcpd_dhcrelay6_configure($verbose = false) service_log("done.\n", $verbose); } -function dhcpd_staticmap($domain_fallback = 'not.found', $ifconfig_details = null, $valid_addresses = true, $proto = null) +function dhcpd_staticmap($proto = null, $domain_fallback = 'not.found', $ifconfig_details = null, $valid_addresses = true) { $staticmap = []; foreach (empty($proto) ? [4, 6] : [$proto] as $inet) { diff --git a/src/etc/inc/plugins.inc.d/dnsmasq.inc b/src/etc/inc/plugins.inc.d/dnsmasq.inc index edfdb3cc1..d9935f096 100644 --- a/src/etc/inc/plugins.inc.d/dnsmasq.inc +++ b/src/etc/inc/plugins.inc.d/dnsmasq.inc @@ -258,7 +258,7 @@ function _dnsmasq_add_host_entries() $domain = $config['dnsmasq']['regdhcpdomain']; } - foreach (plugins_run('static_mapping', [$domain, legacy_interfaces_details()]) as $map) { + foreach (plugins_run('static_mapping', [null, $domain, legacy_interfaces_details()]) as $map) { foreach ($map as $host) { if (empty($host['hostname'])) { /* cannot register without a hostname */ diff --git a/src/etc/inc/plugins.inc.d/unbound.inc b/src/etc/inc/plugins.inc.d/unbound.inc index 5c2b2cc45..f6f932c15 100644 --- a/src/etc/inc/plugins.inc.d/unbound.inc +++ b/src/etc/inc/plugins.inc.d/unbound.inc @@ -544,7 +544,7 @@ function unbound_add_host_entries($ifconfig_details) } if (!empty($general['regdhcpstatic'])) { - foreach (plugins_run('static_mapping', [$config['system']['domain'], $ifconfig_details]) as $map) { + foreach (plugins_run('static_mapping', [null, $config['system']['domain'], $ifconfig_details]) as $map) { foreach ($map as $host) { if (empty($host['hostname'])) { /* cannot register without a hostname */ diff --git a/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/Leases4Controller.php b/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/Leases4Controller.php new file mode 100644 index 000000000..1f7fac2cc --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/Leases4Controller.php @@ -0,0 +1,167 @@ +sessionClose(); + $inactive = $this->request->get('inactive'); + $selected_interfaces = $this->request->get('selected_interfaces'); + $backend = new Backend(); + $config = Config::getInstance()->object(); + $online = []; + + /* get ARP data to match on */ + $arp_data = json_decode($backend->configdRun('interface list arp json'), true); + + foreach ($arp_data as $arp_entry) { + if (!$arp_entry['expired']) { + array_push($online, $arp_entry['mac'], $arp_entry['ip']); + } + } + + /* get configured static leases */ + $sleases = json_decode($backend->configdRun('dhcpd list static 4'), true); + + /* include inactive leases if requested */ + $leases = json_decode($backend->configdpRun('dhcpd list leases', [$inactive]), true); + foreach ($leases as $idx => $lease) { + /* set defaults */ + $leases[$idx]['type'] = 'dynamic'; + $leases[$idx]['status'] = 'offline'; + $leases[$idx]['descr'] = ''; + $leases[$idx]['mac'] = ''; + $leases[$idx]['starts'] = ''; + $leases[$idx]['ends'] = ''; + $leases[$idx]['hostname'] = ''; + $leases[$idx]['state'] = $lease['binding'] == 'free' ? 'expired' : $lease['binding']; + + if (array_key_exists('hardware', $lease)) { + $mac = $lease['hardware']['mac-address']; + $leases[$idx]['mac'] = $mac; + $leases[$idx]['status'] = in_array(strtolower($lease['address']), $online) ? 'online' : 'offline'; + unset($leases[$idx]['hardware']); + } + + if (array_key_exists('starts', $lease)) { + $leases[$idx]['starts'] = date('Y/m/d H:i:s', $lease['starts']); + } + + if (array_key_exists('ends', $lease)) { + $leases[$idx]['ends'] = date('Y/m/d H:i:s', $lease['ends']); + } + + if (array_key_exists('client-hostname', $lease)) { + $leases[$idx]['hostname'] = $lease['client-hostname']; + } + } + + /* handle static leases */ + $statics = []; + foreach ($sleases["dhcpd"] as $slease) { + $static = []; + $static['address'] = $slease['ipaddr']; + $static['type'] = 'static'; + $static['mac'] = $slease['mac']; + $static['starts'] = ''; + $static['ends'] = ''; + $static['hostname'] = $slease['hostname']; + $static['descr'] = $slease['descr']; + $static['state'] = 'active'; + $static['status'] = in_array(strtolower($static['mac']), $online) ? 'online' : 'offline'; + $statics[] = $static; + } + + $leases = array_merge($leases, $statics); + + $mac_man = json_decode($backend->configdRun('interface list macdb json'), true); + $interfaces = []; + foreach ($leases as $idx => $lease) { + /* include manufacturer info */ + $leases[$idx]['man'] = ''; + if ($lease['mac'] != '') { + $mac_hi = strtoupper(substr(str_replace(':', '', $lease['mac']),0, 6)); + $leases[$idx]['man'] = $mac_man[$mac_hi]; + } + + /* include interface */ + $leases[$idx]['if_descr'] = ''; + $leases[$idx]['if'] = ''; + foreach ($config->dhcpd->children() as $dhcpif => $dhcpifconf) { + $if = $config->interfaces->$dhcpif; + if (!empty((string)$if->ipaddr) && Util::isIpAddress((string)$if->ipaddr)) { + if (Util::isIPInCIDR($lease['address'], (string)$if->ipaddr . '/' . (string)$if->subnet)) { + $intf = (string)$if->descr; + $leases[$idx]['if_descr'] = $intf; + $leases[$idx]['if'] = $dhcpif; + + if (!array_key_exists($dhcpif, $interfaces)) { + $interfaces[$dhcpif] = $intf; + } + } + } + } + } + + $response = $this->searchRecordsetBase($leases, null, 'address', function ($key) use ($selected_interfaces) { + if (empty($selected_interfaces) || in_array($key['if'], $selected_interfaces)) { + return true; + } + + return false; + }); + + /* present relevant interfaces to the view so they can be sorted on */ + $response['interfaces'] = $interfaces; + return $response; + } + + public function delLeaseAction($ip) + { + $result = ["result" => "failed"]; + + if ($this->request->isPost()) { + $this->sessionClose(); + $response = json_decode((new Backend())->configdpRun("dhcpd remove lease", [$ip]), true); + if ($response["removed_leases"] != "0") { + $result["result"] = "deleted"; + } + } + + + return $result; + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/ServiceController.php new file mode 100644 index 000000000..92785bd8c --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Api/ServiceController.php @@ -0,0 +1,101 @@ +configdRun('service status dhcpd')); + + if (strpos($response, 'is running') > 0) { + $status = 'running'; + } elseif (strpos($response, 'not running') > 0) { + $status = 'stopped'; + } else { + $status = 'disabled'; + } + + return [ + 'status' => $status, + 'widget' => [ + 'caption_stop' => gettext("stop service"), + 'caption_start' => gettext("start service"), + 'caption_restart' => gettext("restart service") + ] + ]; + } + + public function startAction() + { + $result = ['status' => 'failed']; + + if ($this->request->isPost()) { + $this->sessionClose(); + $response = trim((new Backend())->configdRun('service start dhcpd')); + return ['status' => $response]; + } + + return $result; + } + + public function stopAction() + { + $result = ['status' => 'failed']; + + if ($this->request->isPost()) { + $this->sessionClose(); + $response = trim((new Backend())->configdRun('service stop dhcpd')); + return ['status' => $response]; + } + + return $result; + } + + public function restartAction() + { + $result = ['status' => 'failed']; + + if ($this->request->isPost()) { + $this->sessionClose(); + $response = trim((new Backend())->configdRun('service restart dhcpd')); + return ['status' => $response]; + } + + return $result; + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Leases4Controller.php b/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Leases4Controller.php new file mode 100644 index 000000000..13d6ca02c --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/DHCP/Leases4Controller.php @@ -0,0 +1,39 @@ +view->pick('OPNsense/DHCP/leases4'); + } +} diff --git a/src/opnsense/mvc/app/library/OPNsense/Firewall/Util.php b/src/opnsense/mvc/app/library/OPNsense/Firewall/Util.php index d629a0c84..6d6ba016a 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Firewall/Util.php +++ b/src/opnsense/mvc/app/library/OPNsense/Firewall/Util.php @@ -339,6 +339,59 @@ class Util return md5(json_encode($rule)); } + private static function isIPv4InCIDR($ip, $cidr) + { + list ($subnet, $bits) = explode('/', $cidr); + if ($bits === null) { + $bits = 32; + } + $ip = ip2long($ip); + $subnet = ip2long($subnet); + $mask = -1 << (32 - $bits); + $subnet &= $mask; + return ($ip & $mask) == $subnet; + } + + private static function isIPv6InCIDR($ip, $cidr) + { + $inet_to_bits = function($ip) { + $split = str_split($ip); + $bin_ip = ''; + foreach ($split as $char) { + $bin_ip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT); + } + return $bin_ip; + }; + + $in_addr = inet_pton($ip); + $bin_ip = $inet_to_bits($in_addr); + + list ($net, $maskbits) = explode('/', $cidr); + $net = inet_pton($net); + $bin_net = $inet_to_bits($net); + + $ip_net_bits = substr($bin_ip, 0, $maskbits); + $net_bits = substr($bin_net, 0, $maskbits); + + return $ip_net_bits === $net_bits; + } + + /** + * returns whether a given IP (v4 or v6) is in a CIDR block + */ + public static function isIPInCIDR($ip, $cidr) + { + if (!self::isIpAddress($ip)) { + return false; + } + + if (str_contains($ip, ':')) { + return self::isIPv6InCIDR($ip, $cidr); + } + + return self::isIPv4InCIDR($ip, $cidr); + } + /** * convert ipv4 cidr to netmask e.g. 24 --> 255.255.255.0 * @param int $bits ipv4 bits diff --git a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml index 26e353ba9..0276d0ee5 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml @@ -455,6 +455,13 @@ services_dhcp.php* + + Services: DHCP Server: Leases + + ui/dhcp/leases4 + api/dhcp/leases4/* + + Services: DHCPv6 Relay diff --git a/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml index b7bcfd6fb..0840d62fb 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml @@ -186,9 +186,7 @@ - -
- + diff --git a/src/opnsense/mvc/app/views/OPNsense/DHCP/leases4.volt b/src/opnsense/mvc/app/views/OPNsense/DHCP/leases4.volt new file mode 100644 index 000000000..6574edcc7 --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/DHCP/leases4.volt @@ -0,0 +1,173 @@ +{# + # Copyright (c) 2023 Deciso B.V. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without modification, + # are permitted provided that the following conditions are met: + # + # 1. Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # + # 2. Redistributions in binary form must reproduce the above copyright notice, + # this list of conditions and the following disclaimer in the documentation + # and/or other materials provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + # POSSIBILITY OF SUCH DAMAGE. + #} + + + + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ lang._('Interface') }}{{ lang._('IP Address') }}{{ lang._('MAC Address') }}{{ lang._('Hostname') }}{{ lang._('Description') }}{{ lang._('Start') }}{{ lang._('End') }}{{ lang._('Status') }}{{ lang._('State') }}{{ lang._('Lease Type') }}
+ +
+
+
diff --git a/src/opnsense/scripts/dhcp/get_leases.py b/src/opnsense/scripts/dhcp/get_leases.py index efe020b25..612829fca 100755 --- a/src/opnsense/scripts/dhcp/get_leases.py +++ b/src/opnsense/scripts/dhcp/get_leases.py @@ -38,10 +38,15 @@ import ujson app_params = {'inactive': '0'} params.update_params(app_params) +last_leases = dict() result = list() dhcpdleases = watchers.dhcpd.DHCPDLease() for lease in dhcpdleases.watch(): - if 'ends' not in lease or lease['ends'] is None or lease['ends'] > time.time() or app_params['inactive'] != '0': + # only the last entries for a given IP are relevant + last_leases[lease['address']] = lease + +for lease in last_leases.values(): + if ('ends' in lease and lease['ends'] is not None and lease['ends'] > time.time()) or app_params['inactive'] != '0': result.append(lease) print (ujson.dumps(result)) diff --git a/src/opnsense/service/conf/actions.d/actions_dhcpd.conf b/src/opnsense/service/conf/actions.d/actions_dhcpd.conf index de5563350..218c10b1a 100644 --- a/src/opnsense/service/conf/actions.d/actions_dhcpd.conf +++ b/src/opnsense/service/conf/actions.d/actions_dhcpd.conf @@ -1,9 +1,15 @@ [list.leases] -command:/usr/local/opnsense/scripts/dhcp/get_leases.py /inactive %s -parameters:%s +command:/usr/local/opnsense/scripts/dhcp/get_leases.py +parameters:/inactive %s type:script_output message:list dhcp leases %s +[list.static] +command:/usr/local/sbin/pluginctl -r static_mapping +parameters:%s +type:script_output +message: list dhcp static mappings %s + [update.prefixes] command:/usr/local/opnsense/scripts/dhcp/prefixes.php parameters: diff --git a/src/opnsense/site-python/watchers/dhcpd.py b/src/opnsense/site-python/watchers/dhcpd.py index d58f15dee..41f832186 100644 --- a/src/opnsense/site-python/watchers/dhcpd.py +++ b/src/opnsense/site-python/watchers/dhcpd.py @@ -76,6 +76,8 @@ class DHCPDLease(object): field_value = parts[1].split('"')[1] elif field_name == 'set' and len(parts) >= 4 and parts[1] == 'hostname-override' and parts[3].find('"') > -1: hostname_override = parts[3].split('"')[1] + elif field_name == 'binding' and len(parts) >= 3 and parts[1] == 'state': + field_value = parts[2].strip(';') if field_value is not None: lease[field_name] = field_value diff --git a/src/www/services_dhcp.php b/src/www/services_dhcp.php index 4c430102a..4af036f29 100644 --- a/src/www/services_dhcp.php +++ b/src/www/services_dhcp.php @@ -55,7 +55,7 @@ function reconfigure_dhcpd() clear_subsystem_dirty('staticmaps'); } -$config_copy_fieldsnames = array('enable', 'staticarp', 'failover_peerip', 'failover_split', 'dhcpleaseinlocaltime','descr', +$config_copy_fieldsnames = array('enable', 'staticarp', 'failover_peerip', 'failover_split', 'descr', 'defaultleasetime', 'maxleasetime', 'gateway', 'domain', 'domainsearchlist', 'denyunknown','ignoreuids', 'ddnsdomain', 'ddnsdomainprimary', 'ddnsdomainkeyname', 'ddnsdomainkey', 'ddnsdomainalgorithm', 'ddnsupdate', 'mac_allow', 'mac_deny', 'tftp', 'bootfilename', 'ldap', 'netboot', 'nextserver', 'filename', 'filename32', 'filename64', @@ -869,19 +869,6 @@ include("head.inc"); - - - - /> - - - - - diff --git a/src/www/status_dhcp_leases.php b/src/www/status_dhcp_leases.php deleted file mode 100644 index 80089ea12..000000000 --- a/src/www/status_dhcp_leases.php +++ /dev/null @@ -1,445 +0,0 @@ - - * Copyright (C) 2003-2004 Manuel Kasper - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -require_once("guiconfig.inc"); -require_once("config.inc"); -require_once("interfaces.inc"); -require_once("plugins.inc.d/dhcpd.inc"); - -function adjust_utc($dt) -{ - foreach (config_read_array('dhcpd') as $dhcpd) { - if (!empty($dhcpd['dhcpleaseinlocaltime'])) { - /* we want local time, so specify this is actually UTC */ - return strftime('%Y/%m/%d %H:%M:%S', strtotime("{$dt} UTC")); - } - } - - /* lease time is in UTC, here just pretend it's the correct time */ - return strftime('%Y/%m/%d %H:%M:%S UTC', strtotime($dt)); -} - -function remove_duplicate($array, $field) -{ - foreach ($array as $sub) { - $cmp[] = $sub[$field]; - } - $unique = array_unique(array_reverse($cmp,true)); - foreach ($unique as $k => $rien) { - $new[] = $array[$k]; - } - return $new; -} - -$interfaces = legacy_config_get_interfaces(array('virtual' => false)); - -if ($_SERVER['REQUEST_METHOD'] === 'GET') { - $leases_content = dhcpd_leases(4); - $leases_count = count($leases_content); - - exec("/usr/sbin/arp -an", $rawdata); - $arpdata_ip = array(); - $arpdata_mac = array(); - foreach ($rawdata as $line) { - $elements = explode(' ',$line); - if ($elements[3] != "(incomplete)") { - $arpent = array(); - $arpdata_ip[] = trim(str_replace(array('(',')'),'',$elements[1])); - $arpdata_mac[] = strtolower(trim($elements[3])); - } - } - unset($rawdata); - $pools = array(); - $leases = array(); - $i = 0; - $l = 0; - $p = 0; - - // Put everything together again - foreach($leases_content as $lease) { - /* split the line by space */ - $data = explode(" ", $lease); - /* walk the fields */ - $f = 0; - $fcount = count($data); - /* with less then 20 fields there is nothing useful */ - if ($fcount < 20) { - $i++; - continue; - } - while($f < $fcount) { - switch($data[$f]) { - case "failover": - $pools[$p]['name'] = trim($data[$f+2], '"'); - $pools[$p]['name'] = "{$pools[$p]['name']} (" . convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; - $pools[$p]['mystate'] = $data[$f+7]; - $pools[$p]['peerstate'] = $data[$f+14]; - $pools[$p]['mydate'] = $data[$f+10]; - $pools[$p]['mydate'] .= " " . $data[$f+11]; - $pools[$p]['peerdate'] = $data[$f+17]; - $pools[$p]['peerdate'] .= " " . $data[$f+18]; - $p++; - $i++; - continue 3; - case "lease": - $leases[$l]['ip'] = $data[$f+1]; - $leases[$l]['type'] = "dynamic"; - $f = $f+2; - break; - case "starts": - $leases[$l]['start'] = $data[$f+2]; - $leases[$l]['start'] .= " " . $data[$f+3]; - $f = $f+3; - break; - case "ends": - $leases[$l]['end'] = $data[$f+2]; - $leases[$l]['end'] .= " " . $data[$f+3]; - $f = $f+3; - break; - case "tstp": - $f = $f+3; - break; - case "tsfp": - $f = $f+3; - break; - case "atsfp": - $f = $f+3; - break; - case "cltt": - $f = $f+3; - break; - case "binding": - switch($data[$f+2]) { - case "active": - $leases[$l]['act'] = "active"; - break; - case "free": - $leases[$l]['act'] = "expired"; - $leases[$l]['online'] = "offline"; - break; - case "backup": - $leases[$l]['act'] = "reserved"; - $leases[$l]['online'] = "offline"; - break; - } - $f = $f+1; - break; - case "next": - /* skip the next binding statement */ - $f = $f+3; - break; - case "rewind": - /* skip the rewind binding statement */ - $f = $f+3; - break; - case "hardware": - $leases[$l]['mac'] = $data[$f+2]; - /* check if it's online and the lease is active */ - if (in_array($leases[$l]['ip'], $arpdata_ip)) { - $leases[$l]['online'] = 'online'; - } else { - $leases[$l]['online'] = 'offline'; - } - $f = $f+2; - break; - case "client-hostname": - if ($data[$f + 1] != '') { - $leases[$l]['hostname'] = preg_replace('/"/','',$data[$f + 1]); - } else { - $hostname = gethostbyaddr($leases[$l]['ip']); - if ($hostname != '') { - $leases[$l]['hostname'] = $hostname; - } - } - $f = $f+1; - break; - case "uid": - $f = $f+1; - break; - } - $f++; - } - $l++; - $i++; - /* slowly chisel away at the source array */ - array_shift($leases_content); - } - /* remove the old array */ - unset($lease_content); - - if (count($leases) > 0) { - $leases = remove_duplicate($leases,"ip"); - } - - if (count($pools) > 0) { - $pools = remove_duplicate($pools,"name"); - asort($pools); - } - - $macs = []; - foreach ($leases as $i => $this_lease) { - if (!empty($this_lease['mac'])) { - if (!isset($macs[$this_lease['mac']])) { - $macs[$this_lease['mac']] = []; - } - $macs[$this_lease['mac']][] = $i; - } - } - - foreach (dhcpd_staticmap("not.found", legacy_interfaces_details(), false, 4) as $static) { - $slease = []; - $slease['ip'] = $static['ipaddr']; - $slease['type'] = 'static'; - $slease['mac'] = $static['mac']; - $slease['start'] = ''; - $slease['end'] = ''; - $slease['hostname'] = $static['hostname']; - $slease['descr'] = $static['descr']; - $slease['act'] = 'static'; - $slease['online'] = in_array(strtolower($slease['mac']), $arpdata_mac) ? 'online' : 'offline'; - - if (isset($macs[$slease['mac']])) { - /* update lease with static data */ - foreach ($slease as $key => $value) { - if (!empty($value)) { - foreach ($macs[$slease['mac']] as $idx) { - $leases[$idx][$key] = $value; - } - } - } - } else { - $leases[] = $slease; - } - } - - if (isset($_GET['order']) && in_array($_GET['order'], ['int', 'ip', 'mac', 'hostname', 'descr', 'start', 'end', 'online', 'act'])) { - $order = $_GET['order']; - } else { - $order = 'ip'; - } - - usort($leases, - function ($a, $b) use ($order) { - $cmp = ($order === 'ip') ? 0 : strnatcasecmp($a[$order], $b[$order]); - if ($cmp === 0) { - $cmp = ipcmp($a['ip'], $b['ip']); - } - return $cmp; - } - ); -} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { - if (!empty($_POST['deleteip']) && is_ipaddr($_POST['deleteip'])) { - configdp_run('dhcpd remove lease', [$_POST['deleteip']]); - } - exit; -} - -$service_hook = 'dhcpd'; - -include("head.inc"); - -$leases_count = 0; - -foreach ($leases as $data) { - if (!($data['act'] == 'active' || $data['act'] == 'static' || $_GET['all'] == 1)) { - continue; - } - $leases_count++; -} - -$gentitle_suffix = " ($leases_count)"; -legacy_html_escape_form_data($leases); - -?> - - - - -
-
-
- 0):?> -
-
-
- - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - -
-
-
- - - - - - - - - - - - - - - - - $dhcpifconf) { - if (!empty($interfaces[$dhcpif]['ipaddr'])) { - $ip_min = gen_subnet($interfaces[$dhcpif]['ipaddr'], $interfaces[$dhcpif]['subnet']); - $ip_max = gen_subnet_max($interfaces[$dhcpif]['ipaddr'], $interfaces[$dhcpif]['subnet']); - if ($lip >= ip2ulong($ip_min) && $lip <= ip2ulong($ip_max)) { - $data['int'] = htmlspecialchars($interfaces[$dhcpif]['descr']); - $data['if'] = $dhcpif; - } - } - } - $mac_hi = strtoupper($data['mac'][0] . $data['mac'][1] . $data['mac'][3] . $data['mac'][4] . $data['mac'][6] . $data['mac'][7]); - ?> - - - - - - - - - - - - - - -
-
- -
- - - - - - "> - - - - - - - - - -
-
-
-
-
-
- - - - - - - - -
- -

- -
-
-
-
-