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>
* config.pm: Added options for xprobe2, and autocreate.

View File

@ -2,8 +2,8 @@
##########################################################################
# Pandora FMS Recon Server
##########################################################################
# Copyright (c) 2007 Sancho Lerena, slerena@gmail.com
# Copyright (c) 2007 Artica Soluciones Tecnologicas S.L
# Copyright (c) 2007-2008 Sancho Lerena, slerena@gmail.com
# Copyright (c) 2007-2008 Artica Soluciones Tecnologicas S.L
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -37,6 +37,13 @@ use PandoraFMS::Config;
use PandoraFMS::Tools;
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)
# ENABLED in DEBUGMODE
# DISABLE FOR PRODUCTION
@ -67,8 +74,15 @@ if ( $pa_config{"daemon"} eq "1" ){
&pandora_daemonize ( \%pa_config);
}
# Runs main program (have a infinite loop inside)
threads->new( \&pandora_recon_subsystem, \%pa_config);
# Launch now all plugin threads
# $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);
@ -96,45 +110,110 @@ while ( 1 ){
#------------------------------------------------------------------------------------
#------------------------------------------------------------------------------------
##########################################################################
# 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
sub pandora_recon_producer ($) {
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 $server_id = dame_server_id($pa_config, $pa_config->{'servername'}."_Recon", $dbh);
my $query_sql; # for use in SQL
my $exec_sql; # for use in SQL
my @sql_data; # for use in SQL
while ( 1 ) {
logger ($pa_config, "Loop in Recon Module Subsystem", 10);
$query_sql = "SELECT * FROM trecon_task WHERE id_network_server = $server_id AND status = -1";
$exec_sql = $dbh->prepare($query_sql);
$exec_sql->execute;
while (@sql_data = $exec_sql->fetchrow_array()) {
my $interval = $sql_data[11];
my $my_timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S");
my $my_utimestamp = &UnixDate($my_timestamp, "%s"); # convert from human to integer
my $utimestamp = $sql_data[9];
my $id_task = $sql_data[0];
my $task_name = $sql_data[1];
# Need to exec this task ?
if (($utimestamp + $interval) < $my_utimestamp){
logger($pa_config,"Recon Server: Executing task [$task_name]",8);
# EXEC TASK and mark as "in progress" != -1
pandora_update_reconstatus ($pa_config, $dbh, $id_task, 0);
pandora_recon_exec_task ($pa_config, $id_task);
my $server_id = $pa_config->{'server_id'};
# Initialize variables for posterior usage
my $query_sql;
my @sql_data1;
my $data_id_task;
my $exec_sql1;
while (1) {
$query_sql = "SELECT * FROM trecon_task
WHERE
id_network_server = $server_id
AND
status = -1
AND
(utimestamp + interval_sweep) < UNIX_TIMESTAMP()
";
$exec_sql1 = $dbh->prepare($query_sql);
$exec_sql1 ->execute;
while (@sql_data1 = $exec_sql1->fetchrow_array()) {
$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"});
} # 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)
# Detect OS using xprobe2 tool. Return tconfig_os id code.
@ -146,9 +225,15 @@ sub pandora_detect_os {
my $xprobe2 = $pa_config->{"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);
}
##########################################################################
@ -158,14 +243,15 @@ sub pandora_detect_os {
sub pandora_recon_exec_task {
my $pa_config = $_[0];
my $id_task = $_[1];
my $dbh = $_[2];
my $target_ip; # Real ip to check
my @ip2; # temp array for NetAddr::IP
my $space; # temp var to store space of ip's for netaddr::ip
my $query_sql; # for use in SQL
my $exec_sql; # 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";
$exec_sql = $dbh->prepare($query_sql);
$exec_sql ->execute;
@ -209,20 +295,21 @@ sub pandora_recon_exec_task {
$space++; $position++;
$add_host = 0;
# Check ICMP for this IP
if (($task_type == 1) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 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){
# Is this IP listed for any agent ?
if (pandora_check_ip ($pa_config, $dbh, $target_ip) == 0){
# Check ICMP for this IP
if (($task_type == 1) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)){
$add_host = 1;
}
}
if ($add_host == 1){
# Is this IP listed for any agent ?
if (pandora_check_ip ($pa_config, $dbh, $target_ip) == 0){
# 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){
$host_found ++;
my $target_ip_resolved = resolv_ip2name($target_ip);
$list_ip = $list_ip." ".$target_ip;
@ -248,7 +335,8 @@ sub pandora_recon_exec_task {
}
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
# Create incident
@ -276,27 +364,27 @@ sub scan_icmp {
my $l_timeout = $_[1];
# temporal vars.
my $result = 0;
my $result2 = 0;
my $p;
# Check for valid destination
if (!defined($dest)) {
return 0;
}
# Thread safe
# 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);
$result = $p->ping($dest);
$result2 = $p->ping($dest);
{
$p = Net::Ping->new("icmp",$l_timeout,16);
$p->source_verify(1);
$result = $p->ping($dest);
}
# Check for valid result
if ((!defined($result)) || (!defined($result2))) {
if (!defined($result)) {
return 0;
}
# Lets see the result
if (($result == 1) && ($result2 == 1)) {
if ($result == 1) {
$p->close();
return 1;
} else {
@ -326,6 +414,9 @@ sub scan_tcp {
}
alarm 0;
};
if ($@){
return 0;
}
return $opened;
}
@ -530,14 +621,7 @@ sub pandora_getparent ($$){
);
my $success = 0;
eval {
alarm $pa_config->{"networktimeout"};
$success = $t->traceroute();
alarm 0;
};
if ($@){
return 0;
}
$success = $t->traceroute();
if ($t->hops > 1){
if ($success){
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
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->{"snmp_proc_deadresponse"} = 0; # Introduced on 1.3.1 10 Feb08
$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->{"plugin_timeout"} = 5; # 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->{'autocreate_group'} = 2;
$pa_config->{'autocreate'} = 1;
$pa_config{'recon_threads'} = 3;
# Check for UID0
if ($pa_config->{"quiet"} != 0){
@ -397,6 +399,9 @@ sub pandora_loadconfig {
elsif ($parametro =~ m/^autocreate_group\s([0-9*]*)/i) {
$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 #

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
##########################################################################
@ -1001,9 +1002,9 @@ sub module_generic_data_string (%$$$$$) {
my $m_data = $datos->{data}->[0];
my $a_desc = $datos->{description}->[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") {
$m_data = XMLout($m_data, RootName=>undef);
$m_data = XMLout($m_data, RootName=>undef);
}
if (ref($a_max) eq "HASH") {
$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
##########################################################################
sub pandora_writedata (%$$$$$$$$$$){
my $pa_config = $_[0];
my $timestamp = $_[1];
my $nombre_agente = $_[2];
my $tipo_modulo = $_[3];
my $nombre_modulo = $_[4];
my $datos = $_[5];
my $max = $_[6];
my $timestamp = $_[1];
my $nombre_agente = $_[2];
my $tipo_modulo = $_[3];
my $nombre_modulo = $_[4];
my $datos = $_[5];
my $max = $_[6];
my $min = $_[7];
my $descripcion = $_[8];
my $dbh = $_[9];

View File

@ -41,8 +41,25 @@ our @EXPORT = qw(
pandora_create_agent
pandora_get_os
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
## Write in internal audit system an entry.
@ -157,7 +174,7 @@ sub pandora_create_agent {
my $server = $pa_config->{'servername'}.$pa_config->{"servermode"};
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);
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);
print "\n";
}