added fingtab plugin to scan networks using overlook fing 20/head
authortwigs <pfaffflo@gmx.de>
Tue, 3 Mar 2015 20:53:17 +0000 (21:53 +0100)
committertwigs <pfaffflo@gmx.de>
Tue, 3 Mar 2015 20:53:17 +0000 (21:53 +0100)
fingtab/README [new file with mode: 0644]
fingtab/example.png [new file with mode: 0644]
fingtab/fingtab.php [new file with mode: 0644]

diff --git a/fingtab/README b/fingtab/README
new file mode 100644 (file)
index 0000000..3424660
--- /dev/null
@@ -0,0 +1,65 @@
+fingtab racktables plugin\r
+-------------------------\r
+\r
+Author: Florian Pfaff (pfaffflo@gmx.de)\r
+\r
+\r
+Overview\r
+-------\r
+Scan remote networks which use different DNS servers and cannot be pinged.\r
+This plugin uses Overlook Fing on an SSH host to scan remote networks.\r
+\r
+It shows, which hosts are up and allows to import remote dns names.\r
+In addition the mac address and the vendor name is displayed.\r
+\r
+Installation\r
+------------\r
+1. copy the php file into the racktables plugin directory\r
+2. make sure overlook fing (http://www.overlooksoft.com/download) is installed on the remote host\r
+3. make sure you have an ssh key on the racktables host which you can use\r
+   to log into the remote host to run fing\r
+4. add path to ssh binary to secret.php\r
+5. add connection information for the networks you want to scan to secret.php\r
+\r
+\r
+Configuration\r
+-------------\r
+The following variables have to been set in secret.php\r
+\r
+$FING_ssh_binary\r
+The ssh binary which the plugin should use to connect to the remote system\r
+\r
+>> $FING_ssh_binary = "/usr/bin/ssh";\r
+\r
+\r
+$FING_settings\r
+two dimensional array which contains the connection details for the networks\r
+The first key indicates the network for which the settings are, the second keys define the connection details\r
+\r
+    Network Format:\r
+        IPv4addr/CIDR   1.2.3.4/24\r
+\r
+    Connection Details\r
+\r
+        Key         Example         Type        Description\r
+        gateway     1.2.3.4         string      The IP or hostname of the SSH host on which fing is installed\r
+        user        myuser          string      user name used to authenticate with the SSH host\r
+        sudo        TRUE            boolean     True is sudo should be used to run fing. The sudo command is not allowed to require a password\r
+        id_file     /rsssh/id_rsa   string      private key used to authenticate with the SSH host\r
+\r
+\r
+Example Configuration\r
+---------------------\r
+\r
+$FING_ssh_binary = "/usr/bin/ssh";\r
+$FING_settings = array(\r
+    "192.168.123.0/24" => array(\r
+        "gateway" => "192.168.123.100",\r
+        "user" => "pi",\r
+        "sudo" => TRUE,\r
+        "id_file" => "/var/www/.ssh/id_rsa"\r
+    )\r
+)\r
+\r
+\r
+\r
diff --git a/fingtab/example.png b/fingtab/example.png
new file mode 100644 (file)
index 0000000..14624cd
Binary files /dev/null and b/fingtab/example.png differ
diff --git a/fingtab/fingtab.php b/fingtab/fingtab.php
new file mode 100644 (file)
index 0000000..20f05c2
--- /dev/null
@@ -0,0 +1,403 @@
+<?php\r
+/**\r
+ * Fing network scan plugin\r
+ * version 0.1\r
+ *\r
+ * Written by Florian Pfaff\r
+ *\r
+ * Inspired by livePTR and rc_pingreply\r
+ *\r
+ * The purpose of this plugin is to scan networks which cannot be reached directly.\r
+ * It uses SSH to connect to a host which has overlook fing installed and uses it to scan the defined network\r
+ *\r
+ * Requirements:\r
+ *   - SSH client on racktables server\r
+ *   - Overlook Fing http://www.overlooksoft.com/download installed on a host in the network you want to scan\r
+ *   - the host with overlook fing has to be reachable via SSH\r
+ *   - key SSH authentication needs to be configured\r
+ *   - $FING_ssh_binary and $FING_settings need to be configured in the secret.php\r
+ *\r
+ */\r
+\r
+// register fing tab\r
+$tab['ipv4net']['fing'] = 'Fing Scan';\r
+$tabhandler['ipv4net']['fing'] = 'FingTab';\r
+$ophandler['ipv4net']['fing']['importFingData'] = 'importFingData';\r
+\r
+\r
+/**\r
+ * Execute a command return exit code\r
+ * provide STDOUT and STDERR\r
+ */\r
+function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {\r
+    $proc = proc_open($cmd,[\r
+        1 => ['pipe','w'],\r
+        2 => ['pipe','w'],\r
+    ],$pipes);\r
+    $stdout = stream_get_contents($pipes[1]);\r
+    fclose($pipes[1]);\r
+    $stderr = stream_get_contents($pipes[2]);\r
+    fclose($pipes[2]);\r
+    return proc_close($proc);\r
+}\r
+\r
+\r
+/**\r
+ * Parse a csv returned by fing and return an array with the parsed information\r
+ */\r
+function parse_fing_csv($csv){\r
+    $known_ips = array();\r
+    foreach (preg_split("/((\r?\n)|(\r\n?))/", $csv) as $line) {\r
+        $fields= explode(";", $line);\r
+\r
+        if ($fields[0]) {\r
+            $tmp_array = array(\r
+                "ip" => $fields[0],\r
+                "state" => $fields[2],\r
+                "timestamp" => $fields[3],\r
+                "hostname" => $fields[4],\r
+                "mac" => $fields[5],\r
+                "vendor" => array_key_exists(6,$fields) ? $fields[6] : ""\r
+            );\r
+            $known_ips[$fields[0]] = $tmp_array;\r
+        }\r
+    }\r
+    return $known_ips;\r
+}\r
+\r
+\r
+/**\r
+ * get hostname, state and mac/vendor information for a given ip\r
+ */\r
+function get_fing_info($ip,$known_ips){\r
+    $hostname = "";\r
+    $state = "down";\r
+    $mac_vendor ="";\r
+\r
+    if (array_key_exists($ip, $known_ips))\r
+    {\r
+        $hostname = $known_ips[$ip]["hostname"];\r
+        $state = strtoupper($known_ips[$ip]["state"]);\r
+        $mac_vendor = $known_ips[$ip]["mac"];\r
+        if ($known_ips[$ip]["vendor"])\r
+            $mac_vendor = $mac_vendor." (".$known_ips[$ip]["vendor"].")";\r
+    }\r
+    $result = array($hostname, $state, $mac_vendor);\r
+    return $result;\r
+}\r
+\r
+\r
+/**\r
+ * Return the amount of hosts which are up\r
+ */\r
+function get_fing_up_count($known_ips)\r
+{\r
+    $count = 0;\r
+    foreach ($known_ips as $cip)\r
+    {\r
+        if (strtoupper($cip["state"]) == "UP")\r
+            $count++;\r
+    }\r
+    return $count;\r
+}\r
+\r
+/**\r
+ * render error message\r
+ */\r
+function render_fing_error($title,$msg)\r
+{\r
+    echo "<div class='msg_error'>";\r
+    echo "<h2>${title}</h2>";\r
+    echo $msg;\r
+    echo "</div>";\r
+}\r
+\r
+/**\r
+ * Class FingException\r
+ */\r
+class FingException extends Exception {}\r
+\r
+\r
+/**\r
+ * get fing settings array for a specific subnet\r
+ */\r
+function get_fing_settings($ip,$mask)\r
+{\r
+    global $FING_settings;\r
+    if (!$FING_settings)\r
+        throw new FingException("Fing configuration not found. Please make sure \$FING_settings is configured in the secret.php");\r
+\r
+    $net = $ip."/".$mask;\r
+    if (!array_key_exists($net,$FING_settings))\r
+        throw new FingException("No matching Fing configuration found for network ${net}");\r
+\r
+    return $FING_settings[$net];\r
+}\r
+\r
+\r
+/**\r
+ * scan a subnet using fing\r
+ */\r
+function get_fing_scan($ip,$mask)\r
+{\r
+    global $FING_ssh_binary;\r
+\r
+    if (!$FING_ssh_binary)\r
+        throw new FingException("\$FING_ssh_binary is not defined in secret.php");\r
+\r
+    $settings = get_fing_settings($ip,$mask);\r
+\r
+    $fing_gw = $settings["gateway"];\r
+    $user = $settings["user"];\r
+    $sudo = $settings["gateway"] ? "sudo" : "";\r
+    $id_file = $settings["id_file"];\r
+    $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
+\r
+    $ret_val = my_shell_exec("$cmd", $std_out, $std_err);\r
+    if ($ret_val>0)\r
+        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
+\r
+    $known_ips = parse_fing_csv($std_out);\r
+\r
+    return $known_ips;\r
+}\r
+\r
+\r
+\r
+/**\r
+ *\r
+ * Fing tab handler\r
+ *\r
+ */\r
+function FingTab($id)\r
+{\r
+    $can_import = permitted (NULL, NULL, 'importFingData');\r
+\r
+    //\r
+    // header\r
+    //\r
+    global $pageno, $tabno;\r
+\r
+    $maxperpage = getConfigVar('IPV4_ADDRS_PER_PAGE');\r
+    $range = spotEntity('ipv4net', $id);\r
+    loadIPAddrList($range);\r
+    echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";\r
+\r
+\r
+    //\r
+    // execute fing\r
+    //\r
+    try {\r
+        $known_ips = get_fing_scan($range['ip'], $range['mask']);\r
+        $fing_cfg = get_fing_settings($range['ip'], $range['mask']);\r
+        $fing_gw = $fing_cfg["gateway"];\r
+    } catch (FingException $e) {\r
+        render_fing_error("Could not get network scan via fing:",$e->getMessage());\r
+        return FALSE;\r
+    }\r
+\r
+    echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";\r
+    startPortlet ('overlook fing (via: '.$fing_gw.')');\r
+\r
+    //\r
+    // pagination\r
+    //\r
+    if (isset($_REQUEST['pg']))\r
+        $page = $_REQUEST['pg'];\r
+    else\r
+        $page = 0;\r
+    $startip = ip4_bin2int ($range['ip_bin']);\r
+    $endip = ip4_bin2int (ip_last ($range));\r
+    $numpages = 0;\r
+    if ($endip - $startip > $maxperpage)\r
+    {\r
+        $numpages = ($endip - $startip) / $maxperpage;\r
+        $startip = $startip + $page * $maxperpage;\r
+        $endip = $startip + $maxperpage - 1;\r
+    }\r
+    echo "<center>";\r
+    if ($numpages)\r
+        echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';\r
+    for ($i=0; $i<$numpages; $i++)\r
+        if ($i == $page)\r
+            echo "<b>$i</b> ";\r
+        else\r
+            echo "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'id'=>$id, 'pg'=>$i))."'>$i</a> ";\r
+    echo "</center>";\r
+\r
+    if ($can_import)\r
+    {\r
+        printOpFormIntro ('importFingData', array ('addrcount' => ($endip - $startip + 1)));\r
+        $box_counter = 1;\r
+    }\r
+\r
+\r
+    echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center'>\n";\r
+    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>";\r
+    if ($can_import)\r
+        echo '<th>import</th>';\r
+    echo "</tr>\n";\r
+\r
+\r
+    //\r
+    // Loop through all IPs\r
+    //\r
+    $cnt_match = $cnt_missing = $cnt_mismatch = $cnt_total = 0;\r
+    for ($ip = $startip; $ip <= $endip; $ip++)\r
+    {\r
+        $cnt_total++;\r
+        $print_cbox = FALSE;\r
+        $ip_bin = ip4_int2bin($ip);\r
+        $addr = isset ($range['addrlist'][$ip_bin]) ? $range['addrlist'][$ip_bin] : array ('name' => '', 'reserved' => 'no');\r
+        $straddr = ip4_format ($ip_bin);\r
+\r
+        list($fing_hostname, $fing_state, $fing_mac_vendor) = get_fing_info($straddr, $known_ips);\r
+        $ip_is_up = strtoupper($fing_state) == "UP" ? TRUE : FALSE;\r
+\r
+        if ($can_import)\r
+        {\r
+            echo "<input type=hidden name=addr_${cnt_total} value=${straddr}>\n";\r
+            echo "<input type=hidden name=descr_${cnt_total} value=${fing_hostname}>\n";\r
+            echo "<input type=hidden name=rsvd_${cnt_total} value=${addr['reserved']}>\n";\r
+        }\r
+\r
+        $skip_dns_check = FALSE;\r
+        echo "<tr";\r
+        // Ignore network and broadcast addresses\r
+        if (($ip == $startip && $addr['name'] == 'network') || ($ip == $endip && $addr['name'] == 'broadcast'))\r
+        {\r
+            echo " class='trbusy'";\r
+            $skip_dns_check = TRUE;\r
+        }\r
+        elseif (!$ip_is_up)\r
+            echo " class='trnull'";\r
+        // set line color depending if we have the name already in the DB\r
+        if (!$skip_dns_check) {\r
+            if ($addr['name'] == $fing_hostname) {\r
+                if (strlen($fing_hostname)) {\r
+                    echo ' class=trok';\r
+                    $cnt_match++;\r
+                }\r
+            } elseif (!strlen($addr['name']) or !strlen($fing_hostname)) {\r
+                echo ' class=trwarning';\r
+                $print_cbox = TRUE;\r
+                $cnt_missing++;\r
+            } else {\r
+                echo ' class=trerror';\r
+                $print_cbox = TRUE;\r
+                $cnt_mismatch++;\r
+            }\r
+        }\r
+\r
+        //IP\r
+        echo "><td class='tdleft";\r
+        if (isset ($range['addrlist'][$ip_bin]['class']) and strlen ($range['addrlist'][$ip_bin]['class']))\r
+            echo ' ' . $range['addrlist'][$ip_bin]['class'];\r
+        echo "'><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$straddr))."'>${straddr}</a></td>";\r
+\r
+        //other columns\r
+        if ($skip_dns_check)\r
+            echo "<td class='tdleft'>&nbsp;</td>";\r
+        else {\r
+            if (!$ip_is_up)\r
+                echo "<td class='tdleft'>" . $fing_state . "</td>";\r
+            else\r
+                echo "<td class='tdleft'><div class='strong'>" . $fing_state . "</div></td>";\r
+        }\r
+        echo "<td class=tdleft>${addr['name']}</td>";\r
+        echo "<td class='tdleft'>".$fing_hostname."</td>";\r
+        echo "<td class='tdleft'>".$fing_mac_vendor."</td>";\r
+\r
+        // import column\r
+        if ($can_import)\r
+        {\r
+            echo '<td>';\r
+            if ($print_cbox)\r
+                echo "<input type=checkbox name=import_${cnt_total} id=atom_1_" . $box_counter++ . "_1>";\r
+            else\r
+                echo '&nbsp;';\r
+            echo '</td>';\r
+        }\r
+        echo "</tr>";\r
+    }\r
+\r
+    if ($can_import && $box_counter > 1)\r
+    {\r
+        echo '<tr><td colspan=4 align=center><input type=submit value="Import selected records"></td><td colspan=2 align=right>';\r
+        addJS ('js/racktables.js');\r
+        echo --$box_counter ? "<a href='javascript:;' onclick=\"toggleColumnOfAtoms(1, 1, ${box_counter})\">(toggle selection)</a>" : '&nbsp;';\r
+        echo '</td></tr>';\r
+    }\r
+\r
+    echo "</table>";\r
+    if ($can_import)\r
+        echo '</form>';\r
+    finishPortlet();\r
+\r
+    echo "</td><td class=pcright>";\r
+\r
+    //\r
+    // PING Statistics\r
+    //\r
+    startPortlet ('ping stats');\r
+    $cnt_ping_up = get_fing_up_count($known_ips);\r
+    echo "<table border=0 width='100%' cellspacing=0 cellpadding=2>";\r
+    echo "<tr class=trok><th class=tdright>Replied to Ping</th><td class=tdleft>${cnt_ping_up}</td></tr>\n";\r
+    echo "<tr class=trwarning><th class=tdright>No Response</th><td class=tdleft>".($cnt_total-$cnt_ping_up)."</td></tr>\n";\r
+    echo "</table>\n";\r
+    finishPortlet();\r
+\r
+    //\r
+    // DNS Statistics\r
+    //\r
+    startPortlet ('dns stats');\r
+    echo "<table border=0 width='100%' cellspacing=0 cellpadding=2>";\r
+    echo "<tr class=trok><th class=tdright>Exact matches:</th><td class=tdleft>${cnt_match}</td></tr>\n";\r
+    echo "<tr class=trwarning><th class=tdright>Missing from DB/DNS:</th><td class=tdleft>${cnt_missing}</td></tr>\n";\r
+    if ($cnt_mismatch)\r
+        echo "<tr class=trerror><th class=tdright>Mismatches:</th><td class=tdleft>${cnt_mismatch}</td></tr>\n";\r
+    echo "</table>\n";\r
+    finishPortlet();\r
+}\r
+\r
+\r
+$msgcode['importFingData']['OK'] = 26;\r
+$msgcode['importFingData']['ERR'] = 141;\r
+function importFingData ()\r
+{\r
+    $net = spotEntity ('ipv4net', getBypassValue());\r
+    assertUIntArg ('addrcount');\r
+    $nbad = $ngood = 0;\r
+    for ($i = 1; $i <= $_REQUEST['addrcount']; $i++)\r
+    {\r
+        $inputname = "import_${i}";\r
+        if (! isCheckSet ($inputname))\r
+            continue;\r
+        $ip_bin = assertIPv4Arg ("addr_${i}");\r
+        assertStringArg ("descr_${i}", TRUE);\r
+        assertStringArg ("rsvd_${i}");\r
+        // Non-existent addresses will not have this argument set in request.\r
+        $rsvd = 'no';\r
+        if ($_REQUEST["rsvd_${i}"] == 'yes')\r
+            $rsvd = 'yes';\r
+        try\r
+        {\r
+            if (! ip_in_range ($ip_bin, $net))\r
+                throw new InvalidArgException ('ip_bin', $ip_bin);\r
+            updateAddress ($ip_bin, $_REQUEST["descr_${i}"], $rsvd);\r
+            $ngood++;\r
+        }\r
+        catch (RackTablesError $e)\r
+        {\r
+            $nbad++;\r
+        }\r
+    }\r
+    if (!$nbad)\r
+        showFuncMessage (__FUNCTION__, 'OK', array ($ngood));\r
+    else\r
+        showFuncMessage (__FUNCTION__, 'ERR', array ($nbad, $ngood));\r
+}\r
+\r
+\r
+\r
+?>
\ No newline at end of file