.
* 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.
*
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* "This product includes software developed by the pfSense Project
* for use in the pfSense® software distribution. (http://www.pfsense.org/).
*
* 4. The names "pfSense" and "pfSense Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* coreteam@pfsense.org.
*
* 5. Products derived from this software may not be called "pfSense"
* nor may "pfSense" appear in their names without prior written
* permission of the Electric Sheep Fencing, LLC.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
*
* "This product includes software developed by the pfSense Project
* for use in the pfSense software distribution (http://www.pfsense.org/).
*
* THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
* EXPRESSED 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 pfSense PROJECT OR
* ITS CONTRIBUTORS 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.
*/
##|+PRIV
##|*IDENT=page-status-dhcpv6leases
##|*NAME=Status: DHCPv6 leases
##|*DESCR=Allow access to the 'Status: DHCPv6 leases' page.
##|*MATCH=status_dhcpv6_leases.php*
##|-PRIV
require_once("guiconfig.inc");
require_once("config.inc");
$pgtitle = array(gettext("Status"), gettext("DHCPv6 Leases"));
$shortcut_section = "dhcp6";
$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases";
if (($_GET['deleteip']) && (is_ipaddr($_GET['deleteip']))) {
/* Stop DHCPD */
killbyname("dhcpd");
/* Read existing leases */
$leases_contents = explode("\n", file_get_contents($leasesfile));
$newleases_contents = array();
$i = 0;
while ($i < count($leases_contents)) {
/* Find the lease(s) we want to delete */
if ($leases_contents[$i] == " iaaddr {$_GET['deleteip']} {") {
/* The iaaddr line is two lines down from the start of the lease, so remove those two lines. */
array_pop($newleases_contents);
array_pop($newleases_contents);
/* Skip to the end of the lease declaration */
do {
$i++;
} while ($leases_contents[$i] != "}");
} else {
/* It's a line we want to keep, copy it over. */
$newleases_contents[] = $leases_contents[$i];
}
$i++;
}
/* Write out the new leases file */
$fd = fopen($leasesfile, 'w');
fwrite($fd, implode("\n", $newleases_contents));
fclose($fd);
/* Restart DHCP Service */
services_dhcpd_configure();
header("Location: status_dhcpv6_leases.php?all={$_GET['all']}");
}
// Load MAC-Manufacturer table
$mac_man = load_mac_manufacturer_table();
include("head.inc");
function leasecmp($a, $b) {
return strcmp($a[$_GET['order']], $b[$_GET['order']]);
}
function adjust_gmt($dt) {
global $config;
$dhcpv6leaseinlocaltime = "no";
if (is_array($config['dhcpdv6'])) {
$dhcpdv6 = $config['dhcpdv6'];
foreach ($dhcpdv6 as $dhcpdv6params) {
$dhcpv6leaseinlocaltime = $dhcpdv6params['dhcpv6leaseinlocaltime'];
if ($dhcpv6leaseinlocaltime == "yes") {
break;
}
}
}
if ($dhcpv6leaseinlocaltime == "yes") {
$ts = strtotime($dt . " GMT");
if ($ts !== false) {
return strftime("%Y/%m/%d %H:%M:%S", $ts);
}
}
/* If we did not need to convert to local time or the conversion failed, just return the input. */
return $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;
}
function parse_duid($duid_string) {
$parsed_duid = array();
for ($i = 0; $i < strlen($duid_string); $i++) {
$s = substr($duid_string, $i, 1);
if ($s == '\\') {
$n = substr($duid_string, $i+1, 1);
if (($n == '\\') || ($n == '"')) {
$parsed_duid[] = sprintf("%02x", ord($n));
$i += 1;
} else {
$n = substr($duid_string, $i+1, 3);
if (preg_match('/[0-3][0-7]{2}/', $n)) {
$parsed_duid[] = sprintf("%02x", octdec($n));
$i += 3;
}
}
} else {
$parsed_duid[] = sprintf("%02x", ord($s));
}
}
$iaid = array_slice($parsed_duid, 0, 4);
$duid = array_slice($parsed_duid, 4);
return array($iaid, $duid);
}
$awk = "/usr/bin/awk";
$sed = "/usr/bin/sed";
/* Remove all lines except ia-.. blocks */
$cleanpattern = "'/^ia-.. /, /^}/ !d; s,;$,,; s, *, ,g'";
/* | | |
* | | |
* | | -> Remove extra spaces
* | -> Remove ; from EOL
* -> Delete all lines except blocks that start with ia-..
* and end with }
*/
/* Join each block in single line */
$splitpattern = "'{printf $0}; $0 ~ /^\}/ {printf \"\\n\"}'";
/* stuff the leases file in a proper format into a array by line */
exec("{$sed} {$cleanpattern} {$leasesfile} | {$awk} {$splitpattern}", $leases_content);
$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 = array();
$leases = array();
$prefixes = array();
$mappings = array();
$i = 0;
$l = 0;
$p = 0;
// Translate these once so we don't do it over and over in the loops below.
$online_string = gettext("online");
$offline_string = gettext("offline");
$active_string = gettext("active");
$expired_string = gettext("expired");
$reserved_string = gettext("reserved");
$released_string = gettext("released");
$dynamic_string = gettext("dynamic");
$static_string = gettext("static");
// Put everything together again
while ($i < $leases_count) {
$entry = array();
/* split the line by space */
$duid_split = array();
preg_match('/ia-.. "(.*)" { (.*)/ ', $leases_content[$i], $duid_split);
if (!empty($duid_split[1])) {
$iaid_duid = 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]);
}
/* walk the fields */
$f = 0;
$fcount = count($data);
/* with less then 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;
case "ia-na":
$entry['iaid'] = $tmp_iaid;
$entry['duid'] = $tmp_duid;
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_string;
$f = $f+2;
break;
case "iaaddr":
$entry['ip'] = $data[$f+1];
$entry['type'] = $dynamic_string;
if (in_array($entry['ip'], array_keys($ndpdata))) {
$entry['online'] = $online_string;
} else {
$entry['online'] = $offline_string;
}
$f = $f+2;
break;
case "iaprefix":
$is_prefix = true;
$entry['prefix'] = $data[$f+1];
$entry['type'] = $dynamic_string;
$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_string;
break;
case "free":
$entry['act'] = $expired_string;
$entry['online'] = $offline_string;
break;
case "backup":
$entry['act'] = $reserved_string;
$entry['online'] = $offline_string;
break;
case "released":
$entry['act'] = $released_string;
$entry['online'] = $offline_string;
}
$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['duid']] = $entry['ip'];
}
$l++;
$i++;
$is_prefix = false;
}
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);
}
foreach ($config['interfaces'] as $ifname => $ifarr) {
if (is_array($config['dhcpdv6'][$ifname]) &&
is_array($config['dhcpdv6'][$ifname]['staticmap'])) {
foreach ($config['dhcpdv6'][$ifname]['staticmap'] as $static) {
$slease = array();
$slease['ip'] = $static['ipaddrv6'];
$slease['type'] = "static";
$slease['duid'] = $static['duid'];
$slease['start'] = "";
$slease['end'] = "";
$slease['hostname'] = htmlentities($static['hostname']);
$slease['act'] = $static_string;
if (in_array($slease['ip'], array_keys($ndpdata))) {
$slease['online'] = $online_string;
} else {
$slease['online'] = $offline_string;
}
$leases[] = $slease;
}
}
}
if ($_GET['order']) {
usort($leases, "leasecmp");
}
/* only print pool status when we have one */
if (count($pools) > 0) {
?>
=gettext('Pool Status')?>
=gettext("Failover Group")?> |
=gettext("My State")?> |
=gettext("Since")?> |
=gettext("Peer State")?> |
=gettext("Since")?> |
=$data['name']?> |
=$data['mystate']?> |
=adjust_gmt($data['mydate'])?> |
=$data['peerstate']?> |
=adjust_gmt($data['peerdate'])?> |
=gettext('Leases')?>
|
=gettext("IPv6 address")?> |
=gettext("IAID")?> |
=gettext("DUID")?> |
=gettext("MAC address")?> |
=gettext("Hostname")?> |
=gettext("Start")?> |
=gettext("End")?> |
=gettext("Online")?> |
=gettext("Lease Type")?> |
=gettext("Actions")?> |
$dhcpifconf) {
if (is_array($dhcpifconf['staticmap'])) {
foreach ($dhcpifconf['staticmap'] as $staticent) {
if ($data['ip'] == $staticent['ipaddr']) {
$data['if'] = $dhcpif;
break;
}
}
}
/* exit as soon as we have an interface */
if ($data['if'] != "") {
break;
}
}
} else {
$data['if'] = convert_real_interface_to_friendly_interface_name(guess_interface_from_ip($data['ip']));
}
$mac = trim($ndpdata[$data['ip']]['mac']);
$mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]);
?>
|
=$data['ip']?> |
=$data['iaid']?> |
=$data['duid']?> |
=$mac?>
(=$mac_man[$mac_hi]?>)
|
=htmlentities($data['hostname'])?> |
=adjust_gmt($data['start'])?> |
=adjust_gmt($data['end'])?> |
n/a |
n/a |
=$data['online']?> |
=$data['act']?> |
" href="services_dhcpv6_edit.php?if==$data['if']?>&duid==$data['duid']?>&hostname==htmlspecialchars($data['hostname'])?>">
" href="services_wol_edit.php?if==$data['if']?>&mac==$data['mac']?>&descr==htmlentities($data['hostname'])?>">
|
=gettext('Delegated Prefixes')?>
|
=gettext("IPv6 Prefix")?> |
=gettext("IAID")?> |
=gettext("DUID")?> |
=gettext("Start")?> |
=gettext("End")?> |
=gettext("State")?> |
$dhcpifconf) {
if (is_array($dhcpifconf['staticmap'])) {
foreach ($dhcpifconf['staticmap'] as $staticent) {
if ($data['ip'] == $staticent['ipaddr']) {
$data['if'] = $dhcpif;
break;
}
}
}
/* exit as soon as we have an interface */
if ($data['if'] != "") {
break;
}
}
} else {
$data['if'] = convert_real_interface_to_friendly_interface_name(guess_interface_from_ip($data['ip']));
}
{
$dip = "";
}
?>
|
=$data['prefix']?>
=gettext('Routed To')?>: =$mappings[$data['duid']]?>
|
=$data['iaid']?> |
=$data['duid']?> |
=adjust_gmt($data['start'])?> |
=adjust_gmt($data['end'])?> |
n/a |
n/a |
=$data['act']?> |
=gettext("Show active and static leases only")?>
=gettext("Show all configured leases")?>