* Created the new tools/striker-scan-network tool that ping scans a network range and records the discovered hosts in the new 'mac_to_ip' table. Also created Database->insert_or_update_mac_to_ip() to handle the new table.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent 7e960f1632
commit 1d13e669a7
  1. 2
      Anvil/Tools.pm
  2. 218
      Anvil/Tools/Database.pm
  3. 2
      Anvil/Tools/Network.pm
  4. 50
      share/anvil.sql
  5. 10
      share/words.xml
  6. 38
      tools/anvil-daemon
  7. 9
      tools/striker-parse-oui
  8. 279
      tools/striker-scan-network

@ -1110,6 +1110,7 @@ sub _set_paths
md5sum => "/usr/bin/md5sum", md5sum => "/usr/bin/md5sum",
'mkdir' => "/usr/bin/mkdir", 'mkdir' => "/usr/bin/mkdir",
mv => "/usr/bin/mv", mv => "/usr/bin/mv",
nmap => "/usr/bin/nmap",
nmcli => "/bin/nmcli", nmcli => "/bin/nmcli",
openssl => "/usr/bin/openssl", openssl => "/usr/bin/openssl",
passwd => "/usr/bin/passwd", passwd => "/usr/bin/passwd",
@ -1135,6 +1136,7 @@ sub _set_paths
'striker-manage-peers' => "/usr/sbin/striker-manage-peers", 'striker-manage-peers' => "/usr/sbin/striker-manage-peers",
'striker-parse-oui' => "/usr/sbin/striker-parse-oui", 'striker-parse-oui' => "/usr/sbin/striker-parse-oui",
'striker-prep-database' => "/usr/sbin/striker-prep-database", 'striker-prep-database' => "/usr/sbin/striker-prep-database",
'striker-scan-network' => "/usr/sbin/striker-scan-network",
stty => "/usr/bin/stty", stty => "/usr/bin/stty",
su => "/usr/bin/su", su => "/usr/bin/su",
'subscription-manager' => "/usr/sbin/subscription-manager", 'subscription-manager' => "/usr/sbin/subscription-manager",

@ -4519,6 +4519,222 @@ INSERT INTO
return($network_interface_uuid); return($network_interface_uuid);
} }
=head2 insert_or_update_mac_to_ip
This updates (or inserts) a record in the C<< mac_to_ip >> table used for tracking what MAC addresses have what IP addresses.
If there is an error, an empty string is returned.
B<< NOTE >>: The information is this table IS NOT AUTHORITATIVE! It's generally updated daily, so the information here could be stale.
Parameters;
=head3 mac_to_ip_uuid (optional)
If passed, the column with that specific C<< mac_to_ip_uuid >> will be updated, if it exists.
=head3 mac_to_ip_ip_address (required)
This is the IP address seen in use by the associated C<< mac_to_ip_mac_address >>.
=head3 mac_to_ip_mac_address (required)
This is the MAC address associated with the IP in by C<< mac_to_ip_ip_address >>.
=head3 mac_to_ip_note (optional)
This is a free-form field to store information about the host (like the host name).
=head3 update_note (optional, default '1')
When set to C<< 0 >> and nothing was passed for C<< mac_to_ip_note >>, the note will not be changed if a note exists.
B<< NOTE >>: If C<< mac_to_ip_note >> is set, it will be updated regardless of this parameter.
=cut
sub insert_or_update_mac_to_ip
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_mac_to_ip()" }});
my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $mac_to_ip_uuid = defined $parameter->{mac_to_ip_uuid} ? $parameter->{mac_to_ip_uuid} : "";
my $mac_to_ip_mac_address = defined $parameter->{mac_to_ip_mac_address} ? $parameter->{mac_to_ip_mac_address} : "";
my $mac_to_ip_note = defined $parameter->{mac_to_ip_note} ? $parameter->{mac_to_ip_note} : "";
my $mac_to_ip_ip_address = defined $parameter->{mac_to_ip_ip_address} ? $parameter->{mac_to_ip_ip_address} : "";
my $update_note = defined $parameter->{update_note} ? $parameter->{update_note} : 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
uuid => $uuid,
file => $file,
line => $line,
mac_to_ip_uuid => $mac_to_ip_uuid,
mac_to_ip_mac_address => $mac_to_ip_mac_address,
mac_to_ip_note => $mac_to_ip_note,
mac_to_ip_ip_address => $mac_to_ip_ip_address,
update_note => $update_note,
}});
if (not $mac_to_ip_mac_address)
{
# No user_uuid Throw an error and return.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_mac_to_ip()", parameter => "mac_to_ip_mac_address" }});
return("");
}
if (not $mac_to_ip_ip_address)
{
# No user_uuid Throw an error and return.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_mac_to_ip()", parameter => "mac_to_ip_ip_address" }});
return("");
}
# If the MAC isn't 12 or 17 bytes long (18 being xx:xx:xx:xx:xx:xx), or isn't a valid hex string, abort.
if (((length($mac_to_ip_mac_address) != 12) && (length($mac_to_ip_mac_address) != 17)) or (not $anvil->Validate->is_hex({debug => $debug, string => $mac_to_ip_mac_address, sloppy => 1})))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0096", variables => { mac_to_ip_mac_address => $mac_to_ip_mac_address }});
return("");
}
# If I don't have an mac_to_ip_uuid, try to find one.
if (not $mac_to_ip_uuid)
{
my $query = "
SELECT
mac_to_ip_uuid
FROM
mac_to_ip
WHERE
mac_to_ip_mac_address = ".$anvil->Database->quote($mac_to_ip_mac_address)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
if ($count)
{
$mac_to_ip_uuid = $results->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac_to_ip_uuid => $mac_to_ip_uuid }});
}
}
# If I have an mac_to_ip_uuid, see if an update is needed. If there still isn't an mac_to_ip_uuid, INSERT it.
if ($mac_to_ip_uuid)
{
# Load the old data and see if anything has changed.
my $query = "
SELECT
mac_to_ip_mac_address,
mac_to_ip_ip_address,
mac_to_ip_note
FROM
mac_to_ip
WHERE
mac_to_ip_uuid = ".$anvil->Database->quote($mac_to_ip_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
if (not $count)
{
# I have a mac_to_ip_uuid but no matching record. Probably an error.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0216", variables => { uuid_name => "mac_to_ip_uuid", uuid => $mac_to_ip_uuid }});
return("");
}
foreach my $row (@{$results})
{
my $old_mac_to_ip_mac_address = $row->[0];
my $old_mac_to_ip_ip_address = $row->[1];
my $old_mac_to_ip_note = $row->[2];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_mac_to_ip_mac_address => $old_mac_to_ip_mac_address,
old_mac_to_ip_ip_address => $old_mac_to_ip_ip_address,
old_mac_to_ip_note => $old_mac_to_ip_note,
}});
my $include_note = 1;
if ((not $update_note) && (not $mac_to_ip_note))
{
# Don't evaluate the note. Make the old note empty so it doesn't trigger an
# update below.
$include_note = 0;
$old_mac_to_ip_note = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
include_note => $include_note,
old_mac_to_ip_note => $old_mac_to_ip_note,
}});
}
# Anything change?
if (($old_mac_to_ip_mac_address ne $mac_to_ip_mac_address) or
($old_mac_to_ip_ip_address ne $mac_to_ip_ip_address) or
($old_mac_to_ip_note ne $mac_to_ip_note))
{
# Something changed, save.
my $query = "
UPDATE
mac_to_ip
SET
mac_to_ip_mac_address = ".$anvil->Database->quote($mac_to_ip_mac_address).",
mac_to_ip_ip_address = ".$anvil->Database->quote($mac_to_ip_ip_address).", ";
if ($include_note)
{
$query .= "
mac_to_ip_note = ".$anvil->Database->quote($mac_to_ip_note).", ";
}
$query .= "
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
WHERE
mac_to_ip_uuid = ".$anvil->Database->quote($mac_to_ip_uuid)."
";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Database->write({query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
}
}
else
{
# Save it.
$mac_to_ip_uuid = $anvil->Get->uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac_to_ip_uuid => $mac_to_ip_uuid }});
my $query = "
INSERT INTO
mac_to_ip
(
mac_to_ip_uuid,
mac_to_ip_mac_address,
mac_to_ip_ip_address,
mac_to_ip_note,
modified_date
) VALUES (
".$anvil->Database->quote($mac_to_ip_uuid).",
".$anvil->Database->quote($mac_to_ip_mac_address).",
".$anvil->Database->quote($mac_to_ip_ip_address).",
".$anvil->Database->quote($mac_to_ip_note).",
".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
);
";
$query =~ s/'NULL'/NULL/g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Database->write({query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
return($mac_to_ip_uuid);
}
=head2 insert_or_update_oui =head2 insert_or_update_oui
This updates (or inserts) a record in the C<< oui >> (Organizationally Unique Identifier) table used for converting network MAC addresses to the company that owns it. The C<< oui_uuid >> referencing the database row will be returned. This updates (or inserts) a record in the C<< oui >> (Organizationally Unique Identifier) table used for converting network MAC addresses to the company that owns it. The C<< oui_uuid >> referencing the database row will be returned.
@ -4653,7 +4869,7 @@ WHERE
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_oui_mac_prefix => $old_oui_mac_prefix, old_oui_mac_prefix => $old_oui_mac_prefix,
old_oui_company_address => $old_oui_company_address, old_oui_company_address => $old_oui_company_address,
oui_company_name => $old_oui_company_name, old_oui_company_name => $old_oui_company_name,
}}); }});
# Anything change? # Anything change?

@ -359,7 +359,7 @@ sub download
my $parameter = shift; my $parameter = shift;
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->get_ips()" }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->download()" }});
my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0; my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0;
my $save_to = defined $parameter->{save_to} ? $parameter->{save_to} : ""; my $save_to = defined $parameter->{save_to} ? $parameter->{save_to} : "";

@ -1492,6 +1492,56 @@ CREATE TRIGGER trigger_oui
FOR EACH ROW EXECUTE PROCEDURE history_oui(); FOR EACH ROW EXECUTE PROCEDURE history_oui();
-- It stores a general list of MAC addresses to IP addresses. This may belong to equipment outside the
-- Anvil!, so there's no reference to other tables or a host_uuid.
CREATE TABLE mac_to_ip (
mac_to_ip_uuid uuid not null primary key,
mac_to_ip_mac_address text not null,
mac_to_ip_ip_address text not null,
mac_to_ip_note text not null, -- This is a free-form note, and may contain a server name or host name, if we know it.
modified_date timestamp with time zone not null
);
ALTER TABLE mac_to_ip OWNER TO admin;
CREATE TABLE history.mac_to_ip (
history_id bigserial,
mac_to_ip_uuid uuid,
mac_to_ip_mac_address text,
mac_to_ip_ip_address text,
mac_to_ip_note text,
modified_date timestamp with time zone not null
);
ALTER TABLE history.mac_to_ip OWNER TO admin;
CREATE FUNCTION history_mac_to_ip() RETURNS trigger
AS $$
DECLARE
history_mac_to_ip RECORD;
BEGIN
SELECT INTO history_mac_to_ip * FROM mac_to_ip WHERE mac_to_ip_uuid = new.mac_to_ip_uuid;
INSERT INTO history.mac_to_ip
(mac_to_ip_uuid,
mac_to_ip_mac_address,
mac_to_ip_ip_address,
mac_to_ip_note,
modified_date)
VALUES
(history_mac_to_ip.mac_to_ip_uuid,
history_mac_to_ip.mac_to_ip_mac_address,
history_mac_to_ip.mac_to_ip_ip_address,
history_mac_to_ip.mac_to_ip_note,
history_mac_to_ip.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_mac_to_ip() OWNER TO admin;
CREATE TRIGGER trigger_mac_to_ip
AFTER INSERT OR UPDATE ON mac_to_ip
FOR EACH ROW EXECUTE PROCEDURE history_mac_to_ip();
-- ------------------------------------------------------------------------------------------------------- -- -- ------------------------------------------------------------------------------------------------------- --
-- These are special tables with no history or tracking UUIDs that simply record transient information. -- -- These are special tables with no history or tracking UUIDs that simply record transient information. --
-- ------------------------------------------------------------------------------------------------------- -- -- ------------------------------------------------------------------------------------------------------- --

@ -770,6 +770,13 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0441">The variable: [#!variable!name!#] was expected to be a positive integer, but: [#!variable!value!#] was received.</key> <key name="log_0441">The variable: [#!variable!name!#] was expected to be a positive integer, but: [#!variable!value!#] was received.</key>
<key name="log_0442">The domain: [#!variable!name!#] does not appear to be a valid domain name or an ipv4 IP address. Skipping it.</key> <key name="log_0442">The domain: [#!variable!name!#] does not appear to be a valid domain name or an ipv4 IP address. Skipping it.</key>
<key name="log_0443">The bridge output wasn't in JSON format. Received: [#!variable!output!#].</key> <key name="log_0443">The bridge output wasn't in JSON format. Received: [#!variable!output!#].</key>
<key name="log_0444">
[ Warning ] - Parsed the IP: [#!variable!ip!#] and MAC: [#!variable!mac!#], but something seems wrong. The section in question was:
====
#!variable!section!#
====
</key>
<key name="log_0445">[ NOTE ] - We're about to do a ping scan of: [#!variable!range!#]. This could take a long time, please be patient!</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>
@ -1042,6 +1049,8 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
<key name="job_0063">[ Error ] - Something went wrong. The host name was set to: [#!variable!host_name!#], but the host name returned was: [#!variable!current_host_name!#].</key> <key name="job_0063">[ Error ] - Something went wrong. The host name was set to: [#!variable!host_name!#], but the host name returned was: [#!variable!current_host_name!#].</key>
<key name="job_0064">OUI Database.</key> <key name="job_0064">OUI Database.</key>
<key name="job_0065">Refresh the 'OUI' database used to cross reference MAC addresses to the companies that own them.</key> <key name="job_0065">Refresh the 'OUI' database used to cross reference MAC addresses to the companies that own them.</key>
<key name="job_0066">Network Scan.</key>
<key name="job_0067">This job does a simple ping scan of the networks connected to this host. Any detected hosts have their MAC / IP addresses recorded. This is designed to help determine IP addresses assigned to servers hosted on the Anvil! system.</key>
<!-- Warnings --> <!-- Warnings -->
<key name="striker_warning_0001">The IP address will change. You will need to reconnect after applying these changes.</key> <key name="striker_warning_0001">The IP address will change. You will need to reconnect after applying these changes.</key>
@ -1170,6 +1179,7 @@ Failed to generate an RSA public key for the user: [#!variable!user!#]. The outp
<key name="error_0094">Aborting the download of: [#!variable!url!#] to: [#!variable!save_to!#]. The target file already exists and 'overwrite' was not set.</key> <key name="error_0094">Aborting the download of: [#!variable!url!#] to: [#!variable!save_to!#]. The target file already exists and 'overwrite' was not set.</key>
<key name="error_0095">There was a problem downloading: [#!variable!url!#] to: [#!variable!file!#]. Aborting parsing of the OUI data.</key> <key name="error_0095">There was a problem downloading: [#!variable!url!#] to: [#!variable!file!#]. Aborting parsing of the OUI data.</key>
<key name="error_0096">The 'oui_mac_prefix': [#!variable!oui_mac_prefix!#] string doesn't appear to be a valid 6-byte hex string.</key> <key name="error_0096">The 'oui_mac_prefix': [#!variable!oui_mac_prefix!#] string doesn't appear to be a valid 6-byte hex string.</key>
<key name="error_0097"><![CDATA[The network range: [#!variable!range!#] does not appear to be valid. It must be '<ip>/<subnet>' (subnet can be dotted-decimal or CIDR notation) or be 'bcn', 'sn', 'ifn' or a specific variant like 'bcn1', 'sn2', or 'ifn2'. Alternatively, so not use '--network X' at all and all networks with host is connected to will be scanned.]]></key>
<!-- These are units, words and so on used when displaying information. --> <!-- These are units, words and so on used when displaying information. -->
<key name="unit_0001">Yes</key> <key name="unit_0001">Yes</key>

@ -312,18 +312,32 @@ sub handle_periodic_tasks
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { job_uuid => $job_uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { job_uuid => $job_uuid }});
# Update the OUI data. ### TODO: Restore this when these tools hand jobs properly
($job_uuid) = $anvil->Database->insert_or_update_jobs({ # # Update the OUI data.
file => $THIS_FILE, # ($job_uuid) = $anvil->Database->insert_or_update_jobs({
line => __LINE__, # file => $THIS_FILE,
job_command => $anvil->data->{path}{exe}{'striker-parse-oui'}, # line => __LINE__,
job_data => "", # job_command => $anvil->data->{path}{exe}{'striker-parse-oui'},
job_name => "oui-data::refresh", # job_data => "",
job_title => "job_0064", # job_name => "oui-data::refresh",
job_description => "job_0065", # job_title => "job_0064",
job_progress => 0, # job_description => "job_0065",
}); # job_progress => 0,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { job_uuid => $job_uuid }}); # });
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { job_uuid => $job_uuid }});
#
# # Scan the networks
# ($job_uuid) = $anvil->Database->insert_or_update_jobs({
# file => $THIS_FILE,
# line => __LINE__,
# job_command => $anvil->data->{path}{exe}{'striker-scan-network'},
# job_data => "",
# job_name => "scan-network::refresh",
# job_title => "job_0066",
# job_description => "job_0067",
# job_progress => 0,
# });
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { job_uuid => $job_uuid }});
} }
# Update the next check time. # Update the next check time.

@ -3,6 +3,8 @@
# This periodically reads in http://standards-oui.ieee.org/oui/oui.txt, if possible, and parses it to update/ # This periodically reads in http://standards-oui.ieee.org/oui/oui.txt, if possible, and parses it to update/
# populate the oui database table. # populate the oui database table.
# #
# TODO: * Handle jobs properly
use strict; use strict;
use warnings; use warnings;
use Anvil::Tools; use Anvil::Tools;
@ -127,11 +129,8 @@ sub process_oui
### the real problem is. ### the real problem is.
# The OUI list doesn't include an entry for Red Hat / 52:54:00. So we'll inject it here. # The OUI list doesn't include an entry for Red Hat / 52:54:00. So we'll inject it here.
$oui_body .= " $oui_body .= "
52-54-00 (hex) Red Hat, Inc. 52-54-00 (hex) QEMU Virtual NIC
525400 (base 16) Red Hat, Inc. 525400 (base 16) QEMU Virtual NIC
100 East Davie Street
Raleigh NC 27601
US
EOF EOF
"; ";

@ -0,0 +1,279 @@
#!/usr/bin/perl
#
# This periodically runs a basic ping sweep using nmap to find devices on the given network. As devices are
# found, they may be further processed (ie: to see if a MAC address matches a server to find the IP address
# of a hosted server).
#
# Exit codes;
# 0 = Normal exit
# 1 = No databases available.
# 2 = The '--network X' value is not valid.
#
# TODO: * Support '--dhcp' where, if set, we look up the DHCP range offered by the Striker dashboard(s) and
# scan just the lease range. This should speed up discovery of new/replacement foundation pack
# equipment.
# * Handle jobs properly
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $anvil = Anvil::Tools->new();
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 0});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003"});
$anvil->nice_exit({exit_code => 1});
}
# Do we have a specified network to scan?
$anvil->data->{switches}{network} = "";
$anvil->Get->switches;
scan($anvil);
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
# This scans any networks passed in.
sub scan
{
my ($anvil) = @_;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::network" => $anvil->data->{switches}{network} }});
if ($anvil->data->{switches}{network})
{
# If this network is a specific subnet, scan it. If the network is 'bcn', 'sn', 'ifn' or
# 'bcnX', 'snX', 'snX', find the network on the appropriate interfaces and use it's network.
if ($anvil->data->{switches}{network} =~ /^(\d+.*?)\/(\d+.*)$/)
{
my $ip = $1;
my $subnet = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::network" => $anvil->data->{switches}{network} }});
my $ip_valid = $anvil->Validate->is_ipv4({ip => $ip});
my $subnet_valid = $anvil->Validate->is_ipv4({ip => $subnet});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
ip_valid => $ip_valid,
subnet_valid => $subnet_valid,
}});
if (not $subnet_valid)
{
# Migt be cidr
if (($subnet =~ /^\d+$/) && ($subnet >= 0) && ($subnet <= 32))
{
# Valid CIDR address
$subnet_valid = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { subnet_valid => $subnet_valid }});
}
}
if ((not $ip_valid) or (not $subnet_valid))
{
# Bail out.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0097", variables => { range => $anvil->data->{switches}{network} }});
$anvil->nice_exit({exit_code => 2});
}
else
{
# Scan it!
call_nmap($anvil, $anvil->data->{switches}{network});
}
}
else
{
# If the address is [bc|s|if]n[X], scan it.
$anvil->Network->get_ips();
my $target = "local";
my $to_scan = [];
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$target}{interface}})
{
my $ip = $anvil->data->{network}{$target}{interface}{$interface}{ip};
my $subnet = $anvil->data->{network}{$target}{interface}{$interface}{subnet};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
ip => $ip,
subnet => $subnet,
}});
if (($ip) && ($subnet))
{
# Is this one we're interested in?
my $network_name = $anvil->data->{switches}{network};
if ($interface =~ /^$network_name/)
{
# Yup!
my $network = $anvil->Network->get_network({ip => $ip, subnet => $subnet});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network => $network }});
if ($network)
{
# Scan it.
my $address = $network."/".$subnet;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { address => $address }});
push @{$to_scan}, $address;
}
}
}
}
# Scan what we found
foreach my $address (sort {$a cmp $b} @{$to_scan})
{
call_nmap($anvil, $address);
}
}
}
else
{
# Scan all the networks we have.
$anvil->Network->get_ips();
my $target = "local";
my $to_scan = [];
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$target}{interface}})
{
my $ip = $anvil->data->{network}{$target}{interface}{$interface}{ip};
my $subnet = $anvil->data->{network}{$target}{interface}{$interface}{subnet};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
ip => $ip,
subnet => $subnet,
}});
if (($ip) && ($subnet))
{
my $network = $anvil->Network->get_network({ip => $ip, subnet => $subnet});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network => $network }});
if ($network)
{
# Scan it.
my $address = $network."/".$subnet;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { address => $address }});
push @{$to_scan}, $address;
}
}
}
# Scan what we found
foreach my $address (sort {$a cmp $b} @{$to_scan})
{
call_nmap($anvil, $address);
}
}
return(0);
}
# This calls nmap and parses
sub call_nmap
{
my ($anvil, $address) = @_;
# The subnet can't be dotted decimal, so convert it to CIDR notation, if needed.
my ($ip, $subnet) = ($address =~ /^(\d+.*?)\/(\d.*)$/);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
ip => $ip,
subnet => $subnet,
}});
if ($anvil->Validate->is_ipv4({ip => $subnet}))
{
# Convert to CIDR
my $cidr = $anvil->Convert->cidr({subnet => $subnet});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cidr => $cidr }});
if (($cidr >= 0) && ($cidr <= 32))
{
$address = $ip."/".$cidr;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { address => $address }});
}
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "log_0445", variables => { range => $address }});
my ($nmap_data, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{nmap}." -sn -n ".$address });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
nmap_data => $nmap_data,
return_code => $return_code,
}});
my $this_ip = "";
my $this_mac = "";
my $section = "";
foreach my $line (split/\n/, $nmap_data)
{
$line = $anvil->Words->clean_spaces({'string' => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
$section .= $line."\n";
if ($line =~ /^Nmap scan report for (\d+\.\d+\.\d+\.\d+)$/i)
{
$this_ip = $1;
$this_mac = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:this_mac' => $this_mac,
's2:this_ip' => $this_ip,
}});
}
if ($line =~ /MAC Address: (.*?) \(/)
{
$this_mac = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:this_mac' => $this_mac,
's2:this_ip' => $this_ip,
}});
# Sane?
my $mac_valid = $anvil->Validate->is_hex({string => $this_mac, sloppy => 1});
my $ip_valid = $anvil->Validate->is_ipv4({ip => $this_ip});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
mac_valid => $mac_valid,
ip_valid => $ip_valid,
}});
# Store
if (($mac_valid) && ($ip_valid))
{
my ($mac_to_ip_uuid) = $anvil->Database->insert_or_update_mac_to_ip({
debug => 3,
file => $THIS_FILE,
line => __LINE__,
mac_to_ip_mac_address => $this_mac,
mac_to_ip_ip_address => $this_ip,
update_note => 0,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { mac_to_ip_uuid => $mac_to_ip_uuid }});
}
else
{
# Parse error
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "log_0444", variables => {
ip => $this_ip,
mac => $this_mac,
section => $section,
}});
}
$section = "";
}
}
return(0);
}
Loading…
Cancel
Save