Project

General

Profile

Download (9.92 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * diag_arp.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 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
/* stuff the leases file in a proper format into an array by line */
88
exec("cat {$leasesfile} | {$awk} {$cleanpattern} | {$awk} {$splitpattern}", $leases_content);
89
$leases_count = count($leases_content);
90

    
91
$pools = array();
92
$leases = array();
93
$i = 0;
94
$l = 0;
95
$p = 0;
96
// Put everything together again
97
while ($i < $leases_count) {
98
	/* split the line by space */
99
	$data = explode(" ", $leases_content[$i]);
100
	/* walk the fields */
101
	$f = 0;
102
	$fcount = count($data);
103
	/* with less then 20 fields there is nothing useful */
104
	if ($fcount < 20) {
105
		$i++;
106
		continue;
107
	}
108
	while ($f < $fcount) {
109
		switch ($data[$f]) {
110
			case "failover":
111
				$pools[$p]['name'] = $data[$f+2];
112
				$pools[$p]['mystate'] = $data[$f+7];
113
				$pools[$p]['peerstate'] = $data[$f+14];
114
				$pools[$p]['mydate'] = $data[$f+10];
115
				$pools[$p]['mydate'] .= " " . $data[$f+11];
116
				$pools[$p]['peerdate'] = $data[$f+17];
117
				$pools[$p]['peerdate'] .= " " . $data[$f+18];
118
				$p++;
119
				$i++;
120
				continue 3;
121
			case "lease":
122
				$leases[$l]['ip'] = $data[$f+1];
123
				$leases[$l]['type'] = "dynamic";
124
				$f = $f+2;
125
				break;
126
			case "starts":
127
				$leases[$l]['start'] = $data[$f+2];
128
				$leases[$l]['start'] .= " " . $data[$f+3];
129
				$f = $f+3;
130
				break;
131
			case "ends":
132
				$leases[$l]['end'] = $data[$f+2];
133
				$leases[$l]['end'] .= " " . $data[$f+3];
134
				$f = $f+3;
135
				break;
136
			case "tstp":
137
				$f = $f+3;
138
				break;
139
			case "tsfp":
140
				$f = $f+3;
141
				break;
142
			case "atsfp":
143
				$f = $f+3;
144
				break;
145
			case "cltt":
146
				$f = $f+3;
147
				break;
148
			case "binding":
149
				switch ($data[$f+2]) {
150
					case "active":
151
						$leases[$l]['act'] = "active";
152
						break;
153
					case "free":
154
						$leases[$l]['act'] = "expired";
155
						$leases[$l]['online'] = "offline";
156
						break;
157
					case "backup":
158
						$leases[$l]['act'] = "reserved";
159
						$leases[$l]['online'] = "offline";
160
						break;
161
				}
162
				$f = $f+1;
163
				break;
164
			case "next":
165
				/* skip the next binding statement */
166
				$f = $f+3;
167
				break;
168
			case "rewind":
169
				/* skip the rewind binding statement */
170
				$f = $f+3;
171
				break;
172
			case "hardware":
173
				$leases[$l]['mac'] = $data[$f+2];
174
				/* check if it's online and the lease is active */
175
				if ($leases[$l]['act'] == "active") {
176
					$online = exec("/usr/sbin/arp -an |/usr/bin/awk '/{$leases[$l]['ip']}/ {print}'|wc -l");
177
					if ($online == 1) {
178
						$leases[$l]['online'] = 'online';
179
					} else {
180
						$leases[$l]['online'] = 'offline';
181
					}
182
				}
183
				$f = $f+2;
184
				break;
185
			case "client-hostname":
186
				if ($data[$f+1] <> "") {
187
					$leases[$l]['hostname'] = preg_replace('/"/', '', $data[$f+1]);
188
				} else {
189
					$hostname = gethostbyaddr($leases[$l]['ip']);
190
					if ($hostname <> "") {
191
						$leases[$l]['hostname'] = $hostname;
192
					}
193
				}
194
				$f = $f+1;
195
				break;
196
			case "uid":
197
				$f = $f+1;
198
				break;
199
		}
200
		$f++;
201
	}
202
	$l++;
203
	$i++;
204
}
205

    
206
/* remove duplicate items by mac address */
207
if (count($leases) > 0) {
208
	$leases = remove_duplicate($leases, "ip");
209
}
210

    
211
if (count($pools) > 0) {
212
	$pools = remove_duplicate($pools, "name");
213
	asort($pools);
214
}
215

    
216
// Put this in an easy to use form
217
$dhcpmac = array();
218
$dhcpip = array();
219

    
220
foreach ($leases as $value) {
221
	$dhcpmac[$value['mac']] = $value['hostname'];
222
	$dhcpip[$value['ip']] = $value['hostname'];
223
}
224

    
225
exec("/usr/sbin/arp -an", $rawdata);
226

    
227
$i = 0;
228

    
229
/* if list */
230
$ifdescrs = get_configured_interface_with_descr();
231

    
232
foreach ($ifdescrs as $key => $interface) {
233
	$thisif = convert_friendly_interface_to_real_interface_name($key);
234
	if (!empty($thisif)) {
235
		$hwif[$thisif] = $interface;
236
	}
237
}
238

    
239
$data = array();
240
foreach ($rawdata as $line) {
241
	$elements = explode(' ', $line, 7);
242
	$arpent = array();
243
	$arpent['ip'] = trim(str_replace(array('(', ')'), '', $elements[1]));
244
	$arpent['mac'] = trim($elements[3]);
245
	$arpent['interface'] = trim($elements[5]);
246
	$arpent['status'] = trim(substr($elements[6], 0, strrpos($elements[6], ' ')));
247
	$arpent['linktype'] = trim(str_replace(array('[', ']'), '', strrchr($elements[6], ' ')));
248
	$data[] = $arpent;
249
}
250

    
251
function _getHostName($mac, $ip) {
252
	global $dhcpmac, $dhcpip;
253

    
254
	if ($dhcpmac[$mac]) {
255
		return $dhcpmac[$mac];
256
	} else if ($dhcpip[$ip]) {
257
		return $dhcpip[$ip];
258
	} else {
259
		exec("host -W 1 " . escapeshellarg($ip), $output);
260
		if (preg_match('/.*pointer ([A-Za-z_0-9.-]+)\..*/', $output[0], $matches)) {
261
			if ($matches[1] <> $ip) {
262
				return $matches[1];
263
			}
264
		}
265
	}
266
	return "";
267
}
268

    
269
$pgtitle = array(gettext("Diagnostics"), gettext("ARP Table"));
270
include("head.inc");
271

    
272
// Handle save msg if defined
273
if ($savemsg) {
274
	print_info_box(htmlentities($savemsg), $savemsgtype);
275
}
276
?>
277

    
278
<!-- On modern hardware the table will load so fast you may never see this! -->
279
<div id="loading">
280
	<?= gettext(" Loading, please wait...")?>
281
</div>
282

    
283
<?php
284

    
285
// Flush buffers out to client so that they see Loading, please wait....
286
for ($i = 0; $i < ob_get_level(); $i++) {
287
	ob_end_flush();
288
}
289

    
290
ob_implicit_flush(1);
291

    
292
// Resolve hostnames and replace Z_ with "".  The intention
293
// is to sort the list by hostnames, alpha and then the non
294
// resolvable addresses will appear last in the list.
295
$dnsavailable=1;
296
$dns = trim(_getHostName("", "8.8.8.8"));
297
if ($dns == "") {
298
	$dns = trim(_getHostName("", "8.8.4.4"));
299
	if ($dns == "") {
300
		$dnsavailable = 0;
301
	}
302
}
303

    
304
foreach ($data as &$entry) {
305
	if ($dnsavailable) {
306
		$dns = trim(_getHostName($entry['mac'], $entry['ip']));
307
	} else {
308
		$dns="";
309
	}
310
	if (trim($dns)) {
311
		$entry['dnsresolve'] = "$dns";
312
	} else {
313
		$entry['dnsresolve'] = "Z_ ";
314
	}
315
}
316
unset($entry);
317

    
318
// Sort the data alpha first
319
$data = msort($data, "dnsresolve");
320

    
321
// Load MAC-Manufacturer table
322
$mac_man = load_mac_manufacturer_table();
323
?>
324
<div class="panel panel-default">
325
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('ARP Table')?></h2></div>
326
	<div class="panel-body">
327

    
328
<div class="table-responsive">
329
	<table class="sortable-theme-bootstrap table table-striped table-hover" data-sortable>
330
		<thead>
331
			<tr>
332
				<th><?= gettext("Interface")?></th>
333
				<th><?= gettext("IP address")?></th>
334
				<th><?= gettext("MAC address")?></th>
335
				<th><?= gettext("Hostname")?></th>
336
				<th><?= gettext("Status")?></th>
337
				<th><?= gettext("Link Type")?></th>
338
				<th data-sortable="false"><?=gettext("Actions")?></th>
339
			</tr>
340
		</thead>
341
		<tbody>
342

    
343
<?php
344
		foreach ($data as $entry): ?>
345
			<tr>
346
				<td><?=$hwif[$entry['interface']]?></td>
347
				<td><?=$entry['ip']?></td>
348
				<td>
349
					<?=trim($entry['mac'])?>
350
				<?php
351
					$mac = trim($entry['mac']);
352
					$mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]);
353

    
354
					if (isset($mac_man[$mac_hi])) {
355
						print '<small>('. $mac_man[$mac_hi] .')</small>';
356
					}
357
	?>
358
				</td>
359
				<td><?=trim(str_replace("Z_ ", "", $entry['dnsresolve']))?></td>
360
				<td><?=ucfirst($entry['status'])?></td>
361
				<td><?=$entry['linktype']?></td>
362
				<td>
363
					<a class="fa fa-trash" title="<?=gettext('Delete arp cache entry')?>"	href="diag_arp.php?deleteentry=<?=$entry['ip']?>" usepost></a>
364
				</td>
365
			</tr>
366
		<?php endforeach?>
367
		</tbody>
368
	</table>
369
</div>
370

    
371
	</div>
372
</div>
373

    
374
<script type="text/javascript">
375
//<![CDATA[
376
// Clear the "loading" div once the page has loaded"
377
events.push(function() {
378
	$('#loading').empty();
379
});
380
//]]>
381
</script>
382

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

    
392
<?php
393
include("foot.inc");
394
?>
(8-8/231)