第四章 系統實作
4.3 心得感想
早先嘗試開始實做的時候是都使用交大 Netflow 文件所說的方式來 做,由於找不到學長可以問,許多都是自己懵懵懂懂在跌跌撞撞中一 直嘗試,差不多將交大 Netflow 文件所說的系統架起來之時,卻發生不 小心將檔案都刪除的意外,於是所有都歸零。同時間有再另外搜尋與 Netflow 相關的文章,發現在國外有個 Flow-Tools 可以拿來使用,經過 裝起來時使用之後發現 Flow-Tools 的效能比起交大 Netflow 文件所提 供的收集程式好,交大 Netflow 提供的收集程式會持續讓 CPU Loading 在 100%,早先以為是所使用的機器等級太差(Pentium-166),經過換上 Flow-Tools 之後發現 CPU Loading 大大降低,且其輸出格式也與交大 Netflow 文件使用的格式類似,於是便採用新的收集程式來做。
有了新的收集程式之後便開始重新再架過一次系統,由於早先實驗 用收集的流量資訊為逢甲大學校園網路對外骨幹中的一小部分,且其 流量大都是流出,這個缺點讓我無法確定自己的程式執行結果是否有 架設成功。在詢問系統維運組余組長之後,余組長答應使用較好的 PC(Pentium3-1G) 來 實 作 且 收 集 的 流 量 資 訊 來 源 為 校 內 工 學 院 的 Router(Cisco Catalyst 5500),從這台機器的流入流出很明顯可以看出差 異,在經過一段時間的嘗試之後,便開始參考交大 Netflow 文件裏的分
在經過一段時間的摸索與測試及改進之後,整個系統的運作算大致 上 OK,接下來所要作的就是將運算後資料作儲存及呈現,於是有了放 在資料庫裡的想法,藉由 PHP 與 MySQL 的結合讓我可以對網頁的呈 現做很大的彈性,也做出之前沒想到的功能。
後來指導老師建議應該有個圖表來表示各流量之間的關係圖會更 好。於是便開始找尋如何去使用 PHP 畫圓餅圖,發現 GD 是在 PHP 上 畫圖唯一的選擇,雖然知道 GD 在畫圖方面的能力很強,但是使用上 太過複雜。後來在 http://www.silisoftware.com/找到 phPie()這 個 php script,終於解決了困擾我許久關於如何畫圓餅圖的問題。
自從網站架構越來越完整,細部的需求也一值增加,實作期間也碰 過好幾次病毒流行,如開學病毒潮及最近的 Sasser 病毒肆虐,驗證本 系統可以對於異常行為的 IP 作有效的觀察,進而可以提供網管人員去 做處理,實在很欣慰,辛苦做的專題可以派上用場真的很高興。
參考資料
[1]Jon Orwant, Perl 5 Interactive Course, The Waite Group,Inc 1996 [2] Schwartz, Christiansen, Learning Perl, 2/e, O’REILLY 1999
[3]劉國正,林景毅,PHP4 入門實務手冊,松崗電腦圖書資料股份有限公 司,2000
[4]陳俊宏,PHP4 網站實作-深度研究篇,旗標出版股份有限公司,2000 [5]位元文化編著,PHP4+MySQL4 動態網頁入門實務,文魁資訊股份有 限公司,2002
參考網址
[1] Flow-Tools
http://www.splintered.net/sw/flow-tools/
[2] How to install and configure DBD::mysql
http://search.cpan.org/src/JWIED/DBD-mysql-2.1026/INSTALL.html
[3]交大 Netflow 文件
http://netflow.nctu.edu.tw/netflow.html
[4]如何建置 Netflow
http://www.tn.edu.tw/sammy/netflow/setup.htm
[5] NetFlow Services Solutions Guidehttp://www.cisco.com/univercd/cc/td/doc/cisintwk/intsolns/netflsol/nfw hite.htm
[6] NetflowExporter Project
http://sunsite.cc.ncu.edu.tw/NetflowExporter/
[7]phPie php sript
http://www.silisoftware.com/scripts/index.php?scriptname=phPie
[8]Perl Dochttp://www.perldoc.com/
[9]PHP5 網管實驗室
http://www.perldoc.com/
[10]網站建置百寶箱
http://dob.tnc.edu.tw/
程式碼
$dbh = DBI->connect($dsn, $user, $password);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time-86400);
$mon++;
$year+=1900;
#format date to 2-digit. Ex: 2 -> 02
$mon = sprintf("%02d",$mon);
$mday = sprintf("%02d",$mday);
#setting up path
$rawdir = "/netflow";
$flowprint = "/usr/local/netflow/bin/flow-print";
$dir = "/netflow/prog";
$outputdir = "/netflow/log";
$protfile = "$dir/protocols";
$servfile = "$dir/services";
$tablename = "ff$year$mon$mday";
@gotfilename = `ls $rawdir/ft- v05.$year-$mon-$mday.*`;
$starttime = localtime();
&init_prot($protfile);
&init_serv($servfile);
$dbh->do("DROP TABLE IF EXISTS `$tablename`");
#create tables
&createdb;
for ($i=0;$i<scalar @gotfilena me;$i++) {
$processtime = localtime();
chomp @gotfilename[$i];
printf LOG "NOW %3s %45s AT %20s\n",$i,@gotfilename[$i],$processtime;
open FLOWDATA, "$flowprint - f0 < @gotfilename[$i] |" || die $!;
while (<FLOWDATA>) { chomp;
$size /= 1048576; #(1024*1024);
&checkinout;
} }
#================
$insertdbtime = localtime();
print LOG "Start insert data to DB at $insertdbtime\n";
&insertdata;
&dailysizetotal;
#execute other perl program do "$dir/ipdeny.pl";
do "$dir/flowchart.pl";
$dbh->disconnect();
$endtime = localtime();
print LOG "start :: $starttime\n";
print LOG "end :: $endtime\n";
sub insertdata { my($i,$j);
$i = 0;
$j = 0;
$k = 0;
@sorted_hash = sort keys %iplist;
$length = scalar @sorted_hash;
$tablelistlength = scalar @tablelist;
for ($ipno;$ipno<$length;$ipno++){
for ($j=0;$j<$tablelistlength;$j++){
$tt = ${@tablefield[$j]}{@sorted_hash[$ipno]};
@value[$j] = "\"$tt\"\,";
}
chop @value[$j-1];
$dbh->do("INSERT INTO $tablename(ip\,@tablefield1) VALUES(\"@sorted_hash[$ipno]\",@value)");
}else {
sub checkinout {
if ( isInNet($src) && !isInNet($dst) ){
$iplist{$src}= "$src";
&update("FCU","OUT");
}
elsif ( !isInNet($src) && isInNet($dst) ){
$iplist{$dst}= "$dst";
&update("FCU","IN");
} }
sub update{
my($net,$io) = @_;
if ($proto == '06' || $proto == '11') { if ($service{"$sp"}) {
$port = $sp;
}
elsif ($service{"$dp"}) {
$port = $dp;
} else {
$port = "others";
} }
elsif ($proto == 01) {
$port = "icmp";
}
if ($io eq "IN"){
# my($a, $b, $c, $d) = split(/\./,"$src");
# $src = sprintf("%d.%d.%d", $a, $b, $c);
# ${$net.$io.SRC.$port.FLOWC}{"$src"} ++;
# ${$net.$io.SRC.$port.PKTSC}{"$src"} += $pkts;
# ${$net.$io.SRC.$port.SIZEC}{"$src"} += $size;
${$net.$io.DST.$port.FLOW}{"$dst"} ++;
${$net.$io.DST.$port.PKTS}{"$dst"} += $pkts;
${$net.$io.DST.$port.SIZE}{"$dst"} += $size;
${$net.$io.DST.total.FLOW}{"$dst"} ++;
${$net.$io.DST.total.PKTS}{"$dst"} += $pkts;
${$net.$io.DST.total.SIZE}{"$dst"} += $size;
} else {
# my($a, $b, $c, $d) = split(/\./,"$dst");
# $dst = sprintf("%d.%d.%d", $a, $b, $c);
${$net.$io.SRC.$port.FLOW}{"$src"} ++;
${$net.$io.SRC.$port.PKTS}{"$src"} += $pkts;
${$net.$io.SRC.$port.SIZE}{"$src"} += $size;
${$net.$io.SRC.total.FLOW}{"$src"} ++;
${$net.$io.SRC.total.PKTS}{"$src"} += $pkts;
${$net.$io.SRC.total.SIZE}{"$src"} += $size;
# ${$net.$io.DST.$port.FLOWC}{"$dst"} ++;
# ${$net.$io.DST.$port.PKTSC}{"$dst"} += $pkts;
# ${$net.$io.DST.$port.SIZEC}{"$dst"} += $size;
} }
sub init_prot { my($file) = @_;
open (FN,$file) || die $!;
while(<FN>){
my($name,$proto) = split;
$protocol{"$proto"} = $name;
} }
sub init_serv {
while (<FN>){
my($name,$port) = split;
$service{"$port"} = $name;
}
$service{"total"} = "total";
$service{"others"} = "others";
$service{"icmp"} = "icmp";
}
sub createdb {
my($net,$io,$sd,$port,$ip,$i,$j);
$i = 0;
foreach $net ("FCU"){
foreach $io ("IN","OUT"){
foreach $sd ("SRC","DST"){
foreach $port (keys(%service)) {
foreach $fps ("FLOW", "PKTS", "SIZE") {
@tablefield[$i] = sprintf("$net$io$sd$port$fps");
if (($io eq "IN" && $sd eq "DST") || ($io eq "OUT" && $sd eq "SRC")) {
@tablelist[$i] = sprintf("$net$io$fps$service{$port} DOUBLE(8,1) NOT NULL,");
@tablefield1[$i] = sprintf("$net$io$fps$service{$port} \,");
$i++;
chop @tablefield1[$i-1];
$create = sprintf("CREATE TABLE $tablename(ip char (15) NOT NULL,@tablelist)TYPE=MyISAM");
$dbh ->do($create);
}
sub dailysizetotal {
my($insize,$outsize,$totalsize);
$sth = $dbh->prepare("SELECT
sum(fcuinsizetotal),sum(fcuoutsizetotal),sum(fcuinsizetotal)+sum(fcuoutsizetotal) FROM $tablename");
$sth->execute();
while(($insize ,$outsize ,$totalsize) = $sth->fetchrow_array()) {
$dbh->do("insert into DailyTotalSize(date,insize,outsize,totalsize) values('$year$mon$mday','$insize','$outsize','$totalsize') ");
}
$dbh = DBI->connect($dsn, $user, $password);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time-600);
$mon++;
$year+=1900;
$nowtime = (time+60*60*8)%86400;
$suffix = ($nowtime-(86400/144))/(86400/144)%6;
if ($min >= 0 && $min <= 9 ){
$suffix = 0;
}
$mon = sprintf("%02d",$mon);
$mday = sprintf("%02d",$mday);
$hour = sprintf("%02d",$hour);
$rawdir = "/netflow";
$flowprint = "/usr/local/netflow/bin/flow-print";
$dir = "/netflow/prog";
$outputdir = "/netflow/log";
$protfile = "$dir/protocols";
$servfile = "$dir/services";
$ipfile = "$dir/iplist";
$tablename = "virus";
$sth = $dbh->prepare("SELECT
`icmp`,`445`,`59a`,`87`,`codered`,`nimda`,`netsky`,`smtp`,`netbios` FROM virus_cont");
$sth->execute();
($cont_icmp,$cont_445,$cont_59a,$cont_87,$cont_codered,$cont_nimda,$cont_netsky,
$cont_smtp,$cont_netbios) = $sth->fetchrow_array();
$sth->finish();
$filename = sprintf("ft- v05.%02d-%02d-%02d.%02d%d*", $year, $mon, $mday, $hour,
$suffix);
@gotfilename = `ls $rawdir/$filename`;
open LOG, ">$outputdir/abnormal.$mon$mday$hour-$suffix" or die $!;
$starttime = localtime();
&init_prot($protfile);
&init_serv($servfile);
for ($i=0;$i<scalar @gotfilename;$i++) {
$processtime = localtime();
chomp @gotfilename[$i];
printf LOG "NOW %3s %45s AT %20s\n",$i,@gotfilename[$i],$processtime;
open FLOWDATA, "$flowprint - f0 < @gotfilename[$i] |" || die $!;
while (<FLOWDATA>) { chomp;
$sif = substr($_, 0, index($_, ' ', 0));
$src = substr($_, 5, index($_, ' ', 5) - 5);
$dif = substr($_, 22, index($_, ' ', 22) - 22);
$insertdbtime = localtime();
#$dbh->do("DROP TABLE IF EXISTS `$tablename`");
#&createtable;
#&expire;
&out;
$dbh->disconnect();
print LOG "10 min total flow count $totalflow10min\n";
$endtime = localtime();
print LOG "start :: $starttime\n";
print LOG "end :: $endtime\n";
close LOG;
my ($expired,$mon,$mday,$hour,$min,$year);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time-3600);
$mon++;
$year+=1900;
$year = sprintf("%02d",$year%100);
$mon = sprintf("%02d",$mon);
$mday = sprintf("%02d",$mday);
$hour = sprintf("%02d",$hour);
$min = sprintf("%02d",$min);
print "$year$mon$mday$hour$min\n";
$expired = sprintf("$year$mon$mday$hour$min");
$dbh->do("delete from virus where date < \'$expired\'");
}
sub abnormal{
if (isInNet($src)) {
$flow_cont{$src}++;
$flow_cont_pkts{$src} += $pkts;
$flow_cont_size{$src} += $size;
}
if ( $proto == 01 && $sp == 0 && $dp == 800){
$abnormal_cont_icmp_flow{$src}++;
$abnormal_cont_icmp_pkts{$src} += $pkts;
$abnormal_cont_icmp_size{$src} += $size;
}
if ( $proto == '06' && $dp == '1bd') {
$abnormal_cont_445_flow{$src}++;
$abnormal_cont_445_pkts{$src} += $pkts;
$abnormal_cont_445_size{$src} += $size;
}
if ( $proto == '11' && $dp == '59a') {
$abnormal_cont_59a_flow{$src}++;
$abnormal_cont_59a_pkts{$src} += $pkts;
$abnormal_cont_59a_size{$src} += $size;
}
if ( $proto == '06' && $dp == '87' && $pkts == '1' && $size =='48') {
$abnormal_cont_87_flow{$src}++;
$abnormal_cont_87_pkts{$src} += $pkts;
$abnormal_cont_87_size{$src} += $size;
}
if ( $dp == '50' && $pkts == '3' && $size == '144') {
$abnormal_cont_codered_flow{$src}++;
$abnormal_cont_codered_pkts{$src} += $pkts;
$abnormal_cont_codered_size{$src} += $size;
}
if ( $dp == '50' ) {
$abnormal_cont_nimda_flow{$src}++;
$abnormal_cont_nimda_pkts{$src} += $pkts;
$abnormal_cont_nimda_size{$src} += $size;
}
if ( $dp == '19' && $pkts == '2' && $size == '96') {
$abnormal_cont_netsky_flow{$src}++;
$abnormal_cont_netsky_pkts{$src} += $pkts;
$abnormal_cont_netsky_size{$src} += $size;
}
if ( $dp == '19') {
$abnormal_cont_smtp_flow{$src}++;
$abnormal_cont_smtp_pkts{$src} += $pkts;
$abnormal_cont_smtp_size{$src} += $size;
}
if ( $dp == '8b' && $pkts == '1' && $size == '48') {
$abnormal_cont_netbios_flow{$src}++;
$abnormal_cont_netbios_pkts{$src} += $pkts;
$abnormal_cont_netbios_size{$src} += $size;
} }
sub out { my($src);
foreach $src(keys %flow_cont){
much\n",$src,$flow_cont{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_icmp_flow){
if ($abno rmal_cont_icmp_flow{$src} > $cont_icmp){
printf LOG ("on %18s occur %8s times icmp(dst:2048) pkts %10d,size
%5.2f\n",$src,$abnormal_cont_icmp_flow{$src},$abnormal_cont_icmp_pkts{$src},$a bnormal_cont_icmp_size{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_445_flow){
if ($abnormal_cont_445_flow{$src} > $cont_445){
printf LOG ("on %18s occur %8s times 445 port(1bd) pkts %10d,size
%5.2f\n",$src,$abnormal_cont_445_flow{$src},$abnormal_cont_445_pkts{$src},$abn ormal_cont_445_size{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_59a_flow){
if ($abnormal_cont_59a_flow{$src} > $cont_59a){
printf LOG ("on %18s occur %8s times 1434 port(59a) pkts %10d,size
%5.2f\n",$src,$abnormal_cont_59a_flow{$src},$abnormal_cont_59a_pkts{$src},$abno rmal_cont_59a_size{$src});
$dbh->do("INSERT INTO
foreach $src(keys %abnormal_cont_87_flow){
if ($abno rmal_cont_87_flow{$src} > $cont_87){
printf LOG ("on %18s occur %8s times 135 port(87) pkts %10d,size
%5.2f\n",$src,$abnormal_cont_87_flow{$src},$abnormal_cont_87_pkts{$src},$abnor mal_cont_87_size{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_codered_flow){
if ($abnormal_cont_codered_flow{$src} > $cont_codered){
printf LOG ("on %18s occur %8s times CodeRed pkts %10d,size
%5.2f\n",$src,$abnormal_cont_codered_flow{$src},$abnormal_cont_codered_pkts{$sr c},$abnormal_cont_codered_size{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_nimda_flow){
if ($abnormal_cont_nimda_flow{$src} > $cont_nimda){
printf LOG ("on %18s occur %8s times Nimda pkts %10d,size
%5.2f\n",$src,$abnormal_cont_nimda_flow{$src},$abnormal_cont_nimda_pkts{$src},
$abnormal_cont_nimda_size{$src});
VALUES(\'$src\'\,\'$abnormal_cont_nimda_flow{$src}\'\,\'$abnormal_cont_nimda_pkts {$src}\'\,\'$abnormal_cont_nimda_size{$src}\'\,\'Nimda\'\,\'$flow_cont{$src}\'\,\'$flow_
cont_pkts{$src}\'\,\'$flow_cont_size{$src}\')");
} }
foreach $src(keys %abnormal_cont_netsky_flow){
if ($abnormal_cont_netsky_flow{$src} > $cont_netsky){
printf LOG ("on %18s occur %8s times NetSky pkts %10d,size
%5.2f\n",$src,$abnormal_cont_netsky_flow{$src},$abnormal_cont_netsky_pkts{$src},
$abnormal_cont_netsky_size{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_smtp_flow){
if ($abnormal_cont_smtp_flow{$src} > $cont_smtp){
printf LOG ("on %18s occur %8s times Smtp pkts %10d,size
%5.2f\n",$src,$abnormal_cont_smtp_flow{$src},$abnormal_cont_smtp_pkts{$src},$ab normal_cont_smtp_size{$src});
$dbh->do("INSERT INTO
$tablename(ip\,flow\,pkts\,size\,kind\,10minoutflow\,10minoutpkts\,10minoutsize)
foreach $src(keys %abnormal_cont_netbios_flow){
if ($abnormal_cont_netbios_flow{$src} > $cont_netbios){
ts{$src}\'\,\'$abnormal_cont_netbios_size{$src}\'\,\'NetbiosScan\'\,\'$flow_cont{$src}\'\,
\'$flow_cont_pkts{$src}\'\,\'$flow_cont_size{$src}\')");
} } }
sub createtable {
$dbh->do("CREATE TABLE `virus` (`date` TIMESTAMP (10) DEFAULT '0', `ip`
CHAR (15) DEFAULT '0' NOT NULL, `flow` MEDIUMINT (8) UNSIGNED
DEFAULT '0' NOT NULL, `pkts` MEDIUMINT (8) UNSIGNED DEFAULT '0' NOT NULL, `size` DOUBLE (8,1) DEFAULT '0' NOT NULL, `kind` CHAR (10) DEFAULT '0' NOT NULL) TYPE = MyISAM");
sub init_prot { my($file) = @_;
open (FN,$file) || die $!;
while(<FN>){
my($name,$proto) = split;
$protocol{"$proto"} = $name;
} }
sub init_serv { my($file) = @_;
open(FN, $file) || die $!;
$service{"$port"} = $name;
}
$service{"total"} = "total";
$service{"others"} = "others";
$service{"icmp"} = "icmp";
}
sub ipdeny {#added at 2004/02/25
my($ip,$insize,$outsize,$totalsize,$low,$middle,$high);
$low = 1000;
$middle = 2000;
$high = 3000;
$sth = $dbh->prepare("SELECT ip, fcuinsizetotal,fcuoutsizetotal FROM
$tablename ORDER by ip");
$sth->execute();
while(($ip ,$insize ,$outsize) = $sth->fetchrow_array()) {
$totalsize = $insize+$outsize;
if ($totalsize > $low && $totalsize <= $middle){
$dbh->do("INSERT INTO IpDeny(ip,status_id,date2) VALUES('$ip','1','$year$mon$mday')");
}elsif ($totalsize > $middle && $totalsize <= $high){
$dbh->do("INSERT INTO IpDeny(ip,status_id,date2) VALUES('$ip','2','$year$mon$mday')");
}elsif ($totalsize > $high ){
$dbh->do("INSERT INTO IpDeny(ip,status_id,date2) VALUES('$ip','3','$year$mon$mday')");
} }
$sth->finish();
}