2008-06-18 Sancho Lerena <slerena@gmail.com>

* pandora_dbstress.pl: Added suppor for "text" moduletypes.
    
    * pandora_recon: Now is multithreaded. Better management of already
    registered IP's. Better status update. Some fixes (xprobe2 crash).

    * Config.pm: Added support for recon_threads token.

    * Tools.pm: added pandora_trash_ascii(), autocreate
    set agent in learning mode automatically.



git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@879 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f
This commit is contained in:
slerena 2008-06-19 00:14:17 +00:00
parent b8428c482c
commit c76414caeb
7 changed files with 221 additions and 80 deletions

View File

@ -1,3 +1,15 @@
2008-06-18 Sancho Lerena <slerena@gmail.com>
* pandora_dbstress.pl: Added suppor for "text" moduletypes.
* pandora_recon: Now is multithreaded. Better management of already
registered IP's. Better status update. Some fixes (xprobe2 crash).
* Config.pm: Added support for recon_threads token.
* Tools.pm: added pandora_trash_ascii(), autocreate
set agent in learning mode automatically.
2008-06-17 Sancho Lerena <slerena@artica.es> 2008-06-17 Sancho Lerena <slerena@artica.es>
* config.pm: Added options for xprobe2, and autocreate. * config.pm: Added options for xprobe2, and autocreate.

View File

@ -2,8 +2,8 @@
########################################################################## ##########################################################################
# Pandora FMS Recon Server # Pandora FMS Recon Server
########################################################################## ##########################################################################
# Copyright (c) 2007 Sancho Lerena, slerena@gmail.com # Copyright (c) 2007-2008 Sancho Lerena, slerena@gmail.com
# Copyright (c) 2007 Artica Soluciones Tecnologicas S.L # Copyright (c) 2007-2008 Artica Soluciones Tecnologicas S.L
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@ -37,6 +37,13 @@ use PandoraFMS::Config;
use PandoraFMS::Tools; use PandoraFMS::Tools;
use PandoraFMS::DB; use PandoraFMS::DB;
# Queue management
my @pending_task : shared;
my %pending_task_hash : shared;
my %current_task_hash : shared;
my $queue_lock : shared;
# FLUSH in each IO (only for debug, very slooow) # FLUSH in each IO (only for debug, very slooow)
# ENABLED in DEBUGMODE # ENABLED in DEBUGMODE
# DISABLE FOR PRODUCTION # DISABLE FOR PRODUCTION
@ -67,8 +74,15 @@ if ( $pa_config{"daemon"} eq "1" ){
&pandora_daemonize ( \%pa_config); &pandora_daemonize ( \%pa_config);
} }
# Runs main program (have a infinite loop inside) # Launch now all plugin threads
threads->new( \&pandora_recon_subsystem, \%pa_config); # $ax is local thread id for this server
for (my $ax=0; $ax < $pa_config{'recon_threads'}; $ax++){
threads->new( \&pandora_recon_consumer, \%pa_config, $ax);
}
# Launch now the producer thread
threads->new( \&pandora_recon_producer, \%pa_config);
sleep(1); sleep(1);
@ -96,45 +110,110 @@ while ( 1 ){
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
########################################################################## sub pandora_recon_producer ($) {
# SUB pandora_recon_subsystem
# This module runs each X seconds (server threshold) checking for new
# recon tasks pending to do
##########################################################################
sub pandora_recon_subsystem {
# Init vars
my $pa_config = $_[0]; my $pa_config = $_[0];
print " [*] Starting up Recon Producer Thread ...\n";
my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 });
my $server_id = dame_server_id($pa_config, $pa_config->{'servername'}."_Recon", $dbh); my $server_id = $pa_config->{'server_id'};
my $query_sql; # for use in SQL
my $exec_sql; # for use in SQL # Initialize variables for posterior usage
my @sql_data; # for use in SQL my $query_sql;
while ( 1 ) { my @sql_data1;
logger ($pa_config, "Loop in Recon Module Subsystem", 10); my $data_id_task;
$query_sql = "SELECT * FROM trecon_task WHERE id_network_server = $server_id AND status = -1"; my $exec_sql1;
$exec_sql = $dbh->prepare($query_sql);
$exec_sql->execute; while (1) {
while (@sql_data = $exec_sql->fetchrow_array()) { $query_sql = "SELECT * FROM trecon_task
my $interval = $sql_data[11]; WHERE
my $my_timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); id_network_server = $server_id
my $my_utimestamp = &UnixDate($my_timestamp, "%s"); # convert from human to integer AND
my $utimestamp = $sql_data[9]; status = -1
my $id_task = $sql_data[0]; AND
my $task_name = $sql_data[1]; (utimestamp + interval_sweep) < UNIX_TIMESTAMP()
# Need to exec this task ? ";
if (($utimestamp + $interval) < $my_utimestamp){
logger($pa_config,"Recon Server: Executing task [$task_name]",8); $exec_sql1 = $dbh->prepare($query_sql);
# EXEC TASK and mark as "in progress" != -1 $exec_sql1 ->execute;
pandora_update_reconstatus ($pa_config, $dbh, $id_task, 0); while (@sql_data1 = $exec_sql1->fetchrow_array()) {
pandora_recon_exec_task ($pa_config, $id_task); $data_id_task = $sql_data1[0];
# Skip modules already queued
if ((!defined($pending_task_hash{$data_id_task})) &&
(!defined($current_task_hash{$data_id_task}))) {
pandora_update_reconstatus ($pa_config, $dbh, $data_id_task, 0);
# Locking scope, do not remove redundant { }
{
lock $queue_lock;
push (@pending_task, $data_id_task);
$pending_task_hash {$data_id_task}=1;
}
} }
} }
$exec_sql->finish(); $exec_sql1->finish();
threads->yield;
sleep($pa_config->{"server_threshold"}); sleep($pa_config->{"server_threshold"});
} # Main loop
}
##########################################################################
# SUB pandora_recon_consumer
# This module runs each X seconds (server threshold) checking for recon task
##########################################################################
sub pandora_recon_consumer ($$) {
my $pa_config = $_[0];
my $thread_id = $_[1];
if ($pa_config->{"quiet"} == 0){
print " [*] Starting up Recon Consumer Thread # $thread_id \n";
}
my $data_id_task;
# Create Database handler
my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 });
my $counter =0;
LOOP: while (1) {
if ($counter > 10) {
threads->yield;
$counter = 0;
}
# Take the first element on the shared queue
# Insert this element on the current task hash
{
lock $queue_lock;
if (scalar(@pending_task) == 0){
$counter++;
next LOOP;
}
$data_id_task = shift(@pending_task);
delete($pending_task_hash{$data_id_task});
$current_task_hash{$data_id_task}=1;
}
# Executing recon task with unmanaged error trapping
eval {
pandora_recon_exec_task ($pa_config, $data_id_task, $dbh);
};
if ($@){
logger ($pa_config, "[ERROR] Recon Task for Task ID $data_id_task causes a system exception", 0);
logger ($pa_config, "ERROR Code: $@", 1);
}
# Remove from queue. If catch an error, probably data is
# not been processed, but has been freed from task queue
{
lock $queue_lock;
delete($current_task_hash{$data_id_task});
}
$counter = 0;
threads->yield;
} }
} }
########################################################################## ##########################################################################
# SUB pandora_detect_os (paconfig, host) # SUB pandora_detect_os (paconfig, host)
# Detect OS using xprobe2 tool. Return tconfig_os id code. # Detect OS using xprobe2 tool. Return tconfig_os id code.
@ -146,9 +225,15 @@ sub pandora_detect_os {
my $xprobe2 = $pa_config->{"xprobe2"}; my $xprobe2 = $pa_config->{"xprobe2"};
if (! -e $xprobe2){ if (! -e $xprobe2){
return 10; return 10; # other
} }
my $command = `$xprobe2 $host 2> /dev/null | grep "Running OS" | head -1`; my $command= "";
eval {
$command = `$xprobe2 $host 2> /dev/null | grep "Running OS" | head -1`;
};
if ($@){
return 10;
}
return pandora_get_os ($command); return pandora_get_os ($command);
} }
########################################################################## ##########################################################################
@ -158,13 +243,14 @@ sub pandora_detect_os {
sub pandora_recon_exec_task { sub pandora_recon_exec_task {
my $pa_config = $_[0]; my $pa_config = $_[0];
my $id_task = $_[1]; my $id_task = $_[1];
my $dbh = $_[2];
my $target_ip; # Real ip to check my $target_ip; # Real ip to check
my @ip2; # temp array for NetAddr::IP my @ip2; # temp array for NetAddr::IP
my $space; # temp var to store space of ip's for netaddr::ip my $space; # temp var to store space of ip's for netaddr::ip
my $query_sql; # for use in SQL my $query_sql; # for use in SQL
my $exec_sql; # for use in SQL my $exec_sql; # for use in SQL
my @sql_data; # for use in SQL my @sql_data; # for use in SQL
my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 });
$query_sql = "SELECT * FROM trecon_task WHERE id_rt = $id_task"; $query_sql = "SELECT * FROM trecon_task WHERE id_rt = $id_task";
$exec_sql = $dbh->prepare($query_sql); $exec_sql = $dbh->prepare($query_sql);
@ -209,20 +295,21 @@ sub pandora_recon_exec_task {
$space++; $position++; $space++; $position++;
$add_host = 0; $add_host = 0;
# Check ICMP for this IP # Is this IP listed for any agent ?
if (($task_type == 1) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)){ if (pandora_check_ip ($pa_config, $dbh, $target_ip) == 0){
$add_host = 1;
} # Check ICMP for this IP
# Check TCP port for this IP if (($task_type == 1) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)){
elsif (($task_type == 2) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)) {
if (scan_tcp ($target_ip, $pa_config->{'networktimeout'}, $extended_value) == 1){
$add_host = 1; $add_host = 1;
} }
} # Check TCP port for this IP
elsif (($task_type == 2) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)) {
if (scan_tcp ($target_ip, $pa_config->{'networktimeout'}, $extended_value) == 1){
$add_host = 1;
}
}
if ($add_host == 1){ if ($add_host == 1){
# Is this IP listed for any agent ?
if (pandora_check_ip ($pa_config, $dbh, $target_ip) == 0){
$host_found ++; $host_found ++;
my $target_ip_resolved = resolv_ip2name($target_ip); my $target_ip_resolved = resolv_ip2name($target_ip);
$list_ip = $list_ip." ".$target_ip; $list_ip = $list_ip." ".$target_ip;
@ -249,6 +336,7 @@ sub pandora_recon_exec_task {
my $progress = ceil($position / ($total_hosts / 100)); my $progress = ceil($position / ($total_hosts / 100));
pandora_update_reconstatus ($pa_config, $dbh, $id_task, $progress); pandora_update_reconstatus ($pa_config, $dbh, $id_task, $progress);
pandora_task_set_utimestamp ($pa_config, $dbh, $id_task);
} while ($space < $space->broadcast); # fin del buclie principal de iteracion de Ips } while ($space < $space->broadcast); # fin del buclie principal de iteracion de Ips
# Create incident # Create incident
@ -276,27 +364,27 @@ sub scan_icmp {
my $l_timeout = $_[1]; my $l_timeout = $_[1];
# temporal vars. # temporal vars.
my $result = 0; my $result = 0;
my $result2 = 0;
my $p; my $p;
# Check for valid destination # Check for valid destination
if (!defined($dest)) { if (!defined($dest)) {
return 0; return 0;
} }
# Thread safe
# Some hosts don't accept ICMP with too small payload. Use 16 Bytes # Some hosts don't accept ICMP with too small payload. Use 16 Bytes
$p = Net::Ping->new("icmp",$l_timeout,16); {
$p->source_verify(1); $p = Net::Ping->new("icmp",$l_timeout,16);
$p->source_verify(1);
$result = $p->ping($dest); $result = $p->ping($dest);
$result2 = $p->ping($dest); }
# Check for valid result # Check for valid result
if ((!defined($result)) || (!defined($result2))) { if (!defined($result)) {
return 0; return 0;
} }
# Lets see the result # Lets see the result
if (($result == 1) && ($result2 == 1)) { if ($result == 1) {
$p->close(); $p->close();
return 1; return 1;
} else { } else {
@ -326,6 +414,9 @@ sub scan_tcp {
} }
alarm 0; alarm 0;
}; };
if ($@){
return 0;
}
return $opened; return $opened;
} }
@ -530,14 +621,7 @@ sub pandora_getparent ($$){
); );
my $success = 0; my $success = 0;
eval { $success = $t->traceroute();
alarm $pa_config->{"networktimeout"};
$success = $t->traceroute();
alarm 0;
};
if ($@){
return 0;
}
if ($t->hops > 1){ if ($t->hops > 1){
if ($success){ if ($success){
my $parent_ip = $t->hop_query_host($t->hops-1,0); my $parent_ip = $t->hop_query_host($t->hops-1,0);

View File

@ -176,3 +176,7 @@ autocreate_group 2
# set to 0 to disable # set to 0 to disable
autocreate 1 autocreate 1
# recon_threads (3 by default)
recon_threads 3

View File

@ -175,6 +175,7 @@ sub pandora_loadconfig {
$pa_config->{"tcp_timeout"} = 20; # Introduced on 1.3.1 $pa_config->{"tcp_timeout"} = 20; # Introduced on 1.3.1
$pa_config->{"snmp_proc_deadresponse"} = 0; # Introduced on 1.3.1 10 Feb08 $pa_config->{"snmp_proc_deadresponse"} = 0; # Introduced on 1.3.1 10 Feb08
$pa_config->{"plugin_threads"} = 3; # Introduced on 2.0 $pa_config->{"plugin_threads"} = 3; # Introduced on 2.0
$pa_config->{"recon_threads"} = 3; # Introduced on 2.0
$pa_config->{"prediction_threads"} = 3; # Introduced on 2.0 $pa_config->{"prediction_threads"} = 3; # Introduced on 2.0
$pa_config->{"plugin_timeout"} = 5; # Introduced on 2.0 $pa_config->{"plugin_timeout"} = 5; # Introduced on 2.0
$pa_config->{"wmi_threads"} = 3; # Introduced on 2.0 $pa_config->{"wmi_threads"} = 3; # Introduced on 2.0
@ -194,6 +195,7 @@ sub pandora_loadconfig {
$pa_config->{"xprobe2"} = "/usr/bin/xprobe2"; $pa_config->{"xprobe2"} = "/usr/bin/xprobe2";
$pa_config->{'autocreate_group'} = 2; $pa_config->{'autocreate_group'} = 2;
$pa_config->{'autocreate'} = 1; $pa_config->{'autocreate'} = 1;
$pa_config{'recon_threads'} = 3;
# Check for UID0 # Check for UID0
if ($pa_config->{"quiet"} != 0){ if ($pa_config->{"quiet"} != 0){
@ -397,6 +399,9 @@ sub pandora_loadconfig {
elsif ($parametro =~ m/^autocreate_group\s([0-9*]*)/i) { elsif ($parametro =~ m/^autocreate_group\s([0-9*]*)/i) {
$pa_config->{'autocreate_group'}= clean_blank($1); $pa_config->{'autocreate_group'}= clean_blank($1);
} }
elsif ($parametro =~ m/^recon_threads\s([0-9]*)/i) {
$pa_config->{'recon_threads'}= clean_blank($1);
}
} # end of loop for parameter # } # end of loop for parameter #

View File

@ -560,7 +560,8 @@ sub execute_alert (%$$$$$$$$$$$$$$$) {
########################################################################## ##########################################################################
## SUB pandora_writestate (pa_config, nombre_agente,tipo_modulo,nombre_modulo,valor_datos, estado) ## SUB pandora_writestate (pa_config, nombre_agente,tipo_modulo,
# nombre_modulo,valor_datos, estado, dbh, needupdate)
## Alter data, chaning status of modules in state table ## Alter data, chaning status of modules in state table
########################################################################## ##########################################################################
@ -1001,9 +1002,9 @@ sub module_generic_data_string (%$$$$$) {
my $m_data = $datos->{data}->[0]; my $m_data = $datos->{data}->[0];
my $a_desc = $datos->{description}->[0]; my $a_desc = $datos->{description}->[0];
my $a_max = $datos->{max}->[0]; my $a_max = $datos->{max}->[0];
my $a_min = $datos->{min}->[0]; my $a_min = $datos->{min}->[0];
if (ref($m_data) eq "HASH") { if (ref($m_data) eq "HASH") {
$m_data = XMLout($m_data, RootName=>undef); $m_data = XMLout($m_data, RootName=>undef);
} }
if (ref($a_max) eq "HASH") { if (ref($a_max) eq "HASH") {
$a_max = ""; $a_max = "";
@ -1018,19 +1019,20 @@ sub module_generic_data_string (%$$$$$) {
########################################################################## ##########################################################################
## SUB pandora_writedata (pa_config, timestamp,nombre_agente,tipo_modulo,nombre_modulo,datos) ## SUB pandora_writedata (pa_config, timestamp,nombre_agente,tipo_modulo,
# nombre_modulo, datos, max, min, descripcion, dbh, update)
## Insert data in main table: tagente_datos ## Insert data in main table: tagente_datos
########################################################################## ##########################################################################
sub pandora_writedata (%$$$$$$$$$$){ sub pandora_writedata (%$$$$$$$$$$){
my $pa_config = $_[0]; my $pa_config = $_[0];
my $timestamp = $_[1]; my $timestamp = $_[1];
my $nombre_agente = $_[2]; my $nombre_agente = $_[2];
my $tipo_modulo = $_[3]; my $tipo_modulo = $_[3];
my $nombre_modulo = $_[4]; my $nombre_modulo = $_[4];
my $datos = $_[5]; my $datos = $_[5];
my $max = $_[6]; my $max = $_[6];
my $min = $_[7]; my $min = $_[7];
my $descripcion = $_[8]; my $descripcion = $_[8];
my $dbh = $_[9]; my $dbh = $_[9];

View File

@ -41,8 +41,25 @@ our @EXPORT = qw(
pandora_create_agent pandora_create_agent
pandora_get_os pandora_get_os
pandora_event pandora_event
pandora_trash_ascii
); );
##########################################################################
## SUB pandora_trash_ascii
# Generate random ascii strings with variable lenght
##########################################################################
sub pandora_trash_ascii {
my $config_depth = $_[0];
my $a;
my $output;
for ($a=0;$a<$config_depth;$a++){
$output = $output.chr(int(rand(25)+97));
}
return $output
}
########################################################################## ##########################################################################
## SUB pandora_event ## SUB pandora_event
## Write in internal audit system an entry. ## Write in internal audit system an entry.
@ -157,7 +174,7 @@ sub pandora_create_agent {
my $server = $pa_config->{'servername'}.$pa_config->{"servermode"}; my $server = $pa_config->{'servername'}.$pa_config->{"servermode"};
logger($pa_config,"$server: Creating agent $name $target_ip ", 1); logger($pa_config,"$server: Creating agent $name $target_ip ", 1);
my $query_sql2 = "INSERT INTO tagente (nombre, direccion, comentarios, id_grupo, id_os, id_network_server, intervalo, id_parent) VALUES ('$name', '$target_ip', 'Created by $server', $id_group, $id_os, $id_server, 300, $id_parent)"; my $query_sql2 = "INSERT INTO tagente (nombre, direccion, comentarios, id_grupo, id_os, id_network_server, intervalo, id_parent, modo) VALUES ('$name', '$target_ip', 'Created by $server', $id_group, $id_os, $id_server, 300, $id_parent, 1)";
$dbh->do ($query_sql2); $dbh->do ($query_sql2);
my $lastid = $dbh->{'mysql_insertid'}; my $lastid = $dbh->{'mysql_insertid'};

View File

@ -197,6 +197,23 @@ sub process_module(){
} }
} }
# Generate pseudo-random data for boolean data
if ( $target_name =~ /text/i ){
for ($a=1;$a<$iterations;$a++){
$valor = pandora_trash_ascii (rand(100)+50);
$m_timestamp = DateCalc($m_timestamp,"+ $target_interval seconds",\$err);
$mysql_date = &UnixDate($m_timestamp,"%Y-%m-%d %H:%M:%S");
if ($a % 20 eq 0) {
print "\r -> ".int($a / ($iterations / 100))."% generated for ($target_name) ";
}
pandora_lastagentcontact($pa_config, $mysql_date, $agent_name, "none","1.2", $target_interval, $dbh);
#print LOG $mysql_date, $target_name, $valor, "\n";
pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos);
pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos);
}
}
close (LOG); close (LOG);
print "\n"; print "\n";
} }