Merge pull request #67 from JackSlateur/master

Support optional IP to ASN mapping for netflow v5 / sFlow
This commit is contained in:
Manuel Kasper 2017-04-02 15:47:09 +02:00 committed by GitHub
commit 5b1f2af37a
2 changed files with 69 additions and 4 deletions

View File

@ -45,6 +45,7 @@ Prerequisites
- libdbd-sqlite3-perl - libdbd-sqlite3-perl
- one or more routers than can generate NetFlow v8/v9 AS aggregation records - one or more routers than can generate NetFlow v8/v9 AS aggregation records
or sFlow samples or sFlow samples
- ip2as.pm, for additional lookup (https://github.com/JackSlateur/perl-ip2as)
Considerations Considerations
------------- -------------

View File

@ -12,6 +12,7 @@ use IO::Socket;
use RRDs; use RRDs;
use Getopt::Std; use Getopt::Std;
use Scalar::Util qw(looks_like_number); use Scalar::Util qw(looks_like_number);
use ip2as;
my %knownlinks; my %knownlinks;
my %link_samplingrates; my %link_samplingrates;
@ -39,7 +40,7 @@ my $v10_templates = {};
my $sflow_server_port = 6343; my $sflow_server_port = 6343;
use vars qw/ %opt /; use vars qw/ %opt /;
getopts('r:p:P:k:a:n', \%opt); getopts('r:p:P:k:a:nm:', \%opt);
my $usage = "$0 [-rpPka]\n". my $usage = "$0 [-rpPka]\n".
"\t-r <path to RRD files>\n". "\t-r <path to RRD files>\n".
@ -47,12 +48,14 @@ my $usage = "$0 [-rpPka]\n".
"\t(-P <sFlow UDP listen port - default $sflow_server_port, use 0 to disable sFlow)\n". "\t(-P <sFlow UDP listen port - default $sflow_server_port, use 0 to disable sFlow)\n".
"\t-k <path to known links file>\n". "\t-k <path to known links file>\n".
"\t-a <your own AS number> - only required for sFlow\n". "\t-a <your own AS number> - only required for sFlow\n".
"\t-n enable peer-as statistics\n"; "\t-n enable peer-as statistics\n".
"\t-m IP<->ASN mapping\n";
my $rrdpath = $opt{'r'}; my $rrdpath = $opt{'r'};
my $knownlinksfile = $opt{'k'}; my $knownlinksfile = $opt{'k'};
my $myas_opt = $opt{'a'}; my $myas_opt = $opt{'a'};
my $peerasstats = $opt{'n'}; my $peerasstats = $opt{'n'};
my $mapping = $opt{'m'};
die("$usage") if (!defined($rrdpath) || !defined($knownlinksfile)); die("$usage") if (!defined($rrdpath) || !defined($knownlinksfile));
@ -129,6 +132,13 @@ if ($sflow_server_port > 0) {
my ($him,$datagram,$flags); my ($him,$datagram,$flags);
if (defined($mapping)) {
ip2as::init($mapping);
} else {
#I don't use the mapping, to use an empty one
ip2as::init('/dev/null');
}
# main datagram receive loop # main datagram receive loop
while (1) { while (1) {
while (my @ready = $sel->can_read) { while (my @ready = $sel->can_read) {
@ -160,6 +170,18 @@ while (1) {
} }
} }
sub replace_asn {
my $ip = shift;
my $asn = shift;
my $new_asn = ip2as::getas4ip($ip);
if (defined($new_asn)) {
return $new_asn;
} else {
return $asn;
}
}
sub parse_netflow_v5 { sub parse_netflow_v5 {
my $datagram = shift; my $datagram = shift;
my $ipaddr = shift; my $ipaddr = shift;
@ -173,8 +195,14 @@ sub parse_netflow_v5 {
for (my $i = 0; $i < $count; $i++) { for (my $i = 0; $i < $count; $i++) {
my $flowrec = substr($datagram, $v5_header_len + ($i*$v5_flowrec_len), $v5_flowrec_len); my $flowrec = substr($datagram, $v5_header_len + ($i*$v5_flowrec_len), $v5_flowrec_len);
my @flowdata = unpack("NNNnnNNNNnnccccnnccN", $flowrec); my @flowdata = unpack("NNNnnNNNNnnccccnnccN", $flowrec);
#print "ipaddr: " . inet_ntoa($ipaddr) . " octets: $flowdata[6] srcas: $flowdata[15] dstas: $flowdata[16] in: $flowdata[3] out: $flowdata[4] 4 \n"; my $srcip = join '.', unpack 'C4', pack 'N', $flowdata[0];
handleflow($ipaddr, $flowdata[6], $flowdata[15], $flowdata[16], $flowdata[3], $flowdata[4], 4, 'netflow'); my $dstip = join '.', unpack 'C4', pack 'N', $flowdata[1];
my $srcas = replace_asn($srcip, $flowdata[15]);
my $dstas = replace_asn($dstip, $flowdata[16]);
#print "ipaddr: " . inet_ntoa($ipaddr) . " octets: $flowdata[6] srcas: $srcas dstas: $dstas in: $flowdata[3] out: $flowdata[4] 4 \n";
handleflow($ipaddr, $flowdata[6], $srcas, $dstas, $flowdata[3], $flowdata[4], 4, 'netflow');
} }
} }
@ -563,6 +591,42 @@ sub parse_sflow {
} }
} }
# Extract src & dst IP from packet's header
my $srcip = undef;
my $dstip= undef;
my (undef, $ethertype, $ipdata) = unpack('a12H4a*', $sFlowSample->{'HeaderBin'});
if($ethertype eq '8100'){
(undef, $ethertype, $ipdata) = unpack('nH4a*', $ipdata);
}
if($ethertype eq '0800'){
(undef, undef, undef, $srcip, $dstip) = unpack('nnB64NN', $ipdata);
$srcip = join '.', unpack 'C4', pack 'N', $srcip;
$dstip = join '.', unpack 'C4', pack 'N', $dstip;
}
if($ethertype eq '86dd'){
(undef, $sFlowSample->{HeaderDatalen}, undef, $srcip, $dstip) = unpack('NnnB128B128', $ipdata);
my @array_src = ( $srcip =~ m/......../g );
my @array_dst = ( $dstip =~ m/......../g );
$srcip = '';
$dstip = '';
for(my $x = 0; $x < scalar @array_src; $x = $x + 2){
$srcip .= sprintf("%02x%02x:", oct("0b$array_src[$x]"), oct("0b$array_src[$x + 1]"));
}
chop($srcip);
for(my $x = 0; $x < scalar @array_dst; $x = $x + 2){
$dstip .= sprintf("%02x%02x:", oct("0b$array_dst[$x]"), oct("0b$array_dst[$x + 1]"));
}
chop($dstip);
}
if (defined($srcip) && defined($dstip)){
$srcas = replace_asn($srcip, $srcas);
$dstas = replace_asn($dstip, $dstas);
}
# Outbound packets have our AS number as the source (GatewayAsSource), # Outbound packets have our AS number as the source (GatewayAsSource),
# while inbound packets have 0 as the destination (empty AsPath). # while inbound packets have 0 as the destination (empty AsPath).
# Transit packets have "foreign" AS numbers for both source and # Transit packets have "foreign" AS numbers for both source and