Project

General

Profile

Bug #9343 ยป diag_arp.php

new and improved? - Anthony Hernandez, 02/21/2019 01:38 AM

 
1
<?php
2
/*
3
 * diag_arp.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2018 Rubicon Communications, LLC (Netgate)
7
 * All rights reserved.
8
 *
9
 * originally based on m0n0wall (http://m0n0.ch/wall)
10
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
11
 * All rights reserved.
12
 *
13
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16
 *
17
 * http://www.apache.org/licenses/LICENSE-2.0
18
 *
19
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24
 */
25

    
26
##|+PRIV
27
##|*IDENT=page-diagnostics-arptable
28
##|*NAME=Diagnostics: ARP Table
29
##|*DESCR=Allow access to the 'Diagnostics: ARP Table' page.
30
##|*MATCH=diag_arp.php*
31
##|-PRIV
32

    
33
@ini_set('zlib.output_compression', 0);
34
@ini_set('implicit_flush', 1);
35

    
36
require_once("guiconfig.inc");
37

    
38
// delete arp entry
39
if (isset($_POST['deleteentry'])) {
40
	$ip = $_POST['deleteentry'];
41
	if (is_ipaddrv4($ip)) {
42
		$ret = mwexec("arp -d " . $_POST['deleteentry'], true);
43
	} else {
44
		$ret = 1;
45
	}
46
	if ($ret) {
47
		$savemsg = sprintf(gettext("%s is not a valid IPv4 address or could not be deleted."), $ip);
48
		$savemsgtype = 'alert-warning';
49
	} else {
50
		$savemsg = sprintf(gettext("The ARP cache entry for %s has been deleted."), $ip);
51
		$savemsgtype = 'success';
52
	}
53
}
54

    
55
function leasecmp($a, $b) {
56
	return strcmp($a[$_REQUEST['order']], $b[$_REQUEST['order']]);
57
}
58

    
59
function adjust_gmt($dt) {
60
	$ts = strtotime($dt . " GMT");
61
	return strftime("%Y/%m/%d %H:%M:%S", $ts);
62
}
63

    
64
function remove_duplicate($array, $field) {
65
	foreach ($array as $sub) {
66
		$cmp[] = $sub[$field];
67
	}
68
	$unique = array_unique($cmp);
69
	foreach ($unique as $k => $rien) {
70
		$new[] = $array[$k];
71
	}
72
	return $new;
73
}
74

    
75
// Define path to AWK
76
$awk = "/usr/bin/awk";
77

    
78
// Read in leases file
79
$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases";
80

    
81
/* this pattern sticks comments into a single array item */
82
$cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'";
83

    
84
/* We then split the leases file by } */
85
$splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'";
86

    
87
/* get arp database into local space */
88
exec("/usr/sbin/arp -na --libxo json",$arp_output);
89
$arp_db = json_decode(implode("",$arp_output),true)["arp"]["arp-cache"];
90

    
91

    
92
/* rebuild dhcpd file into an associated array */
93
$dhcpd_file = file_get_contents($leasesfile);
94

    
95
preg_match_all('/^(?:[\s]+?)?lease[\s]+(?:.|\n)+?}/m',$dhcpd_file,$out);
96
$leases = array();
97

    
98
foreach($out[0] as $entry){
99
	preg_match('/^(?:[\s]+?)?lease[\s]+(.+)[\s]+{/m',$entry,$ip);
100
	preg_match('/^(?:[\s]+?)?hardware[\s]+ethernet[\s]+((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2});/m',$entry,$mac);
101
	preg_match('/^(?:[\s]+?)?client-hostname "((.+))";$/m',$entry,$hostname);
102

    
103
	$lease['ip'] = $ip[1];
104
	$lease['mac'] = $mac[1];
105
	$lease['hostname'] = $hostname[1];
106
	$leases[] = $lease;
107
}
108

    
109
if (count($leases) > 0) {
110
	$leases = remove_duplicate($leases, "ip");
111
}
112

    
113
// Put this in an easy to use form
114
$dhcpmac = array();
115
$dhcpip = array();
116

    
117
foreach ($leases as $value) {
118
	$dhcpmac[$value['mac']] = $value['hostname'];
119
	$dhcpip[$value['ip']] = $value['hostname'];
120
}
121

    
122
$i = 0;
123

    
124
/* if list */
125
$ifdescrs = get_configured_interface_with_descr();
126

    
127
foreach ($ifdescrs as $key => $interface) {
128
	$thisif = convert_friendly_interface_to_real_interface_name($key);
129
	if (!empty($thisif)) {
130
		$hwif[$thisif] = $interface;
131
	}
132
}
133

    
134
$data = array();
135
foreach ($arp_db as $elements) {
136
        $arpent = array();
137
        $arpent['ip'] = $elements['ip-address'];
138
        $arpent['mac'] = $elements['mac-address'];
139
	$arpent['interface'] = $elements['interface'];
140
	if($elements['expires']){
141
		$arpent['status'] = "Expires in ".$elements['expires']." seconds";
142
	} elseif ($elements['permanent']){
143
		$arpent['status'] = "Permanent";
144
	}
145
	
146
        $arpent['linktype'] = $elements['type'];
147
        $data[] = $arpent;
148
}
149

    
150
function _getHostName($mac, $ip) {
151
	global $dhcpmac, $dhcpip;
152

    
153
	if ($dhcpmac[$mac]) {
154
		return $dhcpmac[$mac];
155
	} else if ($dhcpip[$ip]) {
156
		return $dhcpip[$ip];
157
	} else {
158
		exec("host -W 1 " . escapeshellarg($ip), $output);
159
		if (preg_match('/.*pointer ([A-Za-z_0-9.-]+)\..*/', $output[0], $matches)) {
160
			if ($matches[1] <> $ip) {
161
				return $matches[1];
162
			}
163
		}
164
	}
165
	return "";
166
}
167

    
168
$pgtitle = array(gettext("Diagnostics"), gettext("ARP Table"));
169
include("head.inc");
170

    
171
// Handle save msg if defined
172
if ($savemsg) {
173
	print_info_box(htmlentities($savemsg), $savemsgtype);
174
}
175
?>
176

    
177
<!-- On modern hardware the table will load so fast you may never see this! -->
178
<div id="loading">
179
	<?= gettext(" Loading, please wait...")?>
180
</div>
181

    
182
<?php
183

    
184
// Flush buffers out to client so that they see Loading, please wait....
185
for ($i = 0; $i < ob_get_level(); $i++) {
186
	ob_end_flush();
187
}
188

    
189
ob_implicit_flush(1);
190

    
191
// Resolve hostnames and replace Z_ with "".  The intention
192
// is to sort the list by hostnames, alpha and then the non
193
// resolvable addresses will appear last in the list.
194
$dnsavailable=1;
195
$dns = trim(_getHostName("", "8.8.8.8"));
196
if ($dns == "") {
197
	$dns = trim(_getHostName("", "8.8.4.4"));
198
	if ($dns == "") {
199
		$dnsavailable = 0;
200
	}
201
}
202

    
203
foreach ($data as &$entry) {
204
	if ($dnsavailable) {
205
		$dns = trim(_getHostName($entry['mac'], $entry['ip']));
206
	} else {
207
		$dns="";
208
	}
209
	if (trim($dns)) {
210
		$entry['dnsresolve'] = "$dns";
211
	} else {
212
		$entry['dnsresolve'] = "Z_ ";
213
	}
214
}
215
unset($entry);
216

    
217
// Sort the data alpha first
218
$data = msort($data, "dnsresolve");
219

    
220
// Load MAC-Manufacturer table
221
$mac_man = load_mac_manufacturer_table();
222
?>
223
<div class="panel panel-default">
224
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('ARP Table')?></h2></div>
225
	<div class="panel-body">
226

    
227
<div class="table-responsive">
228
	<table class="sortable-theme-bootstrap table table-striped table-hover" data-sortable>
229
		<thead>
230
			<tr>
231
				<th><?= gettext("Interface")?></th>
232
				<th><?= gettext("IP address")?></th>
233
				<th><?= gettext("MAC address")?></th>
234
				<th><?= gettext("Hostname")?></th>
235
				<th><?= gettext("Status")?></th>
236
				<th><?= gettext("Link Type")?></th>
237
				<th data-sortable="false"><?=gettext("Actions")?></th>
238
			</tr>
239
		</thead>
240
		<tbody>
241

    
242
<?php
243
		foreach ($data as $entry): ?>
244
			<tr>
245
				<td><?=$hwif[$entry['interface']]?></td>
246
				<td><?=$entry['ip']?></td>
247
				<td>
248
					<?=trim($entry['mac'])?>
249
				<?php
250
					$mac = trim($entry['mac']);
251
					$mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]);
252

    
253
					if (isset($mac_man[$mac_hi])) {
254
						print '<small>('. $mac_man[$mac_hi] .')</small>';
255
					}
256
	?>
257
				</td>
258
				<td><?=trim(str_replace("Z_ ", "", $entry['dnsresolve']))?></td>
259
				<td><?=ucfirst($entry['status'])?></td>
260
				<td><?=$entry['linktype']?></td>
261
				<td>
262
					<a class="fa fa-trash" title="<?=gettext('Delete arp cache entry')?>"	href="diag_arp.php?deleteentry=<?=$entry['ip']?>" usepost></a>
263
				</td>
264
			</tr>
265
		<?php endforeach?>
266
		</tbody>
267
	</table>
268
</div>
269

    
270
	</div>
271
</div>
272

    
273
<script type="text/javascript">
274
//<![CDATA[
275
// Clear the "loading" div once the page has loaded"
276
events.push(function() {
277
	$('#loading').empty();
278
});
279
//]]>
280
</script>
281

    
282
<div class="infoblock blockopen">
283
<?php
284
print_info_box(sprintf(gettext('Local IPv6 peers use %1$sNDP%2$s instead of ARP.'), '<a href="diag_ndp.php">', '</a>') . '<br />' .
285
   '<br />' . gettext('Permanent ARP entries are shown for local interfaces or static ARP entries.') .
286
   '<br />' . gettext('Normal dynamic ARP entries show a countdown timer until they will expire and then be re-checked.') .
287
   '<br />' . gettext('Incomplete ARP entries indicate that the target host has not yet replied to an ARP request.'), 'info', false);
288
?>
289
</div>
290

    
291
<?php
292
include("foot.inc");
293
?>
    (1-1/1)