Change Mail Address
[racktables-contribs] / fingtab / fingtab.php
CommitLineData
d317c3b7 1<?php\r
2/**\r
3 * Fing network scan plugin\r
3fe6ce41 4 * version 0.2\r
d317c3b7 5 *\r
6 * Written by Florian Pfaff\r
7 *\r
8 * Inspired by livePTR and rc_pingreply\r
9 *\r
10 * The purpose of this plugin is to scan networks which cannot be reached directly.\r
11 * It uses SSH to connect to a host which has overlook fing installed and uses it to scan the defined network\r
12 *\r
13 * Requirements:\r
14 * - SSH client on racktables server\r
15 * - Overlook Fing http://www.overlooksoft.com/download installed on a host in the network you want to scan\r
16 * - the host with overlook fing has to be reachable via SSH\r
17 * - key SSH authentication needs to be configured\r
18 * - $FING_ssh_binary and $FING_settings need to be configured in the secret.php\r
19 *\r
20 */\r
21\r
22// register fing tab\r
23$tab['ipv4net']['fing'] = 'Fing Scan';\r
24$tabhandler['ipv4net']['fing'] = 'FingTab';\r
25$ophandler['ipv4net']['fing']['importFingData'] = 'importFingData';\r
26\r
27\r
3fe6ce41 28\r
d317c3b7 29/**\r
30 * Execute a command return exit code\r
31 * provide STDOUT and STDERR\r
32 */\r
33function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {\r
52adcb7b 34 $proc = proc_open($cmd, array(\r
35 1 => array('pipe','w'),\r
36 2 => array('pipe','w'),\r
37 ),$pipes);\r
d317c3b7 38 $stdout = stream_get_contents($pipes[1]);\r
39 fclose($pipes[1]);\r
40 $stderr = stream_get_contents($pipes[2]);\r
41 fclose($pipes[2]);\r
42 return proc_close($proc);\r
43}\r
44\r
45\r
46/**\r
47 * Parse a csv returned by fing and return an array with the parsed information\r
48 */\r
49function parse_fing_csv($csv){\r
50 $known_ips = array();\r
51 foreach (preg_split("/((\r?\n)|(\r\n?))/", $csv) as $line) {\r
52 $fields= explode(";", $line);\r
53\r
54 if ($fields[0]) {\r
55 $tmp_array = array(\r
56 "ip" => $fields[0],\r
57 "state" => $fields[2],\r
58 "timestamp" => $fields[3],\r
59 "hostname" => $fields[4],\r
60 "mac" => $fields[5],\r
61 "vendor" => array_key_exists(6,$fields) ? $fields[6] : ""\r
62 );\r
63 $known_ips[$fields[0]] = $tmp_array;\r
64 }\r
65 }\r
66 return $known_ips;\r
67}\r
68\r
69\r
70/**\r
71 * get hostname, state and mac/vendor information for a given ip\r
72 */\r
73function get_fing_info($ip,$known_ips){\r
74 $hostname = "";\r
75 $state = "down";\r
76 $mac_vendor ="";\r
77\r
78 if (array_key_exists($ip, $known_ips))\r
79 {\r
80 $hostname = $known_ips[$ip]["hostname"];\r
81 $state = strtoupper($known_ips[$ip]["state"]);\r
82 $mac_vendor = $known_ips[$ip]["mac"];\r
83 if ($known_ips[$ip]["vendor"])\r
84 $mac_vendor = $mac_vendor." (".$known_ips[$ip]["vendor"].")";\r
85 }\r
86 $result = array($hostname, $state, $mac_vendor);\r
87 return $result;\r
88}\r
89\r
90\r
91/**\r
92 * Return the amount of hosts which are up\r
93 */\r
94function get_fing_up_count($known_ips)\r
95{\r
96 $count = 0;\r
97 foreach ($known_ips as $cip)\r
98 {\r
99 if (strtoupper($cip["state"]) == "UP")\r
100 $count++;\r
101 }\r
102 return $count;\r
103}\r
104\r
105/**\r
106 * render error message\r
107 */\r
108function render_fing_error($title,$msg)\r
109{\r
110 echo "<div class='msg_error'>";\r
111 echo "<h2>${title}</h2>";\r
112 echo $msg;\r
113 echo "</div>";\r
114}\r
115\r
116/**\r
117 * Class FingException\r
118 */\r
119class FingException extends Exception {}\r
120\r
121\r
122/**\r
123 * get fing settings array for a specific subnet\r
124 */\r
125function get_fing_settings($ip,$mask)\r
126{\r
127 global $FING_settings;\r
128 if (!$FING_settings)\r
129 throw new FingException("Fing configuration not found. Please make sure \$FING_settings is configured in the secret.php");\r
130\r
131 $net = $ip."/".$mask;\r
132 if (!array_key_exists($net,$FING_settings))\r
133 throw new FingException("No matching Fing configuration found for network ${net}");\r
134\r
135 return $FING_settings[$net];\r
136}\r
137\r
138\r
139/**\r
140 * scan a subnet using fing\r
141 */\r
142function get_fing_scan($ip,$mask)\r
143{\r
144 global $FING_ssh_binary;\r
145\r
146 if (!$FING_ssh_binary)\r
147 throw new FingException("\$FING_ssh_binary is not defined in secret.php");\r
148\r
149 $settings = get_fing_settings($ip,$mask);\r
150\r
151 $fing_gw = $settings["gateway"];\r
152 $user = $settings["user"];\r
153 $sudo = $settings["gateway"] ? "sudo" : "";\r
154 $id_file = $settings["id_file"];\r
155 $cmd = "$FING_ssh_binary $user@$fing_gw -v -i $id_file -o 'PasswordAuthentication no' $sudo fing -r 1 -n $ip/$mask -o table,csv,console --silent";\r
156\r
157 $ret_val = my_shell_exec("$cmd", $std_out, $std_err);\r
158 if ($ret_val>0)\r
159 throw new FingException("Error executing fing:<br><pre>Command: $cmd\n\n exit code: $ret_val\n\n STDERR: $std_err\n\n STDOUT: $std_out\n</pre>");\r
160\r
161 $known_ips = parse_fing_csv($std_out);\r
162\r
163 return $known_ips;\r
164}\r
165\r
3fe6ce41 166/**\r
167 *\r
168 * address allocation setting (copied from interface.php)\r
169 *\r
170 */\r
171\r
172\r
d317c3b7 173\r
174\r
175/**\r
176 *\r
177 * Fing tab handler\r
178 *\r
179 */\r
180function FingTab($id)\r
181{\r
182 $can_import = permitted (NULL, NULL, 'importFingData');\r
183\r
3fe6ce41 184\r
185 //\r
186 // allocation settings\r
187 //\r
188 // address allocation code, IPv4 networks view\r
189 $aac_left = array\r
190 (\r
191 'regular' => '',\r
192 'virtual' => '<span class="aac-left" title="Loopback">L:</span>',\r
193 'shared' => '<span class="aac-left" title="Shared">S:</span>',\r
194 'router' => '<span class="aac-left" title="Router">R:</span>',\r
195 'point2point' => '<span class="aac-left" title="Point-to-point">P:</span>',\r
196 );\r
197\r
d317c3b7 198 //\r
199 // header\r
200 //\r
201 global $pageno, $tabno;\r
202\r
203 $maxperpage = getConfigVar('IPV4_ADDRS_PER_PAGE');\r
204 $range = spotEntity('ipv4net', $id);\r
205 loadIPAddrList($range);\r
206 echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";\r
207\r
208\r
209 //\r
210 // execute fing\r
211 //\r
212 try {\r
213 $known_ips = get_fing_scan($range['ip'], $range['mask']);\r
214 $fing_cfg = get_fing_settings($range['ip'], $range['mask']);\r
215 $fing_gw = $fing_cfg["gateway"];\r
216 } catch (FingException $e) {\r
217 render_fing_error("Could not get network scan via fing:",$e->getMessage());\r
218 return FALSE;\r
219 }\r
220\r
221 echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";\r
222 startPortlet ('overlook fing (via: '.$fing_gw.')');\r
223\r
224 //\r
225 // pagination\r
226 //\r
227 if (isset($_REQUEST['pg']))\r
228 $page = $_REQUEST['pg'];\r
229 else\r
230 $page = 0;\r
231 $startip = ip4_bin2int ($range['ip_bin']);\r
232 $endip = ip4_bin2int (ip_last ($range));\r
233 $numpages = 0;\r
234 if ($endip - $startip > $maxperpage)\r
235 {\r
236 $numpages = ($endip - $startip) / $maxperpage;\r
237 $startip = $startip + $page * $maxperpage;\r
238 $endip = $startip + $maxperpage - 1;\r
239 }\r
240 echo "<center>";\r
241 if ($numpages)\r
242 echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';\r
243 for ($i=0; $i<$numpages; $i++)\r
244 if ($i == $page)\r
245 echo "<b>$i</b> ";\r
246 else\r
247 echo "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'id'=>$id, 'pg'=>$i))."'>$i</a> ";\r
248 echo "</center>";\r
249\r
250 if ($can_import)\r
251 {\r
252 printOpFormIntro ('importFingData', array ('addrcount' => ($endip - $startip + 1)));\r
253 $box_counter = 1;\r
254 }\r
255\r
256\r
257 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center'>\n";\r
3fe6ce41 258 echo "<tr><th class='tdleft'>address</th><th class='tdleft'>state</th><th class='tdleft'>current name</th><th class='tdleft'>DNS name</th><th class='tdleft'>MAC</th><th class='tdleft'>Allocation</th>";\r
d317c3b7 259 if ($can_import)\r
260 echo '<th>import</th>';\r
261 echo "</tr>\n";\r
262\r
263\r
264 //\r
265 // Loop through all IPs\r
266 //\r
267 $cnt_match = $cnt_missing = $cnt_mismatch = $cnt_total = 0;\r
268 for ($ip = $startip; $ip <= $endip; $ip++)\r
269 {\r
270 $cnt_total++;\r
271 $print_cbox = FALSE;\r
272 $ip_bin = ip4_int2bin($ip);\r
273 $addr = isset ($range['addrlist'][$ip_bin]) ? $range['addrlist'][$ip_bin] : array ('name' => '', 'reserved' => 'no');\r
274 $straddr = ip4_format ($ip_bin);\r
275\r
276 list($fing_hostname, $fing_state, $fing_mac_vendor) = get_fing_info($straddr, $known_ips);\r
277 $ip_is_up = strtoupper($fing_state) == "UP" ? TRUE : FALSE;\r
278\r
279 if ($can_import)\r
280 {\r
281 echo "<input type=hidden name=addr_${cnt_total} value=${straddr}>\n";\r
282 echo "<input type=hidden name=descr_${cnt_total} value=${fing_hostname}>\n";\r
283 echo "<input type=hidden name=rsvd_${cnt_total} value=${addr['reserved']}>\n";\r
284 }\r
285\r
286 $skip_dns_check = FALSE;\r
287 echo "<tr";\r
288 // Ignore network and broadcast addresses\r
289 if (($ip == $startip && $addr['name'] == 'network') || ($ip == $endip && $addr['name'] == 'broadcast'))\r
290 {\r
291 echo " class='trbusy'";\r
292 $skip_dns_check = TRUE;\r
293 }\r
294 elseif (!$ip_is_up)\r
295 echo " class='trnull'";\r
296 // set line color depending if we have the name already in the DB\r
297 if (!$skip_dns_check) {\r
298 if ($addr['name'] == $fing_hostname) {\r
299 if (strlen($fing_hostname)) {\r
300 echo ' class=trok';\r
301 $cnt_match++;\r
302 }\r
303 } elseif (!strlen($addr['name']) or !strlen($fing_hostname)) {\r
304 echo ' class=trwarning';\r
305 $print_cbox = TRUE;\r
306 $cnt_missing++;\r
307 } else {\r
308 echo ' class=trerror';\r
309 $print_cbox = TRUE;\r
310 $cnt_mismatch++;\r
311 }\r
312 }\r
313\r
314 //IP\r
315 echo "><td class='tdleft";\r
316 if (isset ($range['addrlist'][$ip_bin]['class']) and strlen ($range['addrlist'][$ip_bin]['class']))\r
317 echo ' ' . $range['addrlist'][$ip_bin]['class'];\r
318 echo "'><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$straddr))."'>${straddr}</a></td>";\r
319\r
320 //other columns\r
321 if ($skip_dns_check)\r
322 echo "<td class='tdleft'>&nbsp;</td>";\r
323 else {\r
324 if (!$ip_is_up)\r
325 echo "<td class='tdleft'>" . $fing_state . "</td>";\r
326 else\r
327 echo "<td class='tdleft'><div class='strong'>" . $fing_state . "</div></td>";\r
328 }\r
329 echo "<td class=tdleft>${addr['name']}</td>";\r
330 echo "<td class='tdleft'>".$fing_hostname."</td>";\r
331 echo "<td class='tdleft'>".$fing_mac_vendor."</td>";\r
332\r
3fe6ce41 333\r
334 //allocation\r
335 echo "<td>";\r
336 $delim = '';\r
337 if ( $addr['reserved'] == 'yes')\r
338 {\r
339 echo "<strong>RESERVED</strong> ";\r
340 $delim = '; ';\r
341 }\r
342 foreach ($addr['allocs'] as $ref)\r
343 {\r
344 echo $delim . $aac_left[$ref['type']];\r
345 echo makeIPAllocLink ($ip_bin, $ref, TRUE);\r
346 $delim = '; ';\r
347 }\r
348 if ($delim != '')\r
349 $delim = '<br>';\r
350 foreach ($addr['vslist'] as $vs_id)\r
351 {\r
352 $vs = spotEntity ('ipv4vs', $vs_id);\r
353 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';\r
354 $delim = '<br>';\r
355 }\r
356 foreach ($addr['vsglist'] as $vs_id)\r
357 {\r
358 $vs = spotEntity ('ipvs', $vs_id);\r
359 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';\r
360 $delim = '<br>';\r
361 }\r
362 foreach ($addr['rsplist'] as $rsp_id)\r
363 {\r
364 $rsp = spotEntity ('ipv4rspool', $rsp_id);\r
365 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);\r
366 $delim = '<br>';\r
367 }\r
368 echo "</td>";\r
369\r
d317c3b7 370 // import column\r
371 if ($can_import)\r
372 {\r
373 echo '<td>';\r
374 if ($print_cbox)\r
375 echo "<input type=checkbox name=import_${cnt_total} id=atom_1_" . $box_counter++ . "_1>";\r
376 else\r
377 echo '&nbsp;';\r
378 echo '</td>';\r
379 }\r
380 echo "</tr>";\r
381 }\r
382\r
383 if ($can_import && $box_counter > 1)\r
384 {\r
385 echo '<tr><td colspan=4 align=center><input type=submit value="Import selected records"></td><td colspan=2 align=right>';\r
386 addJS ('js/racktables.js');\r
387 echo --$box_counter ? "<a href='javascript:;' onclick=\"toggleColumnOfAtoms(1, 1, ${box_counter})\">(toggle selection)</a>" : '&nbsp;';\r
388 echo '</td></tr>';\r
389 }\r
390\r
391 echo "</table>";\r
392 if ($can_import)\r
393 echo '</form>';\r
394 finishPortlet();\r
395\r
396 echo "</td><td class=pcright>";\r
397\r
398 //\r
399 // PING Statistics\r
400 //\r
401 startPortlet ('ping stats');\r
402 $cnt_ping_up = get_fing_up_count($known_ips);\r
403 echo "<table border=0 width='100%' cellspacing=0 cellpadding=2>";\r
404 echo "<tr class=trok><th class=tdright>Replied to Ping</th><td class=tdleft>${cnt_ping_up}</td></tr>\n";\r
405 echo "<tr class=trwarning><th class=tdright>No Response</th><td class=tdleft>".($cnt_total-$cnt_ping_up)."</td></tr>\n";\r
406 echo "</table>\n";\r
407 finishPortlet();\r
408\r
409 //\r
410 // DNS Statistics\r
411 //\r
412 startPortlet ('dns stats');\r
413 echo "<table border=0 width='100%' cellspacing=0 cellpadding=2>";\r
414 echo "<tr class=trok><th class=tdright>Exact matches:</th><td class=tdleft>${cnt_match}</td></tr>\n";\r
415 echo "<tr class=trwarning><th class=tdright>Missing from DB/DNS:</th><td class=tdleft>${cnt_missing}</td></tr>\n";\r
416 if ($cnt_mismatch)\r
417 echo "<tr class=trerror><th class=tdright>Mismatches:</th><td class=tdleft>${cnt_mismatch}</td></tr>\n";\r
418 echo "</table>\n";\r
419 finishPortlet();\r
420}\r
421\r
422\r
423$msgcode['importFingData']['OK'] = 26;\r
424$msgcode['importFingData']['ERR'] = 141;\r
425function importFingData ()\r
426{\r
427 $net = spotEntity ('ipv4net', getBypassValue());\r
428 assertUIntArg ('addrcount');\r
429 $nbad = $ngood = 0;\r
430 for ($i = 1; $i <= $_REQUEST['addrcount']; $i++)\r
431 {\r
432 $inputname = "import_${i}";\r
433 if (! isCheckSet ($inputname))\r
434 continue;\r
435 $ip_bin = assertIPv4Arg ("addr_${i}");\r
436 assertStringArg ("descr_${i}", TRUE);\r
437 assertStringArg ("rsvd_${i}");\r
438 // Non-existent addresses will not have this argument set in request.\r
439 $rsvd = 'no';\r
440 if ($_REQUEST["rsvd_${i}"] == 'yes')\r
441 $rsvd = 'yes';\r
442 try\r
443 {\r
444 if (! ip_in_range ($ip_bin, $net))\r
445 throw new InvalidArgException ('ip_bin', $ip_bin);\r
446 updateAddress ($ip_bin, $_REQUEST["descr_${i}"], $rsvd);\r
447 $ngood++;\r
448 }\r
449 catch (RackTablesError $e)\r
450 {\r
451 $nbad++;\r
452 }\r
453 }\r
454 if (!$nbad)\r
455 showFuncMessage (__FUNCTION__, 'OK', array ($ngood));\r
456 else\r
457 showFuncMessage (__FUNCTION__, 'ERR', array ($nbad, $ngood));\r
458}\r