nfsen ile birlikte netflow verilerinin analizi ve bu analiz sonucu istenilen ip’lerin blackhole sistemi kullanılarak engellenmesi yönündeki uygulama için kullanılan programlar aşağıda yer almaktadır.
network-monitor.pm ile nfsen 5dk aralıklar ile filters tablosu içerisindeki nfdump filtrelerini çalıştırır. Buradaki süre aslında nfcapd özetleme aralığıdır. Nfsen kaynak eklenirken opt değişkeni kullanılarak, her kaynak için kullanılan nfcand özetleme aralığı değiştirilebilir (nfcapd man bakıldığında -t değişkeni interval değiştirmek için kullanılmaktadır). -t 10 ile 10 sn vb yapılabilir.
network-monitor.pm kullandığı filter tablosuna istenilen filtreler eklenebilir. Şu an için 1 tip filtre vardır. Buda nfdump ve mevcut timeslot üzerinde, filtre ,pps>100 and proto udp gibi, ile birlikte verilen options, -n 100 -A dstip gibi, çalıştırarak sonuç içerisinde bir ip adresi arar. Eğer blackhole seçeneği filtre ile birlikte seçilmiş ise blackholeprefix tablosuna ekler.
blacholeprefix tablosunda her bir prefix’in default bir penalty değeri vardır. Bu penalty değeri * 5 dakika (nfcapd özetleme aralığı, eğer farklı bir süre kullanılıyor ise ona ayarlanmalıdır ) boyunca prefix tabloda aktif olarak yer alır. Bu süre bittikden sonra pasif’e geçer ve bir sonraki timeslot’da da listeden silinir.
Ceza sisteminin özet işleyişi :
time 0 5 10 15 20 25 30 dakika gibi devam ettiğini var sayalım. NFSEN 5dk aralıklar ile çalışıyor.
time = 0
veri tabanı (boş):
prefix | status | penalty | timestamp
time = 5 pref X veri tablosuna ekleniyor.
foreach pref :
selec pref :
* not defined : add pref , status = A penalty= 1 timestamp = currenttimestamp
if defined : update pref, status = A penalty=penalty + 1 timestamp = 15
veri tabanı :
prefix | status | penalty | timestamp
X | A | 1 | 5
update status = P where timestamp = currenttimestamp – (penalty * 5) # X için 5 != 5-5 eşleşmez
delete where timestamp < currenttimestamp – (penalty * 5) # X için 5 !< 5 – 5 eşleşmez
veri tabanı :
prefix | status | penalty | timestamp
X | A | 1 | 5
time = 10 herhangi bir prefix eklenmiyor
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
if defined : update pref, status = A penalty=penalty + 1 timestamp = currenttimestamp
* update status = P where timestamp = currenttimestamp – (penalty * 5) # X için 5 = 10 – (1*5) eşleşir
veri tabanı :
prefix | status | penalty | timestamp
X | P | 1 | 5
delete where timestamp < currenttimestamp – (penalty * 5) # X için 5 !< 10 – 5 eşleşmez
veri tabanı :
prefix | status | penalty | timestamp
X | P | 1 | 5
time = 15 pref X veri tablosuna ekleniyor.
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
* if defined : update pref, status = A penalty=penalty + 1 timestamp = 15
veri tabanı :
prefix | status | penalty | timestamp
X | A | 2 | 15
update status = P where timestamp = currenttimestamp – (penalty * 5) # X için 15 != 15 – (2*5) eşleşmez
delete where timestamp < currenttimestamp – (penalty * 5) # X için 15 !< 15 – (2*5) eşleşmez
veri tabanı :
prefix | status | penalty | timestamp
X | A | 2 | 15
time = 20 yok
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
if defined : update pref, status = A penalty=penalty + 1 timestamp = 15
* update status = P where timestamp = currenttimestamp – (penalty * 5) # X için 15 != 20 – (2*5) eşleşmez
delete where timestamp < currenttimestamp – (penalty * 5) # X için 15 !< 20 – (2*5) eşleşmez
veri tabanı :
prefix | status | penalty | timestamp
X | A | 2 | 15
time = 25 yok
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
if defined : update pref, status = A penalty=penalty + 1 timestamp = 15
* update status = P where timestamp = currenttimestamp – (penalty * 5) # X için 15 = 25 – (2*5) eşleşir
delete where timestamp < currenttimestamp – (penalty * 5) # X için 15 !< 25 – (2*5) eşleşmez
veri tabanı :
prefix | status | penalty | timestamp
X | P | 2 | 15
time = 30 pref X veri tablosuna ekleniyor.
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
* if defined : update pref, status = A penalty=penalty + 1 timestamp = 30
veri tabanı :
prefix | status | penalty | timestamp
X | A | 3 | 30
update status = P where timestamp = currenttimestamp – (penalty * 5) # X için 30 != 30 – (3*5) eşleşmez
delete where timestamp < currenttimestamp – (penalty * 5) # X için 30 !< 30 – (3*5) eşleşmez
veri tabanı :
prefix | status | penalty | timestamp
X | A | 3 | 30 # penalty 3, 3X5 15 + currenttimestamp = 15+30 = 45’e kadar X A olarak kalır.
time = 45 yok
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
if defined : update pref, status = A penalty=penalty + 1 timestamp = 15
update status = P where timestamp = currenttimestamp – (penalty * 5)
P where 30 = 45 – 15 (time 5 de eklenenler) 30=30 Dolayısı ile bizimki etkilenir.
veri tabanı :
prefix | status | penalty | timestamp
X | P | 3 | 30
time = 50 yok
foreach pref :
selec pref :
not defined : add pref , status = A penalty= 1 timestamp = 5
if defined : update pref, status = A penalty=penalty + 1 timestamp = 15
update status = P where timestamp = currenttimestamp – (penalty * 5)
P where 30 = 50 – 15 (time 5 de eklenenler) 30!=45 Dolayısı ile bizimki etkilenmez.
delete where timestamp < currenttimestamp – (penalty * 5)
30 < 45
veri tabanı :
prefix | status | penalty | timestamp
blackhole.bgp : script’i ile blackhole prefixes tablosunda yer alan ve aktif olan prefilxler /32 olarak network’e anons edilir. Bu şekilde prefix yasaklanmış olur. BGP için golang tabanlı BGP sunucu kullanımasını öneririm. http://monsterdark.com/using-gobgp-as-a-go-native-bgp-library-with-sql-connect-to-get-routes/
network-monitor.pm : nfsen için kullanılan plugin. Live profili üzerinde çalışır. nfsen.conf –>
@plugins = (
# profile # module
[ ‘live’, ‘network_monitor’],
);
ddd.pm plugini temel alarak değiştirilmiştir. Filters tablosu değiştirilmiştir. Tek bir adet filtre tipi uygulama içinde tanımlanmıştır. Filtre sonucu çıkan ip’leri otomatik olarak blackhole sistemine eklenmesi için seçenek eklenmiştir (filtrenin bir ip adresi dönmesi beklenmektedir). Buradaki eşleme işlemi tüm output üzerinde regexp çalıştırılarak yapılır. Aslında ouput formatını csv çeverirek ve bu csv hash üzerinde eşleme işlemi yapmak daha uygun olabilir.
Ayrıca blackhole tablosunun periyodik olarak bakımı, prefix silinmesi ve pasife alınması gibi işlemler içinde bir alt program eklenmiştir.
- default-email alanlarını ekleyin.
- veri tabanı bilgilerini ekleyin.
#!/opt/perl5.10.1/bin/perl
package network_monitor;
use strict; # no cheating
use Sys::Syslog; # used for logging
use DBD::mysql;
# NFSen modules
use NfProfile;
use NfConf;
use Notification;
### database login info
my $dsn = ‘dbi:mysql:network_monitor:127.0.0.1:3306’;
my $user = ‘sqluser’;
my $pass = ‘sqlpass’;
# setup logging
Sys::Syslog::setlogsock(‘unix’);
our %cmd_lookup =
(
‘try’ => \&RunProc,
);
# Identify ourselves as an NFSen 1.3.x plugin
our $VERSION = 130;
my $EODATA = “.\n”;
my ( $nfdump, $PROFILEDIR );
# Debug mode
# 0 = false, 1 = true
my $DEBUG = 0;
my $defaultemails = “”;
# Specify the notification email(s).
if ($DEBUG > 0)
{
$defaultemails = “”;
}
else
{
$defaultemails = “”;
}
# Init alert levels
my $alert = 0;
#TODO: remove
# Set the alert files
# these are imported into the nfsen index page via a php include
# displays an annoying list of time windows for active alerts
my $alertfile = “/tmp/alert.txt”;
my $alerttext = “/tmp/alert-text.txt”;
# beginning of the subject line for alerts
my $defaultsubject = “Network Monitor Alert”;
if ($DEBUG > 0)
{
$defaultsubject = ‘[DEBUG]’.$defaultsubject;
}
sub RunProc
{
my $socket = shift; # scalar
my $opts = shift; # reference to a hash
# error checking example
if ( !exists $$opts{‘colours’} )
{
Nfcomm::socket_send_error($socket, “Missing value”);
return;
}
# retrieve values passed by frontend
# two scalars
my $colour1 = $$opts{‘colour1’};
my $colour2 = $$opts{‘colour2’};
# one array as arrayref
my $colours = $$opts{‘colours’};
my @othercolours = ( ‘red’, ‘blue’ );
# Prepare answer
my %args;
$args{‘string’} = “Greetings from backend plugin. Got colour values: ‘$colour1’ and ‘$colour2′”;
$args{‘othercolours’} = \@othercolours;
Nfcomm::socket_send_ok($socket, \%args);
}
#
# Periodic data processing function
# input: hash reference including the items:
# ‘profile’ profile name
# ‘profilegroup’ profile group
# ‘timeslot’ time of slot to process: Format yyyymmddHHMM e.g. 200503031200
sub run
{
my $dbh = DBI->connect($dsn, $user, $pass);
if (!$dbh)
{
my @mailbody=”$DBI::errstr”;
syslog(‘err’, “nfsen network_monitor plugin: network monitor script can’t connect to the DB, error is $DBI::errstr'”);
die “Can’t connect to the DB: $DBI::errstr\n”;
}
my $argref = shift;
my $profile = $$argref{‘profile’};
my $profilegroup = $$argref{‘profilegroup’};
my $timeslot = $$argref{‘timeslot’};
syslog (‘info’, “nfsen network_monitor plugin: —————————————————————————————————-“);
syslog(‘info’, “nfsen network_monitor plugin: Profilegroup: $profilegroup, Profile: $profile, Time: $timeslot”);
my %profileinfo = NfProfile::ReadProfile($profile, $profilegroup);
my $profilepath = NfProfile::ProfilePath($profile, $profilegroup);
my $all_sources = join ‘:’, keys %{$profileinfo{‘channel’}};
my $netflow_sources = “$PROFILEDIR/$profilepath/$all_sources”;
syslog(‘info’, “nfsen network_monitor plugin: sources ‘$netflow_sources'”);
# get filters and iterate through them
my $filterlistqry = “SELECT id,filter_type,name,options,filter,emailsubject,emailto,addblackhole FROM filters”;
my $filterlist = $dbh->prepare($filterlistqry);
$filterlist->execute();
while (my $fresult = $filterlist->fetchrow_hashref())
{
my $FILTERID = 0;
my $FILTERTYPE = 0;
my $FILTERNAME = “”;
my $OPTIONS = “”;
my $FILTER = “”;
my $EXCLUSIONS = “”;
my $NF_FILTER = “”;
my $subject= $defaultsubject;
my $emails = $defaultemails;
my $ADDBLACKHOLE = 0;
$FILTERID = $fresult->{‘id’};
$FILTERTYPE = $fresult->{‘filter_type’};
$FILTERNAME = $fresult->{‘name’};
$OPTIONS = $fresult->{‘options’};
$FILTER = $fresult->{‘filter’};
$ADDBLACKHOLE = $fresult->{‘addblackhole’};
my $FILTERALERTEMAILSUBJECT = $fresult->{’emailsubject’};
my $FILTERALERTEMAILS = $fresult->{’emailto’};
$subject = $FILTERALERTEMAILSUBJECT if (defined($FILTERALERTEMAILSUBJECT) && $FILTERALERTEMAILSUBJECT ne ” );
$emails = $FILTERALERTEMAILS if (defined($FILTERALERTEMAILS) && $FILTERALERTEMAILS ne ” );
syslog (‘info’, “nfsen network_monitor plugin: —————————————————————————————————-“);
syslog (‘info’, “nfsen network_monitor plugin: processing filter id:$FILTERID name:$FILTERNAME ————————————————–“);
syslog (‘info’, “nfsen network_monitor plugin: alertsubject:$subject alertemails: $emails”) if ($DEBUG > 0);
$alert = 0;
my @output = ();
my @tail = ();
# get exclusions for filter
my $dbh2 = DBI->connect($dsn, $user, $pass) or die “Can’t connect to the DB: $DBI::errstr\n”;
my $esql = “SELECT ipaddr FROM exclusions WHERE filter = $FILTERID”;
my $elist = $dbh2->prepare($esql);
$elist->execute();
my $ei = 0;
while (my $eresult = $elist->fetchrow_hashref())
{
if ($ei == 0)
{
$EXCLUSIONS .= “(NOT net $eresult->{‘ipaddr’} “;
}
elsif ($ei > 0)
{
$EXCLUSIONS .= ” AND NOT net $eresult->{‘ipaddr’}”;
}
$ei++;
}
if ($ei > 0)
{
$EXCLUSIONS .= “)”;
}
$dbh2->disconnect();
syslog (‘info’, “nfsen network_monitor plugin: exclusions: $EXCLUSIONS”);
# now, lets build our NFSen filters
if ($FILTERTYPE == 1)
{
if ($EXCLUSIONS eq “”)
{
$NF_FILTER = “$FILTER”;
}
else
{
$NF_FILTER = “$FILTER AND $EXCLUSIONS”;
}
syslog(‘info’, “nfsen network_monitor plugin: running command $nfdump -M $netflow_sources -r nfcapd.$timeslot $OPTIONS ‘$NF_FILTER'”);
@output = `$nfdump -M $netflow_sources -r nfcapd.$timeslot $OPTIONS ‘$NF_FILTER’`;
}
else
{
syslog(‘debug’, “nfsen network_monitor plugin: undefined filter_type value returned from database. Exiting…”);
return -123;
}
syslog(‘info’, “nfsen network_monitor plugin: Checking Filtername : $FILTERNAME NF Filter : $NF_FILTER”);
if ($FILTERTYPE == 1)
{
my $numlines = scalar(@output);
if ($numlines > 8)
{
syslog(‘info’, “nfsen network_monitor plugin: matched filter id:$FILTERID name:$FILTERNAME”);
notify(“$subject Filtername : $FILTERNAME : Profile $profile, Timeslot $timeslot”, \@output, $emails );
my $outstring = “”;
foreach my $item (@output)
{
$outstring .= $item . “\n”;
}
# log attack to database
logAttackAlert($FILTERID, $FILTERNAME, $profile, $timeslot, $outstring);
if ($ADDBLACKHOLE == 1)
{
syslog(‘info’, “nfsen network_monitor plugin: tring to add black hole database, seraching for an ipv4 address in the output”);
foreach (@output)
{
my @entries=split(/\s/,$_);
foreach (@entries)
{
if($_ =~ /(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)/)
{
my $ipv4=$_;
syslog(‘info’, “nfsen network_monitor plugin: find ipv4 address $ipv4”);
$ipv4=~s /(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)(:\d*)/$1/;
addBlackHolePrefix($timeslot,$ipv4);
}
}
}
}
}
}
else
{
syslog(‘err’, “nfsen network_monitor plugin: Unparsable output line ‘$output[-4]'”);
}
syslog(‘info’, “nfsen network_monitor plugin: End Filtername : $FILTERNAME”);
}
syslog (‘info’, “nfsen network_monitor plugin: updating blackhole database”);
#check up black hole prefixes database
updateBlackHolePrefix($timeslot);
$dbh->disconnect();
syslog (‘info’, “nfsen network_monitor plugin: end with filters”);
}
sub logAttackAlert
{
my $dbh3 = DBI->connect($dsn, $user, $pass) or die “Can’t connect to the DB: $DBI::errstr\n”;
my ($fid, $fn, $p, $ts, $os) = @_;
syslog(‘info’, “logsql: INSERT INTO alerts VALUES (NULL, $fid, ‘$p’, ‘$ts’, ‘$os’)”);
$dbh3->do(“INSERT INTO alerts VALUES (NULL, $fid, ‘$p’, ‘$ts’, ‘$os’)”);
if ($dbh3->err())
{
die “$DBI::errstr\n”;
}
$dbh3->disconnect();
my $alertfile = “/tmp/alert.txt”;
my $alerttext = “/tmp/alert-text.txt”;
open (ALERT, “>>$alertfile”);
print ALERT “$fn: Profile $p, Timeslot $ts<br>\n”;
close (ALERT);
system (“chmod 666 $alertfile”);
open (ALERTTEXT, “>>$alerttext”);
print ALERTTEXT “—————————————————————————\n”;
print ALERTTEXT “$fn, Timeslot $ts\n”, $os, “\n\n”;
close (ALERTTEXT);
}
sub addBlackHolePrefix
{
my $dbh3 = DBI->connect($dsn, $user, $pass) or die “Can’t connect to the DB: $DBI::errstr\n”;
my ($ts, $pf) = @_;
my $slotdate = $ts;
my $slottime = $ts;
$slotdate =~ s/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/$1-$2-$3/;
$slottime =~ s/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/$4:$5:00/;
syslog(‘info’, “nfsen network_monitor plugin: checking prefix $pf in bh database, timeslot $ts”);
my $get_prefix_query = “SELECT `blackholeprefixes`.`id`,`blackholeprefixes`.`prefix`,`blackholeprefixes`.`status`,`blackholeprefixes`.`penalty`,`blackholeprefixes`.`timeslot` FROM `blackholeprefixes` WHERE `blackholeprefixes`.`prefix` = ‘$pf'”;
syslog(‘info’, “nfsen network_monitor plugin: checking prefix sql query $get_prefix_query”);
my $get_prefix_result = $dbh3->prepare($get_prefix_query);
$get_prefix_result->execute();
my $returned_number_of_rows = $get_prefix_result->rows;
$get_prefix_result->fetch();
$get_prefix_result->finish;
syslog(‘info’, “nfsen network_monitor plugin: ROWS:$returned_number_of_rows”);
if ($returned_number_of_rows == 0)
{
#not defined in database, adding
syslog(‘info’, “nfsen network_monitor plugin: prefix $pf not found adding”);
my $insert_prefix_query =”INSERT INTO `blackholeprefixes` (`prefix`,`status`,`penalty`,`timeslot`) VALUES (‘$pf’, 1, 6, ‘$slotdate $slottime’)”;
syslog(‘info’, “nfsen network_monitor plugin: sql query $insert_prefix_query”);
$dbh3->do(“$insert_prefix_query”);
if ($dbh3->err())
{
push (@_,$DBI::errstr);
syslog(‘err’, “nfsen network_monitor plugin: can not add $pf to the dabase $DBI::errstr\n”);
}
$get_prefix_result->finish;
}
elsif ($returned_number_of_rows == 1)
{
#defined in database which should be added previously and also should be in passive state
# max penalty value can be added in where statement like prefix = pf and penalty < 5
my $update_prefix_query = “UPDATE `blackholeprefixes` SET `status` = 1, `penalty` = `penalty` + 1, `timeslot` = ‘$slotdate $slottime’ WHERE `blackholeprefixes`.`prefix` = ‘$pf'”;
syslog(‘info’, “nfsen network_monitor plugin: sql query $update_prefix_query”);
$dbh3->do(“$update_prefix_query”);
if ($dbh3->err())
{
push (@_,$DBI::errstr);
syslog(‘err’, “nfsen network_monitor plugin: can not add $pf to the dabase $DBI::errstr\n”);
}
$get_prefix_result->finish;
}
else
{
push (@_,$DBI::errstr);
syslog(‘err’, “nfsen network_monitor plugin: can not add $pf to the dabase $DBI::errstr\n”);
}
$dbh3->disconnect();
}
sub updateBlackHolePrefix
{
my $dbh4 = DBI->connect($dsn, $user, $pass) or die “Can’t connect to the DB: $DBI::errstr\n”;
my ($ts) = @_;
my $slotdate = $ts;
my $slottime = $ts;
$slotdate =~ s/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/$1-$2-$3/;
$slottime =~ s/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/$4:$5:00/;
# checking prefix that should be in passive state
# status = P where timestamp = currenttimestamp – (penalty * 5)
my $update_prefixes_status_query = “UPDATE `blackholeprefixes` SET `status` = 0 WHERE timeslot = DATE_SUB(‘$slotdate $slottime’, INTERVAL (5*penalty) MINUTE)”;
syslog(‘info’, “nfsen network_monitor plugin: sql query $update_prefixes_status_query”);
$dbh4->do(“$update_prefixes_status_query”);
if ($dbh4->err())
{
push (@_,$DBI::errstr);
syslog(‘err’, “nfsen network_monitor plugin: database error $DBI::errstr\n”);
}
my $delete_prefixes_status_query = “DELETE FROM `blackholeprefixes` WHERE timeslot < DATE_SUB(‘$slotdate $slottime’, INTERVAL (5*penalty) MINUTE)”;
syslog(‘info’, “nfsen network_monitor plugin: sql query $delete_prefixes_status_query”);
$dbh4->do(“$delete_prefixes_status_query”);
if ($dbh4->err())
{
push (@_,$DBI::errstr);
syslog(‘err’, “nfsen network_monitor plugin: database error $DBI::errstr\n”);
}
$dbh4->disconnect();
}
#
# Alert condition function.
# if defined it will be automatically listed as available plugin, when defining an alert.
# Called after flow filter is applied. Resulting flows stored in $alertflows file
# Should return 0 or 1 if condition is met or not
#
sub alert_condition
{
my $argref = shift;
my $alert = $$argref{‘alert’};
my $alertflows = $$argref{‘alertfile’};
my $timeslot = $$argref{‘timeslot’};
syslog(‘info’, “Alert condition function called: alert: $alert, alertfile: $alertflows, timeslot: $timeslot”);
return 1;
}
#
# Alert action function.
# if defined it will be automatically listed as available plugin, when defining an alert.
# Called when the trigger of an alert fires.
# Return value ignored
#
sub alert_action
{
my $argref = shift;
my $alert = $$argref{‘alert’};
my $timeslot = $$argref{‘timeslot’};
syslog(‘info’, “Alert action function called: alert: $alert, timeslot: $timeslot”);
return 1;
}
#
# The Init function is called when the plugin is loaded. It’s purpose is to give the plugin
# the possibility to initialize itself. The plugin should return 1 for success or 0 for
# failure. If the plugin fails to initialize, it’s disabled and not used. Therefore, if
# you want to temporarily disable your plugin return 0 when Init is called.
#
sub Init
{
syslog(“info”, “nfsen network_monitor plugin:: Starting”);
# Init some vars
$nfdump = “$NfConf::PREFIX/nfdump”;
$PROFILEDIR = “$NfConf::PROFILEDATADIR”;
return 1;
}
#
# The Cleanup function is called, when nfsend terminates. It’s purpose is to give the
# plugin the possibility to cleanup itself. It’s return value is discard.
#
sub Cleanup
{
syslog(“info”, “nfsen network_monitor plugin: Calling maid service for cleanup…”);
}
1;
veri tabanı yaratma :
— Current Database: `network_monitor`
—
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `network_monitor` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `network_monitor`;
—
— Table structure for table `alerts`
—
DROP TABLE IF EXISTS `alerts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `alerts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`filter` int(11) NOT NULL,
`profile` varchar(32) NOT NULL,
`timeslot` varchar(32) NOT NULL,
`alert_text` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=129 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
—
— Table structure for table `blackholeprefixes`
—
DROP TABLE IF EXISTS `blackholeprefixes`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blackholeprefixes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`prefix` varchar(15) NOT NULL COMMENT ‘thought and used like /32’,
`penalty` int(11) NOT NULL DEFAULT ‘6’,
`timeslot` datetime NOT NULL,
`status` int(11) NOT NULL DEFAULT ‘1’ COMMENT ‘Acive or Pasive? Active ones will be used for BGP announcing’,
PRIMARY KEY (`id`),
UNIQUE KEY `prefix_UNIQUE` (`prefix`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
—
— Table structure for table `exclusions`
—
DROP TABLE IF EXISTS `exclusions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `exclusions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`filter` int(11) NOT NULL,
`ipaddr` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
`date_added` date NOT NULL,
`reason` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_filter` (`filter`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
—
— Table structure for table `filters`
—
DROP TABLE IF EXISTS `filters`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `filters` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`filter_type` int(11) NOT NULL COMMENT ‘defined in the script\nCurrently :\n1 – Plain nfdump filter : $nfdump -M $netflow_sources -r nfcapd.$timeslot $OPTIONS ”$NF_FILTER”\n’,
`name` varchar(32) NOT NULL,
`options` varchar(45) NOT NULL DEFAULT ‘-a’,
`filter` varchar(1024) NOT NULL DEFAULT ‘any’,
`addblackhole` int(11) NOT NULL DEFAULT ‘0’,
`emailsubject` varchar(45) DEFAULT NULL,
`emailto` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
—
— Table structure for table `general_log`
—
DROP TABLE IF EXISTS `general_log`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `general_log` (
`event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`user_host` mediumtext NOT NULL,
`thread_id` int(11) NOT NULL,
`server_id` int(10) unsigned NOT NULL,
`command_type` varchar(64) NOT NULL,
`argument` mediumtext NOT NULL
) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT=’General log’;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */
bgpscripti :
: /usr/local/sbin .pl olarak ekleyin.
#!/usr/bin/perl
use strict;
use warnings;
use Proc::Daemon;
Proc::Daemon::Init;
my $continue = 1;
$SIG{TERM} = sub { $continue = 0 };
while ($continue) {
$| = 1;
# $Id: routeinjector.pl,v 1.2 2012/02/03 17:11:10 jtk Exp $
use Array::Diff; # WARNING: Algorithm::Diff::XS is not taint mode compatible
use DBI;
use DBD::mysql;
use Regexp::IPv6 qw($IPv6_re);
use Net::BGP::Process;
use Net::BGP::Peer;
use POSIX qw(strftime);
use Sys::Syslog;
use Net::Netmask;
# the period, in seconds, to run the utility callback functions
use constant CHECK_PEER_INTERVAL => 30;
use constant CHECK_DB_INTERVAL => 300;
# adapted from Regexp::IPv6 0.03 (yes, oddly enough)
use constant IPV4_RE => qr{ \A (
(?: 25[0-5] | 2[0-4][0-9] | [0-1]? [0-9]{1,2} )
(?:
[.]
(?: 25[0-5] | 2[0-4][0-9] | [0-1]? [0-9]{1,2} )
){3}
) \Z
}xms;
# BGP ERROR codes and subcodes adapted from bgpsimple.pl
# XXX: put this into a separate module file
my %BGP_ERR = ( # this hash doesn’t work well as a constant, use Readonly?
0 => {
__NAME__ => ‘Reserved’,
0 => ‘Reserved’,
},
1 => {
__NAME__ => ‘Message Header Error’,
0 => ‘Reserved’,
1 => ‘Connection Not Synchronized’,
2 => ‘Bad Message Length’,
3 => ‘Bad Message Type’,
},
2 => {
__NAME__ => ‘OPEN Message Error’,
0 => ‘Reserved’,
1 => ‘Unsupported Version Number’,
2 => ‘Bad Peer AS’,
3 => ‘Bad BGP Identifier’,
4 => ‘Unsupported Optional Parameter’,
5 => ‘[Deprecated], see RFC4271’,
6 => ‘Unacceptable Hold Time’,
},
3 => {
__NAME__ => ‘UPDATE Message Error’,
0 => ‘Reserved’,
1 => ‘Malformed Attribute List’,
2 => ‘Unrecognized Well-known Attribute’,
3 => ‘Missing Well-known Attribute’,
4 => ‘Attribute Flags Error’,
5 => ‘Attribute Length Error’,
6 => ‘Invalid ORIGIN Attribute’,
7 => ‘[Deprecated], see RFC4271’,
8 => ‘Invalid NEXT_HOP Attribute’,
9 => ‘Optional Attribute Error’,
10 => ‘Invalid Network Field’,
11 => ‘Malformed AS_PATH’,
},
4 => {
__NAME__ => ‘Hold Timer Expired’,
0 => ‘Reserved’,
},
5 => {
__NAME__ => ‘Finite State Machine Error’,
0 => ‘Reserved’,
},
6 => {
__NAME__ => ‘Cease’,
0 => ‘Reserved’,
1 => ‘Maximum Number of Prefixes Reached’,
2 => ‘Administrative Shutdown’,
3 => ‘Peer De-configured’,
4 => ‘Administrative Reset’,
5 => ‘Connection Rejected’,
6 => ‘Other Configuration Change’,
7 => ‘Connection Collision Resolution’,
9 => ‘Out of Resources’,
},
);
# required arguments need untainting for Socket.pm
my $local_addr = untaint_addr( ‘x’ );
my $local_asn = untaint_asn( ‘x’ );
my $peer_addr = untaint_addr( ‘x’ );
my $peer_asn = untaint_asn( ‘x’ );
my $next_hop = untaint_addr( ‘x’ );
# setup logging
Sys::Syslog::setlogsock(‘unix’);
openlog(“network-monitor”, “ndelay,pid”, “local0”);
syslog(‘info’, “blackholebgp.pl: BGP peer info Peer ip:$peer_addr”);
syslog(‘info’, “blackholebgp.pl: BGP next hop,used as Blackhole Nexthop: $next_hop”);
# setup persistent database connection if applicable
syslog(‘info’, “blackholebgp.pl: Connecting database for prefixes”);
my $dbh_ref = db_connect(
{
dbhost => ‘127.0.0.1’,
dbtype => ‘mysql’,
dbport => ‘3306’,
dbname => ‘network_monitor’,
dbuser => ‘sqluser’,
dbpass => ‘sqlpass’,
}
);
# announce array reference used to advertise routes
my $announce_ref;
# our peering configuration hash to pass to object creator
my %SESSION_CONFIG = (
Start => 1,
ThisID => $local_addr,
ThisAS => $local_asn,
PeerID => $peer_addr,
PeerAS => $peer_asn,
HoldTime => 180,
KeepAliveTime => 30,
Listen => 0, # non-standard, but we avoid needing root privs
);
# create necessary routing and peer objects
my $bgp = Net::BGP::Process->new;
my $peer = Net::BGP::Peer->new(%SESSION_CONFIG);
# apply peer configuration
$bgp->add_peer($peer);
# setup handlers
$peer->set_open_callback( \&callback_open );
$peer->set_update_callback( \&callback_update );
$peer->set_notification_callback( \&callback_notification );
$peer->set_refresh_callback( \&callback_refresh );
$peer->set_reset_callback( \&callback_reset );
$peer->set_error_callback( \&callback_error );
$peer->add_timer( \&callback_check_peer, CHECK_PEER_INTERVAL );
$peer->add_timer( \&callback_check_db, CHECK_DB_INTERVAL );
$SIG{INT} = \&sig_int;
# run as long as long as we’re not interrupted
$bgp->event_loop();
# tear down database connection
db_disconnect($dbh_ref);
syslog(‘info’, “blackholebgp.pl: Disconnected from database”);
# closing the log file should be the last thing we do
closelog();
# bye bye
syslog(‘info’, “blackholebgp.pl: Exiting”);
exit;
# given a valid looking IP address, return it, else return undef
sub untaint_addr {
my $addr = shift || return;
$addr = lc($addr); # IPv6 hex chars must be lc per RFC 5952
# IPv6 addresses will always have a colon, IPv4 won’t
if ( $addr =~ /:/ ) {
($addr) = $addr =~ /\A($IPv6_re)\Z/;
}
else {
($addr) = $addr =~ IPV4_RE;
}
return $addr; # an IPv4 address, IPv6 address or undef
}
# given a valid looking 16-bit ASN, return it, else return undef
sub untaint_asn {
my $asn = shift || return;
return if $asn !~ m{ \A \d{1,5} \Z }xms; # simple digit check
return if $asn < 0 || $asn > 65535; # Net::BGP only supports 16-bit ASNs
($asn) = $asn =~ m{ \A (\d+) \Z }xms;
return $asn; # will be a 16-bit ASN
}
# used for log messages and database record timestamps
sub current_timestamp {
return strftime “%Y-%m-%d %H:%M:%S UTC”, gmtime();
}
# connect to the database
sub db_connect {
my ($arg_ref) = @_;
my $db_type = $arg_ref->{dbtype};
my $db_host = $arg_ref->{dbhost};
my $db_port = $arg_ref->{dbport};
my $db_user = $arg_ref->{dbuser};
my $db_pass = $arg_ref->{dbpass};
my $db_name = $arg_ref->{dbname};
#TODO: MySQL does this different, try to do the right thing
my $db_dsn = “dbi:$db_type:$db_name:$db_host:$db_port”;
my $dbh;
eval { $dbh = DBI->connect( $db_dsn, $db_user, $db_pass ); };
if ($@) {
syslog(‘err’, “blackholebgp.pl:DBI Connect error $@”);
}
return $dbh ? \$dbh : undef;
}
# disconnect from the database
sub db_disconnect {
my ($dbh_ref) = @_;
eval { $$dbh_ref->disconnect; };
if ($@) {
syslog(‘err’, “blackholebgp.pl:DBI Disconnect error $@”);
}
return;
}
# get the current active route list
sub get_routes {
my $sql = ‘SELECT DISTINCT CONCAT(prefix,\’ 255.255.255.255\’) as prefix FROM blackholeprefixes WHERE status = 1′;
syslog(‘debug’, “blackholebgp.pl: Select Prefixes with $sql”);
# get an array reference to the current announce prefixes
my $routes_ref = $$dbh_ref->selectcol_arrayref($sql);
if ( !$routes_ref ) {
syslog(‘err’, “blackholebgp.pl:DBI Error DBI->errstr”);
return;
}
if (scalar @$routes_ref == 0) {
my $message =
“Total number of Prefixes:”.scalar @$routes_ref.” , no active route”;
syslog(‘info’, “blackholebgp.pl: $message”);
return;
} elsif (scalar @$routes_ref < 0 ) {
my $message =
“Total number of Prefixes:”.scalar @$routes_ref.” is below 0, may be database error”;
syslog(‘err’, “blackholebgp.pl: $message”);
return;
}
return $routes_ref;
}
# what to do when receiving a BGP open message
sub callback_open {
my ($peer) = @_;
my $message =
‘BGP session open from ‘ . $peer->peer_id . ‘ AS’ . $peer->peer_as;
syslog(‘info’, “blackholebgp.pl: $message”);
# NOTE: we can’t do a $peer->update from here for some reason, too soon?
# we’ll get them sent out on timer, but do init/clear array ref
$announce_ref = undef;
return;
}
# what to do when receiving a BGP update message
sub callback_update {
return;
}
# what to do when receiving a BGP notification message
sub callback_notification {
my ( $peer, $notify ) = @_;
# we don’t really care what we see here
return;
}
# what to do when receiving a route refresh message
sub callback_refresh {
my ( $peer, $refresh ) = @_;
# NOTE: It seems Net::BGP calls us when refresh is an option in
# an OPEN message but there won’t be any refresh object for us.
# Furthermore, with Net::BGP 0.14 we don’t seem to be called
# when we receive an actual refresh message. Thus, this
# subroutine is a placeholder.
return;
}
# what to do when BGP session is reset
sub callback_reset {
my ($peer) = @_;
my $message = sprintf “BGP session reset with %s AS%s”,
$peer->peer_id, $peer->peer_as;
syslog(‘info’, “blackholebgp.pl: $message”);
return;
}
# what to do when Net::BGP application detects an error
sub callback_error {
my ( $peer, $error ) = @_;
my $message = sprintf “BGP failure with %s AS%s type %s subcode %s”,
$peer->peer_id, $peer->peer_as,
$BGP_ERR{ $error->error_code }{__NAME__},
$BGP_ERR{ $error->error_code }{ $error->error_subcode };
if ( $error->error_data ) {
$message .= ‘, data ‘ . unpack( ‘H*’, $error->error_data );
}
syslog(‘err’, “blackholebgp.pl: $message”);
return;
}
# what to do every CHECK_PEER_INTERVAL
sub callback_check_peer {
my ($peer) = @_;
my $message = sprintf “Trying to reestablish peering with %s AS%s”,
$peer->peer_id, $peer->peer_as;
# try to get session reestablished if broken
if ( !$peer->is_established ) {
syslog(‘info’, “blackholebgp.pl: $message”);
$peer->start();
return;
}
# if we have routes we’re announcing, see if we need to send an update
if ($announce_ref) {
# get current route announcement list
my $candidate_ref = get_routes();
# Array::Diff doesn’t gracefully handle undef parameters
if ($candidate_ref) {
my $diff = Array::Diff->diff( $announce_ref, $candidate_ref );
send_update( $diff->added, $diff->deleted );
}
else {
# withdraw everything
send_update( undef, $announce_ref );
}
$announce_ref = $candidate_ref;
}
else {
# init announce list and announce if necessary
$announce_ref = get_routes();
send_update($announce_ref);
}
return;
}
# what to do every CHECK_DB_INTERVAL
sub callback_check_db {
my ($peer) = @_;
my $message = sprintf “Trying to reestablish the database connection”;
if ( !$$dbh_ref->ping ) {
syslog(‘info’, “blackholebgp.pl: $message”);
db_connect($dbh_ref);
}
return;
}
# what to do when interrupted
sub sig_int {
my $message = sprintf “Shutting down peering with %s AS%s”,
$peer->peer_id, $peer->peer_as;
syslog(‘info’, “blackholebgp.pl: $message”);
# this calls $peer->stop and sends a cease NOTIFICATION message
$bgp->remove_peer($peer);
return;
}
# send a list of announcements and withdrawals
sub send_update {
my ( $nlri_ref, $withdraw_ref ) = @_;
my $update;
my %prefixes;
my @new_nlri;
my @new_withdraw;
my $maxupdatesize=800;
return if !$nlri_ref && !$withdraw_ref;
# both an announce and withdrawal
if ( $nlri_ref && $withdraw_ref ) {
for (@$nlri_ref) {
my $nlri_row = $_;
$nlri_row=~s/( null0)//;
$nlri_row=~s/(\s)+/\:/;
my $block = new Net::Netmask ($nlri_row);
syslog(‘info’, “blackholebgp.pl: Send BGP UPDATE with NLRI & WITHDRAW for Prefix:$block”);
syslog(‘err’, “blackholebgp.pl: Prefix:$block shorther than 24”) if ($block->bits() <= 24);;
syslog(‘err’, “blackholebgp.pl: Skiping Prefix:$nlri_row normalized as $block Default Route”) ,next if ($block->bits() == 0);
push (@new_nlri,$block);
}
for (@$withdraw_ref) {
my $withdraw_row = $_;
$withdraw_row=~s/( null0)//;
$withdraw_row=~s/(\s)+/\:/;
my $block = new Net::Netmask ($withdraw_row);
push (@new_withdraw,$block);
}
my $l=scalar @new_nlri;
if (scalar @new_withdraw >= scalar @new_nlri) {
$l=scalar @new_withdraw;
}
my $i=0;
my $z=0;
my @tmp_nlriupdate=();
my @tmp_withdrawupdate=();
while ($i < $l) {
push(@tmp_nlriupdate,$new_nlri[$i]) if (defined($new_nlri[$i]));
push(@tmp_withdrawupdate,$new_withdraw[$i]) if (defined($new_withdraw[$i]));
if ($z!=int($i/$maxupdatesize)) {
$update = Net::BGP::Update->new(
NLRI => \@tmp_nlriupdate,
Withdraw => \@tmp_withdrawupdate,
AsPath => $local_asn,
NextHop => $next_hop,
LocalPref => 200,
Origin => 2, # INCOMPLETE, see RFC 4271
);
$peer->update($update);
@tmp_nlriupdate=();
@tmp_withdrawupdate=();
} elsif ($i==$l-1) {
$update = Net::BGP::Update->new(
NLRI => \@tmp_nlriupdate,
Withdraw => \@tmp_withdrawupdate,
AsPath => $local_asn,
NextHop => $next_hop,
LocalPref => 200,
Origin => 2, # INCOMPLETE, see RFC 4271
);
$peer->update($update);
@tmp_nlriupdate=();
@tmp_withdrawupdate=();
}
$z=int($i/$maxupdatesize);
$i++;
}
}
# just an announce
elsif ($nlri_ref) {
syslog(‘info’, “Just NLRI update”);
for (@$nlri_ref) {
my $nlri_row = $_;
$nlri_row=~s/( null0)//;
$nlri_row=~s/(\s)+/\:/;
my $block = new Net::Netmask ($nlri_row);
syslog(‘info’, “blackholebgp.pl: Send UPDATE for Prefix:$block”);
syslog(‘err’, “blackholebgp.pl: Prefix:$block shorther than 24”) if ($block->bits() <= 24);;
syslog(‘err’, “blackholebgp.pl: Skiping Prefix:$nlri_row normalized as $block Default Route”) ,next if ($block->bits() == 0);
push (@new_nlri,$block);
}
my $l=scalar @new_nlri;
my $i=0;
my $z=0;
my @tmp_update;
while ($i < $l) {
push(@tmp_update,$new_nlri[$i]);
if ($z!=int($i/$maxupdatesize)) {
$update = Net::BGP::Update->new(
NLRI => \@tmp_update,
AsPath => $local_asn,
NextHop => $next_hop,
LocalPref => 200,
Origin => 2, # INCOMPLETE, see RFC 4271
);
$peer->update($update);
@tmp_update=();
} elsif ($i==$l-1) {
$update = Net::BGP::Update->new(
NLRI => \@tmp_update,
AsPath => $local_asn,
NextHop => $next_hop,
LocalPref => 200,
Origin => 2, # INCOMPLETE, see RFC 4271
);
$peer->update($update);
@tmp_update=();
}
$z=int($i/$maxupdatesize);
$i++;
}
}
# just a withdrawal
else {
syslog(‘info’, “Just Withdraw”);
for (@$withdraw_ref) {
my $withdraw_row = $_;
$withdraw_row=~s/( null0)//;
$withdraw_row=~s/(\s)+/\:/;
my $block = new Net::Netmask ($withdraw_row);
syslog(‘info’, “blackholebgp.pl: Send WITHDRAW for Prefix:$block”);
push (@new_withdraw,$block);
}
my $l=scalar @new_withdraw;
my $i=0;
my $z=0;
my @tmp_update;
while ($i < $l) {
push(@tmp_update,$new_withdraw[$i]);
if ($z!=int($i/$maxupdatesize)) {
$update = Net::BGP::Update->new( Withdraw => \@tmp_update, );
$peer->update($update);
@tmp_update=();
} elsif ($i==$l-1) {
$update = Net::BGP::Update->new( Withdraw => \@tmp_update, );
$peer->update($update);
@tmp_update=();
}
$z=int($i/$maxupdatesize);
$i++;
}
}
return;
}
}
/usr/local/sbin/watchdog_blackholebgp : Bu script crontab’ eklenmelidir.
# Blackhole BGP icin watchdog – Deniz
* * * * * /usr/local/sbin/watchdog_blackholebgp
more /usr/local/sbin/watchdog_blackholebgp
#!/bin/bash
NAME=”blackholebgp.pl”
START=”/etc/init.d/blackholebgp start”
GREP=”/bin/grep”
PS=”/bin/ps”
NOP=”/bin/true”
DATE=”/bin/date”
$PS -ef | $GREP -v grep | $GREP $NAME >/dev/null 2>&1
case “$?” in
0)
$NOP
;;
1)
echo “$NAME is NOT RUNNING. Starting $NAME and sending notices.”
$START start 2>&1 >/dev/null &
echo “$NAME was not running and was started on `$DATE`” >> /var/log/messages
;;
esac
exit
more /etc/init.d/blackholebgp : blackhole bgp için servis scripti
#!/bin/bash
#
# Source function library.
. /etc/init.d/functions
binary=”/usr/local/sbin/blackholebgp.pl”
[ -x $binary ] || exit 0
RETVAL=0
start() {
if [ -f /var/run/blackholebgp.pid ] && kill -0 `cat /var/run/blackholebgp.pid`; then
echo ‘Service already running’ >&2
return 1
fi
echo -n “Starting blackholebgp: ”
daemon $binary
RETVAL=$?
PID=`pgrep -f blackholebgp.pl`
echo $PID > /var/run/blackholebgp.pid
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/blackholebgp
}
stop() {
echo -n “Shutting down blackholebgp: ”
killproc blackholebgp.pl
RETVAL=$?
echo
if [ $RETVAL -eq 0 ]; then
rm -f /var/lock/subsys/blackholebgp
rm -f /var/run/blackholebgp.pid
fi
}
restart() {
echo -n “Restarting blackholebgp: ”
stop
sleep 2
start
}
case “$1” in
start)
start
;;
stop)
stop
;;
status)
status blackholebgp.pl
;;
restart)
restart
;;
*)
echo “Usage: $0 {start|stop|status|restart}”
;;
esac
exit 0
[root@netflow ~]#