Project

General

Profile

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
?>