2008-01-16 08:36:45 +00:00
|
|
|
|
#!/usr/bin/perl -w
|
|
|
|
|
#
|
|
|
|
|
# $Id$
|
|
|
|
|
#
|
2013-04-29 10:43:44 +02:00
|
|
|
|
# written by Manuel Kasper <mk@neon1.net> for Monzoon Networks AG
|
2009-10-10 16:53:25 +00:00
|
|
|
|
# cli params/rrd storage/sampling mods Steve Colam <steve@colam.co.uk>
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
use strict;
|
2014-06-14 20:16:05 +02:00
|
|
|
|
use 5.010;
|
2014-01-07 16:45:12 +01:00
|
|
|
|
use IO::Select;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
use IO::Socket;
|
|
|
|
|
use RRDs;
|
2009-10-10 16:53:25 +00:00
|
|
|
|
use Getopt::Std;
|
2008-02-19 15:43:28 +00:00
|
|
|
|
|
|
|
|
|
my %knownlinks;
|
2013-03-17 15:12:40 +00:00
|
|
|
|
my %link_samplingrates;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
my $ascache = {};
|
|
|
|
|
my $ascache_lastflush = 0;
|
2012-04-19 11:59:08 +00:00
|
|
|
|
my $ascache_flush_interval = 25;
|
|
|
|
|
my $ascache_flush_number = 0;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
my $MAXREAD = 8192;
|
2014-01-07 16:45:12 +01:00
|
|
|
|
my $childrunning = 0;
|
|
|
|
|
|
|
|
|
|
# NetFlow
|
|
|
|
|
my $server_port = 9000;
|
2013-04-29 10:33:14 +01:00
|
|
|
|
my $v5_header_len = 24;
|
|
|
|
|
my $v5_flowrec_len = 48;
|
2013-01-21 15:12:01 +00:00
|
|
|
|
my $v8_header_len = 28;
|
|
|
|
|
my $v8_flowrec_len = 28;
|
|
|
|
|
my $v9_header_len = 20;
|
|
|
|
|
my $v9_templates = {};
|
2013-04-29 12:30:03 +01:00
|
|
|
|
my $v10_header_len = 16;
|
|
|
|
|
my $v10_templates = {};
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
# sFlow
|
|
|
|
|
my $sflow_server_port = 6343;
|
|
|
|
|
|
2009-10-10 16:53:25 +00:00
|
|
|
|
use vars qw/ %opt /;
|
2014-01-07 16:45:12 +01:00
|
|
|
|
getopts('r:p:P:k:a:', \%opt);
|
2009-10-10 16:53:25 +00:00
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
my $usage = "$0 [-rpPka]\n".
|
2009-10-10 16:53:25 +00:00
|
|
|
|
"\t-r <path to RRD files>\n".
|
2014-01-07 16:45:12 +01:00
|
|
|
|
"\t(-p <NetFlow UDP listen port - default $server_port, use 0 to disable NetFlow)\n".
|
|
|
|
|
"\t(-P <sFlow UDP listen port - default $sflow_server_port, use 0 to disable sFlow)\n".
|
2009-10-10 16:53:25 +00:00
|
|
|
|
"\t-k <path to known links file>\n".
|
2014-01-07 16:45:12 +01:00
|
|
|
|
"\t-a <your own AS number> - only required for sFlow\n";
|
2009-10-10 16:53:25 +00:00
|
|
|
|
|
|
|
|
|
my $rrdpath = $opt{'r'};
|
|
|
|
|
my $knownlinksfile = $opt{'k'};
|
2014-01-07 16:45:12 +01:00
|
|
|
|
my $myas = $opt{'a'};
|
2009-10-10 16:53:25 +00:00
|
|
|
|
|
|
|
|
|
die("$usage") if (!defined($rrdpath) || !defined($knownlinksfile));
|
|
|
|
|
|
|
|
|
|
die("$rrdpath does not exist or is not a directory\n") if ! -d $rrdpath;
|
|
|
|
|
die("$knownlinksfile does not exist or is not a file\n") if ! -f $knownlinksfile;
|
|
|
|
|
|
|
|
|
|
if (defined($opt{'p'})) {
|
|
|
|
|
$server_port = $opt{'p'};
|
2014-01-07 16:45:12 +01:00
|
|
|
|
die("NetFlow server port is non numeric\n") if $server_port !~ /^[0-9]+$/;
|
2009-10-10 16:53:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
if (defined($opt{'P'})) {
|
|
|
|
|
$sflow_server_port = $opt{'P'};
|
|
|
|
|
die("sFlow server port is non numeric\n") if $sflow_server_port !~ /^[0-9]+$/;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($sflow_server_port == $server_port) {
|
|
|
|
|
die("sFlow server port can't be the same as NetFlow server port\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
die("Your own AS number is non numeric\n") if ($sflow_server_port > 0 && $myas !~ /^[0-9]+$/);
|
|
|
|
|
|
|
|
|
|
|
2008-01-16 08:36:45 +00:00
|
|
|
|
# reap dead children
|
|
|
|
|
$SIG{CHLD} = \&REAPER;
|
|
|
|
|
$SIG{TERM} = \&TERM;
|
|
|
|
|
$SIG{INT} = \&TERM;
|
2009-10-10 16:53:25 +00:00
|
|
|
|
$SIG{HUP} = \&read_knownlinks;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
sub REAPER {
|
|
|
|
|
wait;
|
|
|
|
|
$childrunning = 0;
|
|
|
|
|
$SIG{CHLD} = \&REAPER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub TERM {
|
|
|
|
|
print "SIGTERM received\n";
|
|
|
|
|
exit 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-19 15:43:28 +00:00
|
|
|
|
# read known links file
|
|
|
|
|
read_knownlinks();
|
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
my ($lsn_nflow, $lsn_sflow);
|
|
|
|
|
my $sel = IO::Select->new();
|
|
|
|
|
|
2008-01-16 08:36:45 +00:00
|
|
|
|
# prepare to listen for NetFlow UDP packets
|
2014-01-07 16:45:12 +01:00
|
|
|
|
if ($server_port > 0) {
|
|
|
|
|
$lsn_nflow = IO::Socket::INET->new(LocalPort => $server_port, Proto => "udp")
|
|
|
|
|
or die "Couldn't be a NetFlow UDP server on port $server_port : $@\n";
|
|
|
|
|
$sel->add($lsn_nflow);
|
|
|
|
|
}
|
|
|
|
|
# prepare to listen for sFlow UDP packets
|
|
|
|
|
if ($sflow_server_port > 0) {
|
|
|
|
|
require Net::sFlow;
|
|
|
|
|
$lsn_sflow = IO::Socket::INET->new(LocalPort => $sflow_server_port, Proto => "udp")
|
|
|
|
|
or die "Couldn't be a sFlow UDP server on port $sflow_server_port : $@\n";
|
|
|
|
|
$sel->add($lsn_sflow);
|
|
|
|
|
}
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
my ($him,$datagram,$flags);
|
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
# main datagram receive loop
|
2008-01-16 08:36:45 +00:00
|
|
|
|
while (1) {
|
2014-01-07 16:45:12 +01:00
|
|
|
|
while (my @ready = $sel->can_read) {
|
|
|
|
|
foreach my $server (@ready) {
|
|
|
|
|
$him = $server->recv($datagram, $MAXREAD);
|
|
|
|
|
next if (!$him);
|
|
|
|
|
|
|
|
|
|
my ($port, $ipaddr) = sockaddr_in($server->peername);
|
|
|
|
|
|
|
|
|
|
if (defined($lsn_nflow) && $server == $lsn_nflow) {
|
|
|
|
|
my ($version) = unpack("n", $datagram);
|
|
|
|
|
|
|
|
|
|
if ($version == 5) {
|
|
|
|
|
parse_netflow_v5($datagram, $ipaddr);
|
|
|
|
|
} elsif ($version == 8) {
|
|
|
|
|
parse_netflow_v8($datagram, $ipaddr);
|
|
|
|
|
} elsif ($version == 9) {
|
|
|
|
|
parse_netflow_v9($datagram, $ipaddr);
|
|
|
|
|
} elsif ($version == 10) {
|
|
|
|
|
parse_netflow_v10($datagram, $ipaddr);
|
|
|
|
|
} else {
|
|
|
|
|
print "unknown NetFlow version: $version\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
elsif (defined($lsn_sflow) && $server == $lsn_sflow) {
|
|
|
|
|
parse_sflow($datagram, $ipaddr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-21 15:12:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-01-07 16:45:12 +01:00
|
|
|
|
|
2013-04-29 10:33:14 +01:00
|
|
|
|
sub parse_netflow_v5 {
|
|
|
|
|
my $datagram = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
|
|
|
|
|
my ($version, $count, $sysuptime, $unix_secs, $unix_nsecs,
|
|
|
|
|
$flow_sequence, $engine_type, $engine_id, $aggregation,
|
|
|
|
|
$agg_version) = unpack("nnNNNNCCCC", $datagram);
|
|
|
|
|
|
|
|
|
|
my $flowrecs = substr($datagram, $v5_header_len);
|
|
|
|
|
|
|
|
|
|
for (my $i = 0; $i < $count; $i++) {
|
|
|
|
|
my $flowrec = substr($datagram, $v5_header_len + ($i*$v5_flowrec_len), $v5_flowrec_len);
|
|
|
|
|
my @flowdata = unpack("NNNnnNNNNnnccccnnccN", $flowrec);
|
2014-01-07 16:45:12 +01:00
|
|
|
|
#print "ipaddr: " . inet_ntoa($ipaddr) . " octets: $flowdata[6] srcas: $flowdata[15] dstas: $flowdata[16] in: $flowdata[3] out: $flowdata[4] 4 \n";
|
|
|
|
|
handleflow($ipaddr, $flowdata[6], $flowdata[15], $flowdata[16], $flowdata[3], $flowdata[4], 4, 'netflow');
|
2013-04-29 10:33:14 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-21 15:12:01 +00:00
|
|
|
|
sub parse_netflow_v8 {
|
|
|
|
|
my $datagram = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
|
2008-01-16 08:36:45 +00:00
|
|
|
|
my ($version, $count, $sysuptime, $unix_secs, $unix_nsecs,
|
|
|
|
|
$flow_sequence, $engine_type, $engine_id, $aggregation,
|
|
|
|
|
$agg_version) = unpack("nnNNNNCCCC", $datagram);
|
|
|
|
|
|
2013-01-21 15:12:01 +00:00
|
|
|
|
if ($aggregation != 1 || ($agg_version != 0 && $agg_version != 2)) {
|
2008-01-16 08:36:45 +00:00
|
|
|
|
print "unknown version: $version/$aggregation/$agg_version\n";
|
2013-01-21 15:12:01 +00:00
|
|
|
|
return;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-21 15:12:01 +00:00
|
|
|
|
my $flowrecs = substr($datagram, $v8_header_len);
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
for (my $i = 0; $i < $count; $i++) {
|
2013-01-21 15:12:01 +00:00
|
|
|
|
my $flowrec = substr($datagram, $v8_header_len + ($i*$v8_flowrec_len), $v8_flowrec_len);
|
2008-01-16 08:36:45 +00:00
|
|
|
|
my @flowdata = unpack("NNNNNnnnn", $flowrec);
|
2014-01-07 16:45:12 +01:00
|
|
|
|
#print "ipaddr: " . inet_ntoa($ipaddr) . " octets: $flowdata[2] srcas: $flowdata[5] dstas: $flowdata[6] in: $flowdata[7] out: $flowdata[8] 4 \n";
|
|
|
|
|
handleflow($ipaddr, $flowdata[2], $flowdata[5], $flowdata[6], $flowdata[7], $flowdata[8], 4, 'netflow');
|
2013-01-21 15:12:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub parse_netflow_v9 {
|
|
|
|
|
my $datagram = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
|
|
|
|
|
# Parse packet
|
|
|
|
|
my ($version, $count, $sysuptime, $unix_secs, $seqno, $source_id, @flowsets) = unpack("nnNNNN(nnX4/a)*", $datagram);
|
|
|
|
|
|
|
|
|
|
# Loop through FlowSets and take appropriate action
|
|
|
|
|
for (my $i = 0; $i < scalar @flowsets; $i += 2) {
|
|
|
|
|
my $flowsetid = $flowsets[$i];
|
|
|
|
|
my $flowsetdata = substr($flowsets[$i+1], 4); # chop off id/length
|
|
|
|
|
if ($flowsetid == 0) {
|
|
|
|
|
# 0 = Template FlowSet
|
|
|
|
|
parse_netflow_v9_template_flowset($flowsetdata, $ipaddr, $source_id);
|
|
|
|
|
} elsif ($flowsetid == 1) {
|
|
|
|
|
# 1 - Options Template FlowSet
|
|
|
|
|
} elsif ($flowsetid > 255) {
|
|
|
|
|
# > 255: Data FlowSet
|
|
|
|
|
parse_netflow_v9_data_flowset($flowsetid, $flowsetdata, $ipaddr, $source_id);
|
|
|
|
|
} else {
|
|
|
|
|
# reserved FlowSet
|
|
|
|
|
print "Unknown FlowSet ID $flowsetid found\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub parse_netflow_v9_template_flowset {
|
|
|
|
|
my $templatedata = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
my $source_id = shift;
|
|
|
|
|
|
|
|
|
|
# Note: there may be multiple templates in a Template FlowSet
|
|
|
|
|
|
|
|
|
|
my @template_ints = unpack("n*", $templatedata);
|
|
|
|
|
|
|
|
|
|
my $i = 0;
|
|
|
|
|
while ($i < scalar @template_ints) {
|
|
|
|
|
my $template_id = $template_ints[$i];
|
|
|
|
|
my $fldcount = $template_ints[$i+1];
|
|
|
|
|
|
|
|
|
|
last if (!defined($template_id) || !defined($fldcount));
|
|
|
|
|
|
|
|
|
|
#print "Updated template ID $template_id (source ID $source_id, from " . inet_ntoa($ipaddr) . ")\n";
|
|
|
|
|
my $template = [@template_ints[($i+2) .. ($i+2+$fldcount*2-1)]];
|
|
|
|
|
$v9_templates->{$ipaddr}->{$source_id}->{$template_id}->{'template'} = $template;
|
|
|
|
|
|
|
|
|
|
# Calculate total length of template data
|
|
|
|
|
my $totallen = 0;
|
|
|
|
|
for (my $j = 1; $j < scalar @$template; $j += 2) {
|
|
|
|
|
$totallen += $template->[$j];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$v9_templates->{$ipaddr}->{$source_id}->{$template_id}->{'len'} = $totallen;
|
|
|
|
|
|
|
|
|
|
$i += (2 + $fldcount*2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub parse_netflow_v9_data_flowset {
|
|
|
|
|
my $flowsetid = shift;
|
|
|
|
|
my $flowsetdata = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
my $source_id = shift;
|
|
|
|
|
|
|
|
|
|
my $template = $v9_templates->{$ipaddr}->{$source_id}->{$flowsetid}->{'template'};
|
|
|
|
|
if (!defined($template)) {
|
|
|
|
|
#print "Template ID $flowsetid from $source_id/" . inet_ntoa($ipaddr) . " does not (yet) exist\n";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $len = $v9_templates->{$ipaddr}->{$source_id}->{$flowsetid}->{'len'};
|
|
|
|
|
|
|
|
|
|
my $ofs = 0;
|
|
|
|
|
my $datalen = length($flowsetdata);
|
|
|
|
|
while (($ofs + $len) <= $datalen) {
|
|
|
|
|
# Interpret values according to template
|
2014-06-14 20:16:05 +02:00
|
|
|
|
my ($inoctets, $outoctets, $srcas, $dstas, $snmpin, $snmpout, $ipversion, $vlanin, $vlanout);
|
2013-04-29 12:30:03 +01:00
|
|
|
|
|
2013-01-21 15:12:01 +00:00
|
|
|
|
$inoctets = 0;
|
|
|
|
|
$outoctets = 0;
|
|
|
|
|
$ipversion = 4;
|
|
|
|
|
|
|
|
|
|
for (my $i = 0; $i < scalar @$template; $i += 2) {
|
|
|
|
|
my $cur_fldtype = $template->[$i];
|
|
|
|
|
my $cur_fldlen = $template->[$i+1];
|
|
|
|
|
|
|
|
|
|
my $cur_fldval = substr($flowsetdata, $ofs, $cur_fldlen);
|
|
|
|
|
$ofs += $cur_fldlen;
|
|
|
|
|
|
|
|
|
|
if ($cur_fldtype == 16) { # SRC_AS
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$srcas = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$srcas = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 17) { # DST_AS
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$dstas = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$dstas = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 10) { # INPUT_SNMP
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$snmpin = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$snmpin = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 14) { # OUTPUT_SNMP
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$snmpout = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$snmpout = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 1) { # IN_BYTES
|
|
|
|
|
if ($cur_fldlen == 4) {
|
|
|
|
|
$inoctets = unpack("N", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 8) {
|
2013-04-29 15:58:27 +02:00
|
|
|
|
$inoctets = unpack("Q>", $cur_fldval);
|
2013-01-21 15:12:01 +00:00
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 23) { # OUT_BYTES
|
|
|
|
|
if ($cur_fldlen == 4) {
|
|
|
|
|
$outoctets = unpack("N", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 8) {
|
2013-04-29 15:58:27 +02:00
|
|
|
|
$outoctets = unpack("Q>", $cur_fldval);
|
2013-04-29 12:30:03 +01:00
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 60) { # IP_PROTOCOL_VERSION
|
|
|
|
|
$ipversion = unpack("C", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldtype == 27 || $cur_fldtype == 28) { # IPV6_SRC_ADDR/IPV6_DST_ADDR
|
|
|
|
|
$ipversion = 6;
|
2014-06-14 20:16:05 +02:00
|
|
|
|
} elsif ($cur_fldtype == 58) { # SRC_VLAN
|
|
|
|
|
$vlanin = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldtype == 59) { # SRC_VLAN
|
|
|
|
|
$vlanout = unpack("n", $cur_fldval);
|
2013-04-29 12:30:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (defined($srcas) && defined($dstas) && defined($snmpin) && defined($snmpout)) {
|
2014-06-14 20:16:05 +02:00
|
|
|
|
handleflow($ipaddr, $inoctets + $outoctets, $srcas, $dstas, $snmpin, $snmpout, $ipversion, 'netflow', $vlanin, $vlanout);
|
2013-04-29 12:30:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub parse_netflow_v10 {
|
|
|
|
|
my $datagram = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
|
|
|
|
|
# Parse packet
|
|
|
|
|
my ($version, $length, $sysuptime, $seqno, $source_id, @flowsets) = unpack("nnNNN(nnX4/a)*", $datagram);
|
|
|
|
|
|
|
|
|
|
# Loop through FlowSets and take appropriate action
|
|
|
|
|
for (my $i = 0; $i < scalar @flowsets; $i += 2) {
|
|
|
|
|
my $flowsetid = $flowsets[$i];
|
|
|
|
|
my $flowsetdata = substr($flowsets[$i+1], 4); # chop off id/length
|
|
|
|
|
|
|
|
|
|
if ($flowsetid == 2) {
|
|
|
|
|
# 0 = Template FlowSet
|
|
|
|
|
parse_netflow_v10_template_flowset($flowsetdata, $ipaddr, $source_id);
|
|
|
|
|
} elsif ($flowsetid == 3) {
|
|
|
|
|
# 1 - Options Template FlowSet
|
|
|
|
|
} elsif ($flowsetid > 255) {
|
|
|
|
|
# > 255: Data FlowSet
|
|
|
|
|
parse_netflow_v10_data_flowset($flowsetid, $flowsetdata, $ipaddr, $source_id);
|
|
|
|
|
} else {
|
|
|
|
|
# reserved FlowSet
|
|
|
|
|
print "Unknown FlowSet ID $flowsetid found\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub parse_netflow_v10_template_flowset {
|
|
|
|
|
my $templatedata = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
my $source_id = shift;
|
|
|
|
|
|
|
|
|
|
# Note: there may be multiple templates in a Template FlowSet
|
|
|
|
|
|
|
|
|
|
my @template_ints = unpack("n*", $templatedata);
|
|
|
|
|
|
|
|
|
|
my $i = 0;
|
|
|
|
|
while ($i < scalar @template_ints) {
|
|
|
|
|
|
|
|
|
|
my $template_id = $template_ints[$i];
|
|
|
|
|
my $fldcount = $template_ints[$i+1];
|
|
|
|
|
|
|
|
|
|
last if (!defined($template_id) || !defined($fldcount));
|
|
|
|
|
|
|
|
|
|
#print "Updated template ID $template_id (source ID $source_id, from " . inet_ntoa($ipaddr) . ")\n";
|
|
|
|
|
my $template = [@template_ints[($i+2) .. ($i+2+$fldcount*2-1)]];
|
|
|
|
|
|
|
|
|
|
$v10_templates->{$ipaddr}->{$source_id}->{$template_id}->{'template'} = $template;
|
|
|
|
|
|
|
|
|
|
# Calculate total length of template data
|
|
|
|
|
my $totallen = 0;
|
|
|
|
|
for (my $j = 1; $j < scalar @$template; $j += 2) {
|
|
|
|
|
$totallen += $template->[$j];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$v10_templates->{$ipaddr}->{$source_id}->{$template_id}->{'len'} = $totallen;
|
|
|
|
|
|
|
|
|
|
$i += (2 + $fldcount*2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub parse_netflow_v10_data_flowset {
|
|
|
|
|
my $flowsetid = shift;
|
|
|
|
|
my $flowsetdata = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
my $source_id = shift;
|
|
|
|
|
|
|
|
|
|
my $template = $v10_templates->{$ipaddr}->{$source_id}->{$flowsetid}->{'template'};
|
|
|
|
|
if (!defined($template)) {
|
|
|
|
|
#print "Template ID $flowsetid from $source_id/" . inet_ntoa($ipaddr) . " does not (yet) exist\n";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $len = $v10_templates->{$ipaddr}->{$source_id}->{$flowsetid}->{'len'};
|
|
|
|
|
|
|
|
|
|
my $ofs = 0;
|
|
|
|
|
my $datalen = length($flowsetdata);
|
|
|
|
|
while (($ofs + $len) <= $datalen) {
|
|
|
|
|
# Interpret values according to template
|
2014-06-14 20:16:05 +02:00
|
|
|
|
my ($inoctets, $outoctets, $srcas, $dstas, $snmpin, $snmpout, $ipversion, $vlanin, $vlanout);
|
2013-04-29 12:30:03 +01:00
|
|
|
|
|
|
|
|
|
$inoctets = 0;
|
|
|
|
|
$outoctets = 0;
|
|
|
|
|
$ipversion = 4;
|
|
|
|
|
|
|
|
|
|
for (my $i = 0; $i < scalar @$template; $i += 2) {
|
|
|
|
|
my $cur_fldtype = $template->[$i];
|
|
|
|
|
my $cur_fldlen = $template->[$i+1];
|
|
|
|
|
|
|
|
|
|
my $cur_fldval = substr($flowsetdata, $ofs, $cur_fldlen);
|
|
|
|
|
$ofs += $cur_fldlen;
|
|
|
|
|
|
|
|
|
|
if ($cur_fldtype == 16) { # SRC_AS
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$srcas = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$srcas = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 17) { # DST_AS
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$dstas = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$dstas = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 10) { # INPUT_SNMP
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$snmpin = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$snmpin = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 14) { # OUTPUT_SNMP
|
|
|
|
|
if ($cur_fldlen == 2) {
|
|
|
|
|
$snmpout = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 4) {
|
|
|
|
|
$snmpout = unpack("N", $cur_fldval);
|
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 1) { # IN_BYTES
|
|
|
|
|
if ($cur_fldlen == 4) {
|
|
|
|
|
$inoctets = unpack("N", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 8) {
|
2013-04-29 15:58:27 +02:00
|
|
|
|
$inoctets = unpack("Q>", $cur_fldval);
|
2013-04-29 12:30:03 +01:00
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 23) { # OUT_BYTES
|
|
|
|
|
if ($cur_fldlen == 4) {
|
|
|
|
|
$outoctets = unpack("N", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldlen == 8) {
|
2013-04-29 15:58:27 +02:00
|
|
|
|
$outoctets = unpack("Q>", $cur_fldval);
|
2013-01-21 15:12:01 +00:00
|
|
|
|
}
|
|
|
|
|
} elsif ($cur_fldtype == 60) { # IP_PROTOCOL_VERSION
|
|
|
|
|
$ipversion = unpack("C", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldtype == 27 || $cur_fldtype == 28) { # IPV6_SRC_ADDR/IPV6_DST_ADDR
|
|
|
|
|
$ipversion = 6;
|
2014-06-14 20:16:05 +02:00
|
|
|
|
} elsif ($cur_fldtype == 58) { # SRC_VLAN
|
|
|
|
|
$vlanin = unpack("n", $cur_fldval);
|
|
|
|
|
} elsif ($cur_fldtype == 59) { # SRC_VLAN
|
|
|
|
|
$vlanout = unpack("n", $cur_fldval);
|
2013-01-21 15:12:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (defined($srcas) && defined($dstas) && defined($snmpin) && defined($snmpout)) {
|
2014-06-14 20:16:05 +02:00
|
|
|
|
handleflow($ipaddr, $inoctets + $outoctets, $srcas, $dstas, $snmpin, $snmpout, $ipversion, 'netflow', $vlanin, $vlanout);
|
2013-01-21 15:12:01 +00:00
|
|
|
|
}
|
2008-01-16 08:36:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
sub parse_sflow {
|
|
|
|
|
my $datagram = shift;
|
|
|
|
|
my $ipaddr = shift;
|
|
|
|
|
|
|
|
|
|
# decode the sFlow packet
|
|
|
|
|
my ($sFlowDatagramRef, $sFlowSamplesRef, $errorsRef) = Net::sFlow::decode($datagram);
|
|
|
|
|
|
|
|
|
|
if ($sFlowDatagramRef->{'sFlowVersion'} != 5) {
|
|
|
|
|
print "Warning: non-v5 packet received - not supported\n";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# use agent IP if available (in case of proxy)
|
|
|
|
|
if ($sFlowDatagramRef->{'AgentIp'}) {
|
|
|
|
|
$ipaddr = inet_aton($sFlowDatagramRef->{'AgentIp'});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach my $sFlowSample (@{$sFlowSamplesRef}) {
|
|
|
|
|
my $ipversion = 4;
|
|
|
|
|
|
|
|
|
|
# only process standard structures
|
|
|
|
|
next if ($sFlowSample->{'sampleTypeEnterprise'} != 0);
|
|
|
|
|
|
|
|
|
|
# only process normal flow samples
|
|
|
|
|
next if ($sFlowSample->{'sampleTypeFormat'} != 1);
|
|
|
|
|
|
|
|
|
|
my $snmpin = $sFlowSample->{'inputInterface'};
|
|
|
|
|
my $snmpout = $sFlowSample->{'outputInterface'};
|
|
|
|
|
|
|
|
|
|
if ($snmpin >= 1073741823 || $snmpout >= 1073741823) {
|
|
|
|
|
# invalid interface index - could be dropped packet or internal
|
|
|
|
|
# (routing protocol, management etc.)
|
|
|
|
|
#print "Invalid interface index $snmpin/$snmpout\n";
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $noctets;
|
|
|
|
|
if ($sFlowSample->{'IPv4Packetlength'}) {
|
|
|
|
|
$noctets = $sFlowSample->{'IPv4Packetlength'};
|
|
|
|
|
} elsif ($sFlowSample->{'IPv6Packetlength'}) {
|
|
|
|
|
$noctets = $sFlowSample->{'IPv6Packetlength'};
|
|
|
|
|
$ipversion = 6;
|
|
|
|
|
} else {
|
|
|
|
|
$noctets = $sFlowSample->{'HeaderFrameLength'} - 14;
|
|
|
|
|
|
|
|
|
|
# make one more attempt at figuring out the IP version
|
|
|
|
|
if ((defined($sFlowSample->{'GatewayIpVersionNextHopRouter'}) &&
|
|
|
|
|
$sFlowSample->{'GatewayIpVersionNextHopRouter'} == 2) ||
|
|
|
|
|
(defined($sFlowSample->{'HeaderType'}) && $sFlowSample->{'HeaderType'} eq '86dd')) {
|
|
|
|
|
$ipversion = 6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $srcas = 0;
|
|
|
|
|
my $dstas = 0;
|
|
|
|
|
|
|
|
|
|
if ($sFlowSample->{'GatewayAsSource'}) {
|
|
|
|
|
$srcas = $sFlowSample->{'GatewayAsSource'};
|
|
|
|
|
}
|
|
|
|
|
if ($sFlowSample->{'GatewayDestAsPaths'}) {
|
|
|
|
|
$dstas = pop(@{$sFlowSample->{'GatewayDestAsPaths'}->[0]->{'AsPath'}});
|
|
|
|
|
if (!$dstas) {
|
|
|
|
|
$dstas = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Outbound packets have our AS number as the source (GatewayAsSource),
|
|
|
|
|
# while inbound packets have 0 as the destination (empty AsPath).
|
|
|
|
|
# Transit packets have "foreign" AS numbers for both source and
|
|
|
|
|
# destination (handleflow() currently deals with those by counting
|
|
|
|
|
# them twice; once for input and once for output)
|
|
|
|
|
|
|
|
|
|
# substitute 0 for own AS number
|
|
|
|
|
if ($srcas == $myas) {
|
|
|
|
|
$srcas = 0;
|
|
|
|
|
}
|
|
|
|
|
if ($dstas == $myas) {
|
|
|
|
|
$dstas = 0;
|
|
|
|
|
}
|
2014-06-14 20:16:05 +02:00
|
|
|
|
|
|
|
|
|
# Extract VLAN information
|
|
|
|
|
my ($vlanin, $vlanout);
|
|
|
|
|
if ($sFlowSample->{'SwitchSrcVlan'}) {
|
|
|
|
|
$vlanin = $sFlowSample->{'SwitchSrcVlan'};
|
|
|
|
|
}
|
|
|
|
|
if ($sFlowSample->{'SwitchDestVlan'}) {
|
|
|
|
|
$vlanout = $sFlowSample->{'SwitchDestVlan'};
|
|
|
|
|
}
|
2014-01-07 16:45:12 +01:00
|
|
|
|
|
2014-06-14 20:16:05 +02:00
|
|
|
|
handleflow($ipaddr, $noctets, $srcas, $dstas, $snmpin, $snmpout, $ipversion, 'sflow', $vlanin, $vlanout);
|
2014-01-07 16:45:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-01-16 08:36:45 +00:00
|
|
|
|
sub handleflow {
|
2014-06-14 20:25:58 +02:00
|
|
|
|
my ($routerip, $noctets, $srcas, $dstas, $snmpin, $snmpout, $ipversion, $type, $vlanin, $vlanout) = @_;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
if ($srcas == 0 && $dstas == 0) {
|
2014-01-07 16:45:12 +01:00
|
|
|
|
# don't care about internal traffic
|
2008-01-16 08:36:45 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-01-07 16:45:12 +01:00
|
|
|
|
|
2013-01-21 15:12:01 +00:00
|
|
|
|
#print "$srcas => $dstas ($noctets octets, version $ipversion, snmpin $snmpin, snmpout $snmpout)\n";
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
# determine direction and interface alias name (if known)
|
|
|
|
|
my $direction;
|
|
|
|
|
my $ifalias;
|
|
|
|
|
my $as;
|
|
|
|
|
|
|
|
|
|
if ($srcas == 0) {
|
|
|
|
|
$as = $dstas;
|
|
|
|
|
$direction = "out";
|
2014-06-14 20:31:07 +02:00
|
|
|
|
$ifalias = $knownlinks{inet_ntoa($routerip) . '_' . $snmpout . '/' . $vlanout} if defined($vlanout);
|
2014-06-14 20:16:05 +02:00
|
|
|
|
$ifalias //= $knownlinks{inet_ntoa($routerip) . '_' . $snmpout};
|
2008-01-16 08:36:45 +00:00
|
|
|
|
} elsif ($dstas == 0) {
|
|
|
|
|
$as = $srcas;
|
|
|
|
|
$direction = "in";
|
2014-06-14 20:31:07 +02:00
|
|
|
|
$ifalias = $knownlinks{inet_ntoa($routerip) . '_' . $snmpin . '/' . $vlanin} if defined($vlanin);
|
2014-06-14 20:16:05 +02:00
|
|
|
|
$ifalias //= $knownlinks{inet_ntoa($routerip) . '_' . $snmpin};
|
2008-01-16 08:36:45 +00:00
|
|
|
|
} else {
|
2014-06-14 20:16:05 +02:00
|
|
|
|
handleflow($routerip, $noctets, $srcas, 0, $snmpin, $snmpout, $ipversion, $vlanin, $vlanout);
|
|
|
|
|
handleflow($routerip, $noctets, 0, $dstas, $snmpin, $snmpout, $ipversion, $vlanin, $vlanout);
|
2008-01-16 08:36:45 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$ifalias) {
|
|
|
|
|
# ignore this, as it's through an interface we don't monitor
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-21 15:12:01 +00:00
|
|
|
|
my $dsname;
|
|
|
|
|
if ($ipversion == 6) {
|
|
|
|
|
$dsname = "${ifalias}_v6_${direction}";
|
|
|
|
|
} else {
|
|
|
|
|
$dsname = "${ifalias}_${direction}";
|
|
|
|
|
}
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
# put it into the cache
|
|
|
|
|
if (!$ascache->{$as}) {
|
|
|
|
|
$ascache->{$as} = {createts => time};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ascache->{$as}->{$dsname} += $noctets;
|
|
|
|
|
$ascache->{$as}->{updatets} = time;
|
|
|
|
|
|
|
|
|
|
if ($ascache->{$as}->{updatets} == $ascache_lastflush) {
|
|
|
|
|
# cheat a bit here
|
|
|
|
|
$ascache->{$as}->{updatets}++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# now flush the cache, if necessary
|
|
|
|
|
flush_cache();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub flush_cache {
|
|
|
|
|
if ($childrunning || ((time - $ascache_lastflush) < $ascache_flush_interval)) {
|
|
|
|
|
# can't/don't want to flush cache right now
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-03 17:21:32 +00:00
|
|
|
|
$childrunning = 1;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
my $pid = fork();
|
|
|
|
|
|
|
|
|
|
if (!defined $pid) {
|
2008-09-03 17:21:32 +00:00
|
|
|
|
$childrunning = 0;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
print "cannot fork\n";
|
|
|
|
|
} elsif ($pid != 0) {
|
|
|
|
|
# in parent
|
|
|
|
|
$ascache_lastflush = time;
|
2012-04-19 11:59:08 +00:00
|
|
|
|
for (keys %$ascache) {
|
|
|
|
|
if ($_ % 10 == $ascache_flush_number % 10) {
|
|
|
|
|
delete $ascache->{$_};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$ascache_flush_number++;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (my ($as, $cacheent) = each(%$ascache)) {
|
2012-04-19 11:59:08 +00:00
|
|
|
|
if ($as % 10 == $ascache_flush_number % 10) {
|
|
|
|
|
#print "$$: flushing data for AS $as ($cacheent->{updatets})\n";
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
2012-04-19 11:59:08 +00:00
|
|
|
|
my $rrdfile = getrrdfile($as, $cacheent->{updatets});
|
|
|
|
|
my @templatearg;
|
|
|
|
|
my @args;
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
2012-04-19 11:59:08 +00:00
|
|
|
|
while (my ($dsname, $value) = each(%$cacheent)) {
|
|
|
|
|
next if ($dsname !~ /_(in|out)$/);
|
2013-03-17 15:12:40 +00:00
|
|
|
|
|
|
|
|
|
my $tag = $dsname;
|
|
|
|
|
$tag =~ s/(_v6)?_(in|out)$//;
|
2014-01-07 16:45:12 +01:00
|
|
|
|
my $cursamplingrate = $link_samplingrates{$tag};
|
2013-03-17 15:12:40 +00:00
|
|
|
|
|
2012-04-19 11:59:08 +00:00
|
|
|
|
push(@templatearg, $dsname);
|
2013-03-17 15:12:40 +00:00
|
|
|
|
push(@args, $value * $cursamplingrate);
|
2012-04-19 11:59:08 +00:00
|
|
|
|
}
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
2012-04-19 11:59:08 +00:00
|
|
|
|
RRDs::update($rrdfile, "--template", join(':', @templatearg),
|
|
|
|
|
$cacheent->{updatets} . ":" . join(':', @args));
|
|
|
|
|
my $ERR = RRDs::error;
|
|
|
|
|
if ($ERR) {
|
|
|
|
|
print "Error updating RRD file $rrdfile: $ERR\n";
|
|
|
|
|
}
|
2008-01-16 08:36:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# create an RRD file for the given AS, if it doesn't exist already,
|
|
|
|
|
# and return its file name
|
|
|
|
|
sub getrrdfile {
|
|
|
|
|
my $as = shift;
|
|
|
|
|
my $startts = shift;
|
|
|
|
|
$startts--;
|
2014-01-07 16:45:12 +01:00
|
|
|
|
|
2009-10-10 16:53:25 +00:00
|
|
|
|
# we create 256 directories and store RRD files based on the lower
|
|
|
|
|
# 8 bytes of the AS number
|
|
|
|
|
my $dirname = "$rrdpath/" . sprintf("%02x", $as % 256);
|
|
|
|
|
if (! -d $dirname) {
|
|
|
|
|
# need to create directory
|
|
|
|
|
mkdir($dirname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $rrdfile = "$dirname/$as.rrd";
|
2008-01-16 08:36:45 +00:00
|
|
|
|
|
|
|
|
|
# let's see if there's already an RRD file for this AS - if not, create one
|
|
|
|
|
if (! -r $rrdfile) {
|
|
|
|
|
#print "$$: creating RRD file for AS $as\n";
|
|
|
|
|
|
2012-06-20 12:48:20 +00:00
|
|
|
|
my %links = map { $_, 1 } values %knownlinks;
|
|
|
|
|
|
2008-01-16 08:36:45 +00:00
|
|
|
|
my @args;
|
2012-06-20 12:48:20 +00:00
|
|
|
|
foreach my $alias (keys %links) {
|
2008-01-16 08:36:45 +00:00
|
|
|
|
push(@args, "DS:${alias}_in:ABSOLUTE:300:U:U");
|
|
|
|
|
push(@args, "DS:${alias}_out:ABSOLUTE:300:U:U");
|
2013-01-21 15:12:01 +00:00
|
|
|
|
push(@args, "DS:${alias}_v6_in:ABSOLUTE:300:U:U");
|
|
|
|
|
push(@args, "DS:${alias}_v6_out:ABSOLUTE:300:U:U");
|
2008-01-16 08:36:45 +00:00
|
|
|
|
}
|
2008-02-19 15:43:28 +00:00
|
|
|
|
push(@args, "RRA:AVERAGE:0.99999:1:576"); # 48 hours at 5 minute resolution
|
|
|
|
|
push(@args, "RRA:AVERAGE:0.99999:12:168"); # 1 week at 1 hour resolution
|
2013-01-21 15:12:01 +00:00
|
|
|
|
push(@args, "RRA:AVERAGE:0.99999:48:180"); # 1 month at 4 hour resolution
|
2008-02-19 15:43:28 +00:00
|
|
|
|
push(@args, "RRA:AVERAGE:0.99999:288:366"); # 1 year at 1 day resolution
|
2008-01-16 08:36:45 +00:00
|
|
|
|
RRDs::create($rrdfile, "--start", $startts, @args);
|
|
|
|
|
|
|
|
|
|
my $ERR = RRDs::error;
|
|
|
|
|
if ($ERR) {
|
|
|
|
|
print "Error creating RRD file $rrdfile: $ERR\n";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $rrdfile;
|
|
|
|
|
}
|
2008-02-19 15:43:28 +00:00
|
|
|
|
|
|
|
|
|
sub read_knownlinks {
|
2009-10-10 16:53:25 +00:00
|
|
|
|
my %knownlinks_tmp;
|
2013-03-17 15:12:40 +00:00
|
|
|
|
my %link_samplingrates_tmp;
|
2014-01-07 16:45:12 +01:00
|
|
|
|
open(KLFILE, $knownlinksfile) or die("Cannot open $knownlinksfile: $!");
|
2008-02-19 15:43:28 +00:00
|
|
|
|
while (<KLFILE>) {
|
|
|
|
|
chomp;
|
|
|
|
|
next if (/(^\s*#)|(^\s*$)/); # empty line or comment
|
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
my ($routerip,$ifindex,$tag,$descr,$color,$linksamplingrate) = split(/\t+/);
|
2009-10-10 16:53:25 +00:00
|
|
|
|
$knownlinks_tmp{"${routerip}_${ifindex}"} = $tag;
|
2013-03-17 15:12:40 +00:00
|
|
|
|
|
2014-01-07 16:45:12 +01:00
|
|
|
|
unless(defined($linksamplingrate) && $linksamplingrate =~ /^\d+$/) {
|
|
|
|
|
die("ERROR: No samplingrate for ".$routerip."\n");
|
2013-03-17 15:12:40 +00:00
|
|
|
|
}
|
2014-01-07 16:45:12 +01:00
|
|
|
|
|
|
|
|
|
$link_samplingrates_tmp{$tag} = $linksamplingrate;
|
2008-02-19 15:43:28 +00:00
|
|
|
|
}
|
|
|
|
|
close(KLFILE);
|
2009-10-10 16:53:25 +00:00
|
|
|
|
|
|
|
|
|
%knownlinks = %knownlinks_tmp;
|
2013-03-17 15:12:40 +00:00
|
|
|
|
%link_samplingrates = %link_samplingrates_tmp;
|
2009-10-10 16:53:25 +00:00
|
|
|
|
return;
|
2008-02-19 15:43:28 +00:00
|
|
|
|
}
|
2014-01-07 16:45:12 +01:00
|
|
|
|
|