mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 08:09:41 +00:00
dhcp6: migrate leases page to MVC (#6653)
* dhcp6: add backend for listing dhcpv6 leases * dhcp6: add leases view and controller * dhcp6: lease deletion backend * dhcp6: move to separate dhcpv6 directory to accomodate the service control UI
This commit is contained in:
parent
8dc8d39d4b
commit
f782be9cbf
@ -1856,7 +1856,7 @@ function dhcpd_dhcrelay6_configure($verbose = false)
|
||||
service_log("done.\n", $verbose);
|
||||
}
|
||||
|
||||
function dhcpd_staticmap($proto = null, $domain_fallback = 'not.found', $ifconfig_details = null, $valid_addresses = true)
|
||||
function dhcpd_staticmap($proto = null, $valid_addresses = true, $domain_fallback = 'not.found', $ifconfig_details = null)
|
||||
{
|
||||
$staticmap = [];
|
||||
foreach (empty($proto) ? [4, 6] : [$proto] as $inet) {
|
||||
@ -1926,29 +1926,6 @@ function dhcpd_staticmap($proto = null, $domain_fallback = 'not.found', $ifconfi
|
||||
return $staticmap;
|
||||
}
|
||||
|
||||
function dhcpd_leases($inet = 4)
|
||||
{
|
||||
$file = $inet == 6 ? '/var/dhcpd/var/db/dhcpd6.leases' : '/var/dhcpd/var/db/dhcpd.leases';
|
||||
$token = $inet == 6 ? 'ia-' : 'lease';
|
||||
$content = [];
|
||||
$section = [];
|
||||
|
||||
$fin = @fopen($file, 'r');
|
||||
if ($fin) {
|
||||
while (($line = fgets($fin, 4096)) !== false) {
|
||||
if (strpos($line, $token) === 0) {
|
||||
$section = [];
|
||||
} elseif (strpos($line, '}') === 0 && count($section) > 0) {
|
||||
$content[] = implode('', $section);
|
||||
}
|
||||
$section[] = rtrim($line, "\n;");
|
||||
}
|
||||
fclose($fin);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
function dhcpd_parse_duid($duid_string)
|
||||
{
|
||||
$parsed_duid = [];
|
||||
|
||||
@ -258,7 +258,7 @@ function _dnsmasq_add_host_entries()
|
||||
$domain = $config['dnsmasq']['regdhcpdomain'];
|
||||
}
|
||||
|
||||
foreach (plugins_run('static_mapping', [null, $domain, legacy_interfaces_details()]) as $map) {
|
||||
foreach (plugins_run('static_mapping', [null, true, $domain, legacy_interfaces_details()]) as $map) {
|
||||
foreach ($map as $host) {
|
||||
if (empty($host['hostname'])) {
|
||||
/* cannot register without a hostname */
|
||||
|
||||
@ -544,7 +544,7 @@ function unbound_add_host_entries($ifconfig_details)
|
||||
}
|
||||
|
||||
if (!empty($general['regdhcpstatic'])) {
|
||||
foreach (plugins_run('static_mapping', [null, $config['system']['domain'], $ifconfig_details]) as $map) {
|
||||
foreach (plugins_run('static_mapping', [null, true, $config['system']['domain'], $ifconfig_details]) as $map) {
|
||||
foreach ($map as $host) {
|
||||
if (empty($host['hostname'])) {
|
||||
/* cannot register without a hostname */
|
||||
|
||||
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace OPNsense\DHCPv6\Api;
|
||||
|
||||
use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Firewall\Util;
|
||||
|
||||
class LeasesController extends ApiControllerBase
|
||||
{
|
||||
public function searchLeaseAction()
|
||||
{
|
||||
$this->sessionClose();
|
||||
$inactive = $this->request->get('inactive');
|
||||
$selected_interfaces = $this->request->get('selected_interfaces');
|
||||
$backend = new Backend();
|
||||
$config = Config::getInstance()->object();
|
||||
$online = [];
|
||||
$leases = [];
|
||||
|
||||
$ndp_data = json_decode($backend->configdRun('interface list ndp json'), true);
|
||||
|
||||
foreach ($ndp_data as $ndp_entry) {
|
||||
array_push($online, $ndp_entry['mac'], $ndp_entry['ip']);
|
||||
}
|
||||
|
||||
$raw_leases = json_decode($backend->configdpRun('dhcpd list leases6', [$inactive]), true);
|
||||
foreach ($raw_leases as $raw_lease) {
|
||||
if (!array_key_exists('addresses', $raw_lease)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set defaults */
|
||||
$lease = [];
|
||||
$lease['type'] = 'dynamic';
|
||||
$lease['status'] = 'offline';
|
||||
$lease['lease_type'] = $raw_lease['lease_type'];
|
||||
$lease['iaid'] = $raw_lease['iaid'];
|
||||
$lease['duid'] = $raw_lease['duid'];
|
||||
$lease['iaid_duid'] = $raw_lease['iaid_duid'];
|
||||
$lease['descr'] = '';
|
||||
$lease['if'] = '';
|
||||
|
||||
if (array_key_exists('cltt', $raw_lease)) {
|
||||
$lease['cltt'] = date('Y/m/d H:i:s', $raw_lease['cltt']);
|
||||
}
|
||||
|
||||
/* XXX we pick the first address, this will be fine for a typical deployment
|
||||
* according to RFC8415 section 6.6, but it should be noted that the protocol
|
||||
* (and isc-dhcpv6) is capable of handing over multiple addresses/prefixes to
|
||||
* a single client within a single lease. The backend accounts for this.
|
||||
*/
|
||||
$seg = $raw_lease['addresses'][0];
|
||||
$lease['state'] = $seg['binding'] == 'free' ? 'expired' : $seg['binding'];
|
||||
if (array_key_exists('ends', $seg)) {
|
||||
$lease['ends'] = date('Y/m/d H:i:s', $seg['ends']);
|
||||
}
|
||||
|
||||
$lease['address'] = $seg['iaaddr'];
|
||||
$lease['online'] = in_array(strtolower($seg['iaaddr']), $online) ? 'online' : 'offline';
|
||||
|
||||
$leases[] = $lease;
|
||||
}
|
||||
|
||||
$sleases = json_decode($backend->configdRun('dhcpd list static 6 0'), true);
|
||||
$statics = [];
|
||||
foreach ($sleases['dhcpd'] as $slease) {
|
||||
$static = [
|
||||
'address' => $slease['ipaddrv6'],
|
||||
'type' => 'static',
|
||||
'cltt' => '',
|
||||
'ends' => '',
|
||||
'descr' => $slease['descr'],
|
||||
'iaid' => '',
|
||||
'duid' => $slease['duid'],
|
||||
'if_descr' => '',
|
||||
'if' => $slease['interface'],
|
||||
'state' => 'active',
|
||||
'status' => in_array(strtolower($slease['ipaddrv6']), $online) ? 'online' : 'offline'
|
||||
];
|
||||
$statics[] = $static;
|
||||
}
|
||||
|
||||
$leases = array_merge($leases, $statics);
|
||||
|
||||
$mac_man = json_decode($backend->configdRun('interface list macdb json'), true);
|
||||
$interfaces = [];
|
||||
|
||||
/* fetch interfaces ranges so we can match leases to interfaces */
|
||||
$if_ranges = [];
|
||||
foreach ($config->dhcpdv6->children() as $dhcpif => $dhcpifconf) {
|
||||
$if = $config->interfaces->$dhcpif;
|
||||
if (!empty((string)$if->ipaddrv6) && !empty((string)$if->subnetv6)) {
|
||||
$if_ranges[$dhcpif] = (string)$if->ipaddrv6.'/'.(string)$if->subnetv6;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($leases as $idx => $lease) {
|
||||
$leases[$idx]['man'] = '';
|
||||
$leases[$idx]['mac'] = '';
|
||||
/* We infer the MAC from NDP data if available, otherwise we extract it out
|
||||
* of the DUID. However, RFC8415 section 11 states that an attempt to parse
|
||||
* a DUID to obtain a client's link-layer addresss is unreliable, as there is no
|
||||
* guarantee that the client is still using the same link-layer address as when
|
||||
* it generated its DUID. Therefore, if we can link it to a manufacturer, chances
|
||||
* are fairly high that this is a valid MAC address, otherwise we omit the MAC
|
||||
* address.
|
||||
*/
|
||||
if (!empty(['address'])) {
|
||||
foreach ($ndp_data as $ndp) {
|
||||
if ($ndp['ip'] == $lease['address']) {
|
||||
$leases[$idx]['mac'] = $ndp['mac'];
|
||||
$leases[$idx]['man'] = empty($ndp['manufacturer']) ? '' : $ndp['manufacturer'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($lease['duid'])) {
|
||||
$mac = '';
|
||||
$duid_type = substr($lease['duid'], 0, 5);
|
||||
if ($duid_type === "00:01" || $duid_type === "00:03") {
|
||||
/* DUID generated based on LL addr with or without timestamp */
|
||||
$hw_type = substr($lease['duid'], 6, 5);
|
||||
if ($hw_type == "00:01") { /* HW type ethernet */
|
||||
$mac = substr($lease['duid'], -17, 17);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($mac)) {
|
||||
$mac_hi = strtoupper(substr(str_replace(':', '', $mac), 0, 6));
|
||||
if (array_key_exists($mac_hi, $mac_man)) {
|
||||
$leases[$idx]['mac'] = $mac;
|
||||
$leases[$idx]['man'] = $mac_man[$mac_hi];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* include interface */
|
||||
$intf = '';
|
||||
$intf_descr = '';
|
||||
if (!empty($lease['if'])) {
|
||||
$if = $config->interfaces->{$lease['if']};
|
||||
if (!empty((string)$if->ipaddrv6) && Util::isIpAddress((string)$if->ipaddrv6)) {
|
||||
$intf = $lease['if'];
|
||||
$intf_descr = (string)$if->descr;
|
||||
}
|
||||
} else {
|
||||
foreach ($if_ranges as $if_name => $if_range) {
|
||||
if (Util::isIPInCIDR($lease['address'], $if_range)) {
|
||||
$intf = $if_name;
|
||||
$intf_descr = $config->interfaces->$if_name->descr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$leases[$idx]['if'] = $intf;
|
||||
$leases[$idx]['if_descr'] = $intf_descr;
|
||||
|
||||
if (!empty($if_name) && !array_key_exists($if_name, $interfaces)) {
|
||||
$interfaces[$intf] = $intf_descr;
|
||||
}
|
||||
}
|
||||
|
||||
$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;
|
||||
}, SORT_REGULAR);
|
||||
|
||||
$response['interfaces'] = $interfaces;
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function searchPrefixAction()
|
||||
{
|
||||
$this->sessionClose();
|
||||
$backend = new Backend();
|
||||
$prefixes = [];
|
||||
|
||||
$raw_leases = json_decode($backend->configdpRun('dhcpd list leases6 1'), true);
|
||||
foreach ($raw_leases as $raw_lease) {
|
||||
if ($raw_lease['lease_type'] === 'ia-pd' && array_key_exists('prefixes', $raw_lease)) {
|
||||
$prefix = [];
|
||||
$prefix['lease_type'] = $raw_lease['lease_type'];
|
||||
$prefix['iaid'] = $raw_lease['iaid'];
|
||||
$prefix['duid'] = $raw_lease['duid'];
|
||||
if (array_key_exists('cltt', $raw_lease)) {
|
||||
$prefix['cltt'] = date('Y/m/d H:i:s', $raw_lease['cltt']);
|
||||
}
|
||||
|
||||
$prefix_raw = $raw_lease['prefixes'][0];
|
||||
$prefix['prefix'] = $prefix_raw['iaprefix'];
|
||||
if (array_key_exists('ends', $prefix_raw)) {
|
||||
$prefix['ends'] = date('Y/m/d H:i:s', $prefix_raw['ends']);
|
||||
}
|
||||
$prefix['state'] = $prefix_raw['binding'] == 'free' ? 'expired' : $prefix_raw['binding'];
|
||||
$prefixes[] = $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->searchRecordsetBase($prefixes, null, 'prefix', null, SORT_REGULAR);
|
||||
}
|
||||
|
||||
public function delLeaseAction($ip)
|
||||
{
|
||||
$result = ["result" => "failed"];
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
$response = json_decode((new Backend())->configdpRun("dhcpd remove lease6", [$ip]), true);
|
||||
if ($response["removed_leases"] != "0") {
|
||||
$result["result"] = "deleted";
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace OPNsense\DHCPv6\Api;
|
||||
|
||||
use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
|
||||
class ServiceController extends ApiControllerBase
|
||||
{
|
||||
/**
|
||||
* XXX most of this logic can be replaced when appropriate start/stop/restart/status
|
||||
* hooks are provided to fit into an ApiMutableServiceControllerBase class. dhcpd being
|
||||
* 'enabled' isn't as straight-forward however with current legacy config format.
|
||||
*/
|
||||
public function statusAction()
|
||||
{
|
||||
$response = trim((new Backend())->configdRun('service status dhcpd6'));
|
||||
|
||||
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 dhcpd6'));
|
||||
return ['status' => $response];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function stopAction()
|
||||
{
|
||||
$result = ['status' => 'failed'];
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
$response = trim((new Backend())->configdRun('service stop dhcpd6'));
|
||||
return ['status' => $response];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function restartAction()
|
||||
{
|
||||
$result = ['status' => 'failed'];
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
$response = trim((new Backend())->configdRun('service restart dhcpd6'));
|
||||
return ['status' => $response];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace OPNsense\DHCPv6;
|
||||
|
||||
use OPNsense\Base\IndexController;
|
||||
|
||||
class LeasesController extends IndexController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->pick('OPNsense/DHCPv6/leases');
|
||||
}
|
||||
}
|
||||
@ -507,7 +507,8 @@
|
||||
<page-status-dhcpv6leases>
|
||||
<name>Status: DHCPv6 leases</name>
|
||||
<patterns>
|
||||
<pattern>status_dhcpv6_leases.php*</pattern>
|
||||
<pattern>ui/dhcpv6/leases</pattern>
|
||||
<pattern>api/dhcpv6/leases/*</pattern>
|
||||
</patterns>
|
||||
</page-status-dhcpv6leases>
|
||||
<page-status-interfaces>
|
||||
|
||||
@ -191,9 +191,7 @@
|
||||
</DHCPv4>
|
||||
<DHCPv6 cssClass="fa fa-bullseye fa-fw">
|
||||
<Relay order="300" url="/services_dhcpv6_relay.php"/>
|
||||
<Leases order="400" url="/status_dhcpv6_leases.php">
|
||||
<Details url="/status_dhcpv6_leases.php?*" visibility="hidden"/>
|
||||
</Leases>
|
||||
<Leases order="400" url="/ui/dhcpv6/leases"/>
|
||||
</DHCPv6>
|
||||
<OpenDNS VisibleName="OpenDNS" url="/services_opendns.php" cssClass="fa fa-tags fa-fw"/>
|
||||
<RouterAdv VisibleName="Router Advertisements" cssClass="fa fa-bullseye fa-fw" />
|
||||
|
||||
207
src/opnsense/mvc/app/views/OPNsense/DHCPv6/leases.volt
Normal file
207
src/opnsense/mvc/app/views/OPNsense/DHCPv6/leases.volt
Normal file
@ -0,0 +1,207 @@
|
||||
{#
|
||||
# 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.
|
||||
#}
|
||||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
let show_inactive = false;
|
||||
let selected_interfaces = [];
|
||||
|
||||
if (window.localStorage) {
|
||||
if (window.localStorage.getItem("api.dhcp.leases6.inactive") !== null) {
|
||||
show_inactive = window.localStorage.getItem("api.dhcp.leases6.inactive") == 'true'
|
||||
$("#show-inactive").prop('checked', show_inactive);
|
||||
}
|
||||
}
|
||||
|
||||
$("#show-inactive").change(function() {
|
||||
show_inactive = this.checked;
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem("api.dhcp.leases6.inactive", show_inactive);
|
||||
}
|
||||
|
||||
$("#grid-leases").bootgrid('reload');
|
||||
});
|
||||
|
||||
$("#interface-selection").on("changed.bs.select", function (e) {
|
||||
console.log($(this).val());
|
||||
selected_interfaces = $(this).val();
|
||||
$("#grid-leases").bootgrid('reload');
|
||||
})
|
||||
|
||||
$("#grid-leases").UIBootgrid({
|
||||
search:'/api/dhcpv6/leases/searchLease/',
|
||||
del:'/api/dhcpv6/leases/delLease/',
|
||||
options: {
|
||||
selection: false,
|
||||
multiSelect: false,
|
||||
useRequestHandlerOnGet: true,
|
||||
requestHandler: function(request) {
|
||||
request['inactive'] = show_inactive;
|
||||
request['selected_interfaces'] = selected_interfaces;
|
||||
return request;
|
||||
},
|
||||
responseHandler: function (response) {
|
||||
if (response.hasOwnProperty('interfaces')) {
|
||||
for ([intf, descr] of Object.entries(response['interfaces'])) {
|
||||
let exists = false;
|
||||
$('#interface-selection option').each(function() {
|
||||
if (this.value == intf) {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!exists) {
|
||||
$("#interface-selection").append($('<option>', {
|
||||
value: intf,
|
||||
text: descr
|
||||
}));
|
||||
}
|
||||
}
|
||||
$("#interface-selection").selectpicker('refresh');
|
||||
}
|
||||
return response;
|
||||
},
|
||||
formatters: {
|
||||
"macformatter": function (column, row) {
|
||||
if (row.man != '') {
|
||||
return row.mac + '<br/>' + '<small><i>' + row.man + '</i></small>';
|
||||
}
|
||||
return row.mac;
|
||||
},
|
||||
"statusformatter": function (column, row) {
|
||||
let connected = row.status == 'offline' ? 'text-danger' : 'text-success';
|
||||
return '<i class="fa fa-plug ' + connected +'" title="' + row.status + '" data-toggle="tooltip"></i>'
|
||||
},
|
||||
"commands": function (column, row) {
|
||||
/* we override the default commands data formatter in order to utilize
|
||||
* two different types of data keys for two different actions. The mapping
|
||||
* action needs a DUID, while the delete action requires an IPv6 address.
|
||||
*/
|
||||
if (row.type == 'static') {
|
||||
return '';
|
||||
}
|
||||
|
||||
let static_map = '';
|
||||
|
||||
if (row.if != '') {
|
||||
static_map = '<a class="btn btn-default btn-xs" href="/services_dhcpv6_edit.php?if=' + row.if +
|
||||
'&duid=' + row.duid + '">' +
|
||||
'<i class="fa fa-plus fa-fw act-map" data-value="' + row.duid + '" data-toggle="tooltip" ' +
|
||||
'title="{{lang._("Add a static mapping for this MAC address")}}"></i>' +
|
||||
'</a>';
|
||||
}
|
||||
|
||||
/* The delete action can be hooked up to the default bootgrid behaviour */
|
||||
let deleteip = '<button type="button" class="btn btn-xs btn-default bootgrid-tooltip command-delete"' +
|
||||
'data-row-id="' + row.address + '" data-action="deleteSelected">' +
|
||||
'<i class="fa fa-trash fa-fw"></i>' +
|
||||
'</a>';
|
||||
|
||||
return static_map + ' ' + deleteip;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#grid-prefixes").UIBootgrid({
|
||||
search:'/api/dhcpv6/leases/searchPrefix/'
|
||||
});
|
||||
|
||||
$("#inactive-selection-wrapper").detach().prependTo('#grid-leases-header > .row > .actionBar');
|
||||
$("#interface-selection-wrapper").detach().prependTo('#grid-leases-header > .row > .actionBar > .actions');
|
||||
|
||||
if (window.location.hash != "") {
|
||||
$('a[href="' + window.location.hash + '"]').click();
|
||||
} else {
|
||||
$('a[href="#leases"]').click();
|
||||
}
|
||||
|
||||
updateServiceControlUI('dhcpv6');
|
||||
});
|
||||
</script>
|
||||
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li><a data-toggle="tab" href="#leases" id="leases_tab">{{ lang._('Leases') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#prefixes" id="prefixes_tab">{{ lang._('Delegated Prefixes') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content content-box col-xs-12 __mb">
|
||||
<div id="leases" class="tab-pane fade in active">
|
||||
<div id="inactive-selection-wrapper" style="float: left;">
|
||||
<label>
|
||||
<input id="show-inactive" type="checkbox"/>
|
||||
{{ lang._('Show inactive') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group" id="interface-selection-wrapper">
|
||||
<select class="selectpicker" multiple="multiple" data-live-search="true" id="interface-selection" data-width="auto" title="All Interfaces">
|
||||
</select>
|
||||
</div>
|
||||
<table id="grid-leases" class="table table-condensed table-hover table-striped table-responsive">
|
||||
<tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="if_descr" data-type="string">{{ lang._('Interface') }}</th>
|
||||
<th data-column-id="address" data-identifier="true" data-type="string">{{ lang._('IP Address') }}</th>
|
||||
<th data-column-id="iaid" data-type="number">{{ lang._('IAID') }}</th>
|
||||
<th data-column-id="duid" data-type="string" data-width="15em">{{ lang._('DUID') }}</th>
|
||||
<th data-column-id="mac" data-type="string" data-formatter="macformatter">{{ lang._('MAC Address') }}</th>
|
||||
<th data-column-id="descr" data-type="string">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="cltt" data-type="string">{{ lang._('Last Transaction Time') }}</th>
|
||||
<th data-column-id="ends" data-type="string">{{ lang._('End') }}</th>
|
||||
<th data-column-id="status" data-type="string" data-formatter="statusformatter">{{ lang._('Status') }}</th>
|
||||
<th data-column-id="state" data-type="string">{{ lang._('State') }}</th>
|
||||
<th data-column-id="type" data-type="string">{{ lang._('Lease Type') }}</th>
|
||||
<th data-column-id="commands" data-formatter="commands", data-sortable="false"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
</tfoot>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="prefixes" class="tab-pane fade in">
|
||||
<table id="grid-prefixes" class="table table-condensed table-hover table-striped table-responsive">
|
||||
<tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="prefix" data-type="string">{{ lang._('IPv6 Prefix') }}</th>
|
||||
<th data-column-id="iaid" data-type="number" data-width="5em">{{ lang._('IAID') }}</th>
|
||||
<th data-column-id="duid" data-type="string">{{ lang._('DUID') }}</th>
|
||||
<th data-column-id="cltt" data-type="string">{{ lang._('Last Transaction Time') }}</th>
|
||||
<th data-column-id="ends" data-type="string">{{ lang._('End') }}</th>
|
||||
<th data-column-id="state" data-type="string">{{ lang._('State') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
</tfoot>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
108
src/opnsense/scripts/dhcp/cleanup_leases6.php
Executable file
108
src/opnsense/scripts/dhcp/cleanup_leases6.php
Executable file
@ -0,0 +1,108 @@
|
||||
#!/usr/local/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require_once("config.inc");
|
||||
require_once("util.inc");
|
||||
require_once("interfaces.inc");
|
||||
require_once("plugins.inc.d/dhcpd.inc");
|
||||
|
||||
$dhcp_lease_file = "/var/dhcpd/var/db/dhcpd6.leases";
|
||||
$opts = getopt('d::f::hs', []);
|
||||
|
||||
if (isset($opts['h']) || empty($opts)) {
|
||||
echo "Usage: cleanup_leases6.php [-h]\n\n";
|
||||
echo "\t-h show this help text and exit\n";
|
||||
echo "\t-s restart service (required when service is active)\n";
|
||||
echo "\t-d=xxx remove ipv6 address\n";
|
||||
echo "\t-f=dhcpd6.leases file (default = /var/dhcpd/var/db/dhcpd6.leases)\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!empty($opts['f'])) {
|
||||
$dhcp_lease_file = $opts['f'];
|
||||
}
|
||||
|
||||
if (!empty($opts['d'])) {
|
||||
$ip_to_remove = $opts['d'];
|
||||
}
|
||||
|
||||
if (isset($opts['s'])) {
|
||||
killbypid('/var/dhcpd/var/run/dhcpdv6.pid');
|
||||
} elseif (isvalidpid('/var/dhcpd/var/run/dhcpdv6.pid')) {
|
||||
echo "dhcpdv6 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)) {
|
||||
$iaaddr = "";
|
||||
$content_to_flush = array();
|
||||
while (($line = fgets($fin, 4096)) !== false) {
|
||||
$fields = explode(' ', trim($line));
|
||||
if ($fields[0] == 'iaaddr') {
|
||||
// lease segment, record ip
|
||||
$iaaddr = trim($fields[1]);
|
||||
$content_to_flush[] = $line;
|
||||
} elseif ($fields[0] == 'ia-na' || count($content_to_flush) > 0) {
|
||||
$content_to_flush[] = $line;
|
||||
} else {
|
||||
// output data directly if we're not in a "ia-na" section
|
||||
fputs($fout, $line);
|
||||
}
|
||||
|
||||
if ($line == "}\n") {
|
||||
if ($iaaddr != $ip_to_remove) {
|
||||
// write ia-na section
|
||||
foreach ($content_to_flush as $cached_line) {
|
||||
fputs($fout, $cached_line);
|
||||
}
|
||||
} else {
|
||||
$removed_leases++;
|
||||
// skip empty line
|
||||
fgets($fin, 4096);
|
||||
}
|
||||
// end of segment
|
||||
$content_to_flush = array();
|
||||
$iaaddr = "";
|
||||
}
|
||||
}
|
||||
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_dhcp6_configure();
|
||||
}
|
||||
|
||||
echo json_encode(["removed_leases" => $removed_leases]);
|
||||
179
src/opnsense/scripts/dhcp/get_leases6.py
Executable file
179
src/opnsense/scripts/dhcp/get_leases6.py
Executable file
@ -0,0 +1,179 @@
|
||||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
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.
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
list dhcpv6 leases
|
||||
"""
|
||||
import ujson
|
||||
import calendar
|
||||
import datetime
|
||||
import time
|
||||
import argparse
|
||||
|
||||
def parse_date(ymd, hms):
|
||||
dt = '%s %s' % (ymd, hms)
|
||||
try:
|
||||
return calendar.timegm(datetime.datetime.strptime(dt, "%Y/%m/%d %H:%M:%S;").timetuple())
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def parse_iaaddr_iaprefix(input):
|
||||
"""
|
||||
parse either an iaaddr or iaprefix segment. return a tuple
|
||||
containing the type parsed and the corresponding segment
|
||||
"""
|
||||
seg_type = input[0].split()[0]
|
||||
segment = dict()
|
||||
segment[seg_type] = input[0].split()[1]
|
||||
for line in input[1:]:
|
||||
parts = line.split()
|
||||
field_name = parts[0] if len(parts) > 0 else ''
|
||||
field_value = None
|
||||
if field_name == 'binding':
|
||||
segment[field_name] = parts[2].strip(';')
|
||||
elif field_name in ('preferred-life', 'max-life'):
|
||||
field_value = parts[1].strip(';')
|
||||
elif field_name == 'ends':
|
||||
field_value = parse_date(parts[2], parts[3])
|
||||
|
||||
if field_value is not None:
|
||||
segment[field_name] = field_value
|
||||
|
||||
return (seg_type, segment)
|
||||
|
||||
def parse_iaid_duid(input):
|
||||
"""
|
||||
parse the combined IAID_DUID value. This is provided in the form
|
||||
of ascii characters. Non-printable characters are provided as octal escapes.
|
||||
We return the hex representation of the raw IAID_DUID value, the IAID integer,
|
||||
as well as the separated DUID value in a dict. The IAID_DUID value is
|
||||
used to uniquely identify a lease, so this value should be used to determine the last
|
||||
relevant entry in the leases file.
|
||||
"""
|
||||
input = input[1:-1] # strip double quotes
|
||||
parsed = []
|
||||
i = 0
|
||||
while i < len(input):
|
||||
c = input[i]
|
||||
if c == '\\':
|
||||
next_c = input[i + 1]
|
||||
if next_c == '\\' or next == '"':
|
||||
parsed.append("%02x" % ord(next_c))
|
||||
i += 1
|
||||
elif next_c.isnumeric():
|
||||
octal_to_decimal = int(input[i+1:i+4], 8)
|
||||
parsed.append("%02x" % octal_to_decimal)
|
||||
i += 3
|
||||
else:
|
||||
parsed.append("%02x" % ord(c))
|
||||
i += 1
|
||||
|
||||
return {
|
||||
'iaid': int(''.join(reversed(parsed[0:4]))),
|
||||
'duid': ":".join([str(a) for a in parsed[4:]]),
|
||||
'iaid_duid': ":".join([str(a) for a in parsed])
|
||||
}
|
||||
|
||||
def parse_lease(lines):
|
||||
"""
|
||||
Parse a DHCPv6 lease. We return a two-tuple containing the combined iaid_duid
|
||||
and the lease. a single lease may contain multiple addresses/prefixes.
|
||||
"""
|
||||
lease = dict()
|
||||
cur_segment = []
|
||||
addresses = []
|
||||
prefixes = []
|
||||
iaid_duid = parse_iaid_duid(lines[0].split()[1])
|
||||
lease['lease_type'] = lines[0].split()[0]
|
||||
lease.update(iaid_duid)
|
||||
|
||||
for line in lines:
|
||||
parts = line.split()
|
||||
|
||||
if parts[0] == 'cltt' and len(parts) >= 3:
|
||||
cltt = parse_date(parts[2], parts[3])
|
||||
lease['cltt'] = cltt
|
||||
|
||||
if parts[0] == 'iaaddr' or parts[0] == 'iaprefix':
|
||||
cur_segment.append(line)
|
||||
elif len(line) > 3 and line[2] == '}' and len(cur_segment) > 0:
|
||||
cur_segment.append(line)
|
||||
(segment_type, segment) = parse_iaaddr_iaprefix(cur_segment)
|
||||
if segment_type == 'iaaddr':
|
||||
addresses.append(segment)
|
||||
if segment_type == 'iaprefix':
|
||||
prefixes.append(segment)
|
||||
cur_segment = []
|
||||
elif len(cur_segment) > 0:
|
||||
cur_segment.append(line)
|
||||
|
||||
# ia_ta/ia_na (addresses) and ia_pd (prefixes) are mutually exclusive.
|
||||
if addresses:
|
||||
lease['addresses'] = addresses
|
||||
if prefixes:
|
||||
lease['prefixes'] = prefixes
|
||||
|
||||
return (iaid_duid['iaid_duid'], lease)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--inactive', help='include inactive leases', default='0', type=str)
|
||||
args = parser.parse_args()
|
||||
leasefile = '/var/dhcpd/var/db/dhcpd6.leases'
|
||||
result = []
|
||||
cur_lease = []
|
||||
last_leases = dict()
|
||||
|
||||
try:
|
||||
with open(leasefile, 'r') as leasef:
|
||||
for line in leasef:
|
||||
if len(line) > 5 and (line[0:5] == 'ia-ta' or line[0:5] == 'ia-na' or line[0:5] == 'ia-pd'):
|
||||
cur_lease.append(line)
|
||||
elif len(line) > 1 and line[0] == '}' and len(cur_lease) > 0:
|
||||
cur_lease.append(line)
|
||||
parsed_lease = parse_lease(cur_lease)
|
||||
last_leases[parsed_lease[0]] = parsed_lease[1]
|
||||
cur_lease = []
|
||||
elif len(cur_lease) > 0:
|
||||
cur_lease.append(line)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
for lease in last_leases.values():
|
||||
if args.inactive == '1':
|
||||
result.append(lease)
|
||||
else:
|
||||
for key in ('addresses', 'prefixes'):
|
||||
if key in lease:
|
||||
for i in range(len(lease[key])):
|
||||
segment = lease[key][i]
|
||||
if not ('ends' in segment and segment['ends'] is not None and segment['ends'] > time.time()):
|
||||
del lease[key][i]
|
||||
if key in lease and lease[key]:
|
||||
result.append(lease)
|
||||
|
||||
print(ujson.dumps(result))
|
||||
@ -4,9 +4,15 @@ parameters:/inactive %s
|
||||
type:script_output
|
||||
message:list dhcp leases %s
|
||||
|
||||
[list.leases6]
|
||||
command:/usr/local/opnsense/scripts/dhcp/get_leases6.py
|
||||
parameters:--inactive %s
|
||||
type:script_output
|
||||
message:list dhcpv6 leases %s
|
||||
|
||||
[list.static]
|
||||
command:/usr/local/sbin/pluginctl -r static_mapping
|
||||
parameters:%s
|
||||
parameters:%s %s
|
||||
type:script_output
|
||||
message: list dhcp static mappings %s
|
||||
|
||||
@ -28,3 +34,9 @@ command:/usr/local/opnsense/scripts/dhcp/cleanup_leases4.php
|
||||
parameters:-d=%s -s
|
||||
type:script_output
|
||||
message:remove lease for %s
|
||||
|
||||
[remove.lease6]
|
||||
command:/usr/local/opnsense/scripts/dhcp/cleanup_leases6.php
|
||||
parameters:-d=%s -s
|
||||
type:script_output
|
||||
message:remove lease6 for %s
|
||||
|
||||
@ -1,574 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014-2021 Deciso B.V.
|
||||
* Copyright (C) 2004-2009 Scott Ullrich <sullrich@gmail.com>
|
||||
* Copyright (C) 2011 Seth Mos <seth.mos@dds.nl>
|
||||
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
|
||||
* 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("interfaces.inc");
|
||||
require_once("plugins.inc.d/dhcpd.inc");
|
||||
|
||||
function adjust_utc($dt)
|
||||
{
|
||||
foreach (config_read_array('dhcpdv6') as $dhcpdv6) {
|
||||
if (!empty($dhcpdv6['dhcpv6leaseinlocaltime'])) {
|
||||
/* 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));
|
||||
$order = 'ip';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$leases_content = dhcpd_leases(6);
|
||||
$leases_count = count($leases_content);
|
||||
|
||||
exec("/usr/sbin/ndp -an", $rawdata);
|
||||
$ndpdata = array();
|
||||
foreach ($rawdata as $line) {
|
||||
$elements = preg_split('/\s+/ ',$line);
|
||||
if ($elements[1] != "(incomplete)") {
|
||||
$ndpent = array();
|
||||
$ip = trim(str_replace(array('(',')'),'',$elements[0]));
|
||||
$ndpent['mac'] = trim($elements[1]);
|
||||
$ndpent['interface'] = trim($elements[2]);
|
||||
$ndpdata[$ip] = $ndpent;
|
||||
}
|
||||
}
|
||||
|
||||
$pools = [];
|
||||
$leases = [];
|
||||
$prefixes = [];
|
||||
$mappings = [];
|
||||
$i = 0;
|
||||
$l = 0;
|
||||
$p = 0;
|
||||
|
||||
while($i < $leases_count) {
|
||||
$is_prefix = false;
|
||||
$duid_split = [];
|
||||
$entry = [];
|
||||
|
||||
preg_match('/ia-.. "(.*)" { (.*)/ ', $leases_content[$i], $duid_split);
|
||||
if (!empty($duid_split[1])) {
|
||||
$iaid_duid = dhcpd_parse_duid($duid_split[1]);
|
||||
$entry['iaid'] = hexdec(implode('', array_reverse($iaid_duid[0])));
|
||||
$entry['duid'] = implode(':', $iaid_duid[1]);
|
||||
$data = explode(' ', $duid_split[2]);
|
||||
} else {
|
||||
$data = explode(' ', $leases_content[$i]);
|
||||
}
|
||||
|
||||
$f = 0;
|
||||
$fcount = count($data);
|
||||
|
||||
/* with less than 12 fields there is nothing useful */
|
||||
if ($fcount < 12) {
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
while($f < $fcount) {
|
||||
switch($data[$f]) {
|
||||
case "failover":
|
||||
$pools[$p]['name'] = $data[$f+2];
|
||||
$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 "ia-pd":
|
||||
$is_prefix = true;
|
||||
/* FALLTHROUGH */
|
||||
case "ia-na":
|
||||
if ($data[$f+1][0] == '"') {
|
||||
$duid = "";
|
||||
/* FIXME: This needs a safety belt to prevent an infinite loop */
|
||||
while ($data[$f][strlen($data[$f])-1] != '"') {
|
||||
$duid .= " " . $data[$f+1];
|
||||
$f++;
|
||||
}
|
||||
$entry['duid'] = $duid;
|
||||
} else {
|
||||
$entry['duid'] = $data[$f+1];
|
||||
}
|
||||
$entry['type'] = "dynamic";
|
||||
$f = $f+2;
|
||||
break;
|
||||
case "iaaddr":
|
||||
$entry['ip'] = $data[$f+1];
|
||||
$entry['type'] = "dynamic";
|
||||
if (in_array($entry['ip'], array_keys($ndpdata))) {
|
||||
$entry['online'] = 'online';
|
||||
} else {
|
||||
$entry['online'] = 'offline';
|
||||
}
|
||||
$f = $f+2;
|
||||
break;
|
||||
case "iaprefix":
|
||||
$is_prefix = true;
|
||||
$entry['prefix'] = $data[$f+1];
|
||||
$entry['type'] = "dynamic";
|
||||
$f = $f+2;
|
||||
break;
|
||||
case "starts":
|
||||
$entry['start'] = $data[$f+2];
|
||||
$entry['start'] .= " " . $data[$f+3];
|
||||
$f = $f+3;
|
||||
break;
|
||||
case "ends":
|
||||
$entry['end'] = $data[$f+2];
|
||||
$entry['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":
|
||||
$entry['start'] = $data[$f+2];
|
||||
$entry['start'] .= " " . $data[$f+3];
|
||||
$f = $f+3;
|
||||
break;
|
||||
case "binding":
|
||||
switch($data[$f+2]) {
|
||||
case "active":
|
||||
$entry['act'] = "active";
|
||||
break;
|
||||
case "free":
|
||||
$entry['act'] = "expired";
|
||||
$entry['online'] = "offline";
|
||||
break;
|
||||
case "backup":
|
||||
$entry['act'] = "reserved";
|
||||
$entry['online'] = "offline";
|
||||
break;
|
||||
case "released":
|
||||
$entry['act'] = "released";
|
||||
$entry['online'] = "offline";
|
||||
}
|
||||
$f = $f+1;
|
||||
break;
|
||||
case "next":
|
||||
/* skip the next binding statement */
|
||||
$f = $f+3;
|
||||
break;
|
||||
case "hardware":
|
||||
$f = $f+2;
|
||||
break;
|
||||
case "client-hostname":
|
||||
if ($data[$f+1] != '') {
|
||||
$entry['hostname'] = preg_replace('/"/','',$data[$f+1]);
|
||||
} else {
|
||||
$hostname = gethostbyaddr($entry['ip']);
|
||||
if ($hostname != '') {
|
||||
$entry['hostname'] = $hostname;
|
||||
}
|
||||
}
|
||||
$f = $f+1;
|
||||
break;
|
||||
case "uid":
|
||||
$f = $f+1;
|
||||
break;
|
||||
}
|
||||
$f++;
|
||||
}
|
||||
if ($is_prefix) {
|
||||
$prefixes[] = $entry;
|
||||
} else {
|
||||
$leases[] = $entry;
|
||||
$mappings[$entry['iaid'] . $entry['duid']] = $entry['ip'];
|
||||
}
|
||||
$l++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
if (count($leases) > 0) {
|
||||
$leases = remove_duplicate($leases,"ip");
|
||||
}
|
||||
|
||||
if (count($prefixes) > 0) {
|
||||
$prefixes = remove_duplicate($prefixes,"prefix");
|
||||
}
|
||||
|
||||
if (count($pools) > 0) {
|
||||
$pools = remove_duplicate($pools,"name");
|
||||
asort($pools);
|
||||
}
|
||||
|
||||
$duids = [];
|
||||
foreach ($leases as $i => $this_lease) {
|
||||
if (!empty($this_lease['duid'])) {
|
||||
if (!isset($duids[$this_lease['duid']])) {
|
||||
$duids[$this_lease['duid']] = [];
|
||||
}
|
||||
$duids[$this_lease['duid']][] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (dhcpd_staticmap(6, "not.found", legacy_interfaces_details(), false) as $static) {
|
||||
$slease = [];
|
||||
$slease['ip'] = $static['ipaddrv6'];
|
||||
$slease['if'] = $static['interface'];
|
||||
$slease['type'] = 'static';
|
||||
$slease['duid'] = $static['duid'];
|
||||
$slease['start'] = '';
|
||||
$slease['end'] = '';
|
||||
$slease['hostname'] = $static['hostname'];
|
||||
$slease['descr'] = $static['descr'];
|
||||
$slease['act'] = 'static';
|
||||
$slease['online'] = in_array($slease['ip'], array_keys($ndpdata)) ? 'online' : 'offline';
|
||||
|
||||
if (isset($duids[$slease['duid']])) {
|
||||
/* update lease with static data */
|
||||
foreach ($slease as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
foreach ($duids[$slease['duid']] as $idx) {
|
||||
$leases[$idx][$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$leases[] = $slease;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['order']) && in_array($_GET['order'], ['int', 'ip', 'iaid', 'duid', 'hostname', 'descr', 'start', 'end', 'online', 'act'])) {
|
||||
$order = $_GET['order'];
|
||||
}
|
||||
|
||||
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'])) {
|
||||
killbypid('/var/dhcpd/var/run/dhcpdv6.pid');
|
||||
$leasesfile = '/var/dhcpd/var/db/dhcpd6.leases'; /* XXX needs wrapper */
|
||||
$fin = @fopen($leasesfile, "r");
|
||||
$fout = @fopen($leasesfile.".new", "w");
|
||||
if ($fin) {
|
||||
$ip_to_remove = $_POST['deleteip'];
|
||||
$iaaddr = "";
|
||||
$content_to_flush = array();
|
||||
while (($line = fgets($fin, 4096)) !== false) {
|
||||
$fields = explode(' ', trim($line));
|
||||
if ($fields[0] == 'iaaddr') {
|
||||
// lease segment, record ip
|
||||
$iaaddr = trim($fields[1]);
|
||||
$content_to_flush[] = $line;
|
||||
} elseif ($fields[0] == 'ia-na' || count($content_to_flush) > 0) {
|
||||
$content_to_flush[] = $line;
|
||||
} else {
|
||||
// output data directly if we're not in a "ia-na" section
|
||||
fputs($fout, $line);
|
||||
}
|
||||
|
||||
if ($line == "}\n") {
|
||||
if ($iaaddr != $ip_to_remove) {
|
||||
// write ia-na section
|
||||
foreach ($content_to_flush as $cached_line) {
|
||||
fputs($fout, $cached_line);
|
||||
}
|
||||
} else {
|
||||
// skip empty line
|
||||
fgets($fin, 4096);
|
||||
}
|
||||
// end of segment
|
||||
$content_to_flush = array();
|
||||
$iaaddr = "";
|
||||
}
|
||||
}
|
||||
fclose($fin);
|
||||
fclose($fout);
|
||||
@unlink($leasesfile);
|
||||
@rename($leasesfile.".new", $leasesfile);
|
||||
|
||||
dhcpd_dhcp6_configure();
|
||||
}
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$service_hook = 'dhcpd6';
|
||||
|
||||
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);
|
||||
?>
|
||||
<body>
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
$(".act_delete").click(function(){
|
||||
$.post(window.location, {deleteip: $(this).data('deleteip')}, function(data) {
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
// keep sorting in place.
|
||||
$(".act_sort").click(function(){
|
||||
var all = <?=!empty($_GET['all']) ? 1 : 0;?> ;
|
||||
document.location = document.location.origin + window.location.pathname +"?all="+all+"&order="+$(this).data('field');
|
||||
});
|
||||
$(".act_sort :first-child").css('cursor', 'pointer');
|
||||
});
|
||||
</script>
|
||||
<?php include("fbegin.inc"); ?>
|
||||
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<?php
|
||||
/* only print pool status when we have one */
|
||||
if (count($pools) > 0):?>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable __nomb">
|
||||
<tr>
|
||||
<td><?=gettext("Failover Group"); ?></a></td>
|
||||
<td><?=gettext("My State"); ?></a></td>
|
||||
<td><?=gettext("Since"); ?></a></td>
|
||||
<td><?=gettext("Peer State"); ?></a></td>
|
||||
<td><?=gettext("Since"); ?></a></td>
|
||||
</tr>
|
||||
<?php
|
||||
foreach ($pools as $data):?>
|
||||
<tr>
|
||||
<td><?=$data['name'];?></td>
|
||||
<td><?=$data['mystate'];?></td>
|
||||
<td><?=adjust_utc($data['mydate']);?></td>
|
||||
<td><?=$data['peerstate'];?></td>
|
||||
<td><?=adjust_utc($data['peerdate']);?></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
endforeach;?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
endif;?>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="act_sort" data-field="int"><span><?=gettext("Interface"); ?></span></th>
|
||||
<th class="act_sort" data-field="ip"><span><?=gettext("IPv6 address"); ?></span></th>
|
||||
<th class="act_sort" data-field="iaid"><span><?=gettext("IAID"); ?></span></th>
|
||||
<th class="act_sort" data-field="duid"><span><?=gettext("DUID/MAC"); ?></span></th>
|
||||
<th class="act_sort" data-field="hostname"><span><?=gettext("Hostname"); ?></span></th>
|
||||
<th class="act_sort" data-field="descr"><span><?=gettext("Description"); ?></span></th>
|
||||
<th class="act_sort" data-field="start"><span><?=gettext("Start"); ?></span></th>
|
||||
<th class="act_sort" data-field="end"><span><?=gettext("End"); ?></span></th>
|
||||
<th class="act_sort" data-field="online"><span><?=gettext("Online"); ?></span></th>
|
||||
<th class="act_sort" data-field="act"><span><?=gettext("Lease Type"); ?></span></th>
|
||||
<th class="text-nowrap"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$mac_man = json_decode(configd_run("interface list macdb json"), true);
|
||||
foreach ($leases as $data):
|
||||
if (!($data['act'] == 'active' || $data['act'] == 'static' || $_GET['all'] == 1)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($data['if'])) {
|
||||
$data['if'] = convert_real_interface_to_friendly_interface_name(guess_interface_from_ip($data['ip']));
|
||||
}
|
||||
$data['int'] = htmlspecialchars($interfaces[$data['if']]['descr']);
|
||||
|
||||
$mac_from_ndp = !empty($ndpdata[$data['ip']]) ? $ndpdata[$data['ip']]['mac'] : "";
|
||||
$vendor_from_ndp = empty($mac_from_ndp) ? "" : ($mac_man[strtoupper(implode("", explode(":", substr($mac_from_ndp, 0, 8))))] ?? "");
|
||||
|
||||
$mac_from_duid = "";
|
||||
$duid_formatted = $data['duid'];
|
||||
$duid_type = substr($data['duid'], 0, 5);
|
||||
if ($duid_type === "00:01" || $duid_type === "00:03"){
|
||||
$duid_subtype = substr($data['duid'], 6, 5);
|
||||
if ($duid_subtype === "00:01") {
|
||||
$mac_from_duid = substr($data['duid'], -17, 17);
|
||||
$duid_formatted = substr($data['duid'], 0, strlen($data['duid']) - 17) . '<u>' . $mac_from_duid . '</u>';
|
||||
}
|
||||
}
|
||||
$vendor_from_duid = empty($mac_from_duid) ? "" : ($mac_man[strtoupper(implode("", explode(":", substr($mac_from_duid, 0, 8))))] ?? "");
|
||||
|
||||
$duid_content = $duid_formatted;
|
||||
if (!empty($vendor_from_duid)) {
|
||||
$duid_content .= '<br/><small><i>'.$vendor_from_duid.'</i></small>';
|
||||
}
|
||||
if (!empty($mac_from_ndp) && $mac_from_duid !== $mac_from_ndp) {
|
||||
$duid_content .= '</br>'.gettext('NDP MAC').': '.$mac_from_ndp;
|
||||
if (!empty($vendor_from_ndp)) {
|
||||
$duid_content .= '<br/><small><i>'.$vendor_from_ndp.'</i></small>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><?=$data['int'];?></td>
|
||||
<td><?=$data['ip'];?></td>
|
||||
<td><?=$data['iaid'];?></td>
|
||||
<td><?=$duid_content;?></td>
|
||||
<td><?= !empty($data['hostname']) ? html_safe($data['hostname']) : '' ?></td>
|
||||
<td><?= html_safe($data['descr'] ?? '');?></td>
|
||||
<td><?= !empty($data['start']) ? adjust_utc($data['start']) : '' ?></td>
|
||||
<td><?= !empty($data['end']) ? adjust_utc($data['end']) : '' ?></td>
|
||||
<td>
|
||||
<i class="fa fa-<?=$data['online']=='online' ? 'signal' : 'ban';?>" title="<?=$data['online'];?>" data-toggle="tooltip"></i>
|
||||
</td>
|
||||
<td><?=$data['act'];?></td>
|
||||
<td class="text-nowrap">
|
||||
<?php if (!empty($config['interfaces'][$data['if']])): ?>
|
||||
<?php if (empty($config['interfaces'][$data['if']]['virtual']) && isset($config['interfaces'][$data['if']]['enable'])): ?>
|
||||
<?php if (is_ipaddrv6($config['interfaces'][$data['if']]['ipaddrv6']) || !empty($config['interfaces'][$data['if']]['dhcpd6track6allowoverride'])): ?>
|
||||
<?php if ($data['type'] == 'dynamic'): ?>
|
||||
<a class="btn btn-default btn-xs" href="services_dhcpv6_edit.php?if=<?=$data['if'];?>&duid=<?=$data['duid'];?>&hostname=<?=$data['hostname'];?>">
|
||||
<i class="fa fa-plus fa-fw"></i>
|
||||
</a>
|
||||
<?php if ($data['online'] != 'online'): ?>
|
||||
<a class="act_delete btn btn-default btn-xs" href="#" data-deleteip="<?=$data['ip'];?>" title="<?= html_safe(gettext('Delete')) ?>" data-toggle="tooltip">
|
||||
<i class="fa fa-trash fa-fw"></i>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<header class="content-box-head container-fluid">
|
||||
<h3><?=gettext("Delegated Prefixes");?></h3>
|
||||
</header>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?=gettext("IPv6 Prefix"); ?></th>
|
||||
<th><?=gettext("IAID"); ?></th>
|
||||
<th><?=gettext("DUID"); ?></th>
|
||||
<th><?=gettext("Start"); ?></th>
|
||||
<th><?=gettext("End"); ?></th>
|
||||
<th><?=gettext("State"); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($prefixes as $data):?>
|
||||
<tr>
|
||||
<td>
|
||||
<?=!empty($mappings[$data['iaid'] . $data['duid']]) ? $mappings[$data['iaid'] . $data['duid']] : "";?>
|
||||
<?=$data['prefix'];?>
|
||||
</td>
|
||||
<td><?=$data['iaid'];?></td>
|
||||
<td><?=$data['duid'];?></td>
|
||||
<td><?= !empty($data['start']) ? adjust_utc($data['start']) : '' ?></td>
|
||||
<td><?= !empty($data['end']) ? adjust_utc($data['end']) : '' ?></td>
|
||||
<td><?=$data['act'];?></td>
|
||||
</tr>
|
||||
<?php
|
||||
endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="col-xs-12">
|
||||
<form method="get">
|
||||
<input type="hidden" name="order" value="<?= html_safe($order) ?>" />
|
||||
<?php if ($_GET['all'] ?? 0): ?>
|
||||
<input type="hidden" name="all" value="0" />
|
||||
<input type="submit" class="btn btn-default" value="<?= html_safe(gettext('Show active and static leases only')) ?>" />
|
||||
<?php else: ?>
|
||||
<input type="hidden" name="all" value="1" />
|
||||
<input type="submit" class="btn btn-default" value="<?= html_safe(gettext('Show all configured leases')) ?>" />
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<?php if ($leases == 0): ?>
|
||||
<p><?= gettext('No leases file found. Is the DHCP server active?') ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
|
||||
include("foot.inc");
|
||||
Loading…
x
Reference in New Issue
Block a user