commit 8238a25: [Feature] Asn: Allow to use bgpdump when NET::MRT is broken
Vsevolod Stakhov
vsevolod at highsecure.ru
Tue Jul 16 09:07:04 UTC 2019
Author: Vsevolod Stakhov
Date: 2019-07-16 10:01:26 +0100
URL: https://github.com/rspamd/rspamd/commit/8238a25077f93f027119defe5dd010d3284e85de (HEAD -> master)
[Feature] Asn: Allow to use bgpdump when NET::MRT is broken
---
utils/asn.pl | 145 +++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 121 insertions(+), 24 deletions(-)
diff --git a/utils/asn.pl b/utils/asn.pl
index fe5436354..42554cf5a 100644
--- a/utils/asn.pl
+++ b/utils/asn.pl
@@ -39,6 +39,7 @@ my $v6_zone = "asn6.rspamd.com";
my $v4_file = "asn.zone";
my $v6_file = "asn6.zone";
my $ns_servers = [ "asn-ns.rspamd.com", "asn-ns2.rspamd.com" ];
+my $use_bgpdump = 0;
GetOptions(
"download-asn" => \$download_asn,
@@ -53,7 +54,8 @@ GetOptions(
"file-v6=s" => \$v6_file,
"ns-server=s@" => \$ns_servers,
"help|?" => \$help,
- "man" => \$man
+ "man" => \$man,
+ "bgpdump" => \$use_bgpdump
) or pod2usage(2);
pod2usage(1) if $help;
@@ -103,41 +105,135 @@ if ($v6) {
}
}
+sub is_bougus_asn {
+ my $as = shift;
+ # 64496-64511 Reserved for use in documentation and sample code
+ # 64512-65534 Designated for private use
+ # 65535 Reserved
+ # 65536-65551 Reserved for use in documentation and sample code
+ # 65552-131071 Reserved
+ return 1 if $as >= 64496 && $as <= 131071;
+ # 4294967295
+ return 1 if $as == 4294967295;
+ # AS0 is reserved
+ # AS1 is legal AS, but in most cases used by others without permission
+ # of owner (probably lame admins use AS1 as private AS).
+ return 1 if $as <= 1;
+
+ return 0;
+}
+
# Now load BGP data
my $networks = {};
foreach my $u ( @{ $config{'bgp_sources'} } ) {
my $parsed = URI->new($u);
my $fname = $download_target . '/' . basename( $parsed->path );
- open( my $fh, "<:gzip", $fname )
- or die "Cannot open $fname: $!";
-
- while ( my $dd = eval { Net::MRT::mrt_read_next($fh) } ) {
- if ( $dd->{'prefix'} && $dd->{'bits'} ) {
- next if $dd->{'subtype'} == 2 and !$v4;
- next if $dd->{'subtype'} == 4 and !$v6;
- my $entry = $dd->{'entries'}->[0];
- my $net = $dd->{'prefix'} . '/' . $dd->{'bits'};
- if ( $entry && $entry->{'AS_PATH'} ) {
- my $as = $entry->{'AS_PATH'}->[-1];
- if ( ref($as) eq "ARRAY" ) {
- $as = @{$as}[0];
- }
- if ( !$networks->{$as} ) {
- if ( $dd->{'subtype'} == 2 ) {
- $networks->{$as} = { nets_v4 => [$net], nets_v6 => [] };
- }
- else {
- $networks->{$as} = { nets_v6 => [$net], nets_v4 => [] };
+ if ($use_bgpdump) {
+ use constant {
+ F_MARKER => 0,
+ F_TIMESTAMP => 1,
+ F_PEER_IP => 3,
+ F_PEER_AS => 4,
+ F_PREFIX => 5,
+ F_AS_PATH => 6,
+ F_ORIGIN => 7,
+ };
+
+ open(my $bgpd, "bgpdump -M $fname |") or die "can't start bgpdump: $!";
+
+ while (<$bgpd>) {
+ chomp;
+ my @e = split /\|/;
+ if ($e[F_MARKER] ne 'TABLE_DUMP2') {
+ warn "bad line: $_\n";
+ next;
+ }
+
+ my $origin_as;
+ my $prefix = $e[F_PREFIX];
+ my $ipv6 = 0;
+
+ if ($prefix !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/) {
+ $ipv6 = 1;
+ }
+
+ if ($e[F_AS_PATH]) {
+ # not empty AS_PATH
+ my @as_path = split /\s/, $e[F_AS_PATH];
+ $origin_as = pop @as_path;
+
+ if (substr($origin_as, 0, 1) eq '{') {
+ # route is aggregated
+ if ($origin_as =~ /^{(\d+)}$/) {
+ # single AS aggregated, just remove { } around
+ $origin_as = $1;
+ } else {
+ # use previous AS from AS_PATH
+ $origin_as = pop @as_path;
}
}
+ # strip bogus AS
+ while (is_bougus_asn($origin_as)) {
+ $origin_as = pop @as_path;
+ last if scalar @as_path == 0;
+ }
+ }
+ # empty AS_PATH or all AS_PATH elements is stripped as bogus - use PEER_AS is origin AS
+ $origin_as //= $e[F_PEER_AS];
+
+ if (!$networks->{$origin_as}) {
+ if (!$ipv6) {
+ $networks->{$origin_as} = { nets_v4 => [ $prefix ], nets_v6 => [] };
+ }
else {
- if ( $dd->{'subtype'} == 2 ) {
- push @{ $networks->{$as}->{'nets_v4'} }, $net;
+ $networks->{$origin_as} = { nets_v6 => [ $prefix ], nets_v4 => [] };
+ }
+ }
+ else {
+ if (!$ipv6) {
+ push @{$networks->{$origin_as}->{'nets_v4'}}, $prefix;
+ }
+ else {
+ push @{$networks->{$origin_as}->{'nets_v6'}}, $prefix;
+ }
+ }
+ }
+ }
+ else {
+ open( my $fh, "<:gzip", $fname )
+ or die "Cannot open $fname: $!";
+ while (my $dd = eval {Net::MRT::mrt_read_next($fh)}) {
+ if ($dd->{'prefix'} && $dd->{'bits'}) {
+ next if $dd->{'subtype'} == 2 and !$v4;
+ next if $dd->{'subtype'} == 4 and !$v6;
+ my $entry = $dd->{'entries'}->[0];
+ my $net = $dd->{'prefix'} . '/' . $dd->{'bits'};
+ if ($entry && $entry->{'AS_PATH'}) {
+ my $as = $entry->{'AS_PATH'}->[-1];
+ if (ref($as) eq "ARRAY") {
+ $as = @{$as}[0];
+ }
+
+ next if (is_bougus_asn($as));
+
+ if (!$networks->{$as}) {
+ if ($dd->{'subtype'} == 2) {
+ $networks->{$as} = { nets_v4 => [ $net ], nets_v6 => [] };
+ }
+ else {
+ $networks->{$as} = { nets_v6 => [ $net ], nets_v4 => [] };
+ }
}
else {
- push @{ $networks->{$as}->{'nets_v6'} }, $net;
+
+ if ($dd->{'subtype'} == 2) {
+ push @{$networks->{$as}->{'nets_v4'}}, $net;
+ }
+ else {
+ push @{$networks->{$as}->{'nets_v6'}}, $net;
+ }
}
}
}
@@ -221,6 +317,7 @@ asn.pl [options]
--zone-v6 IPv6 zone (default: asn6.rspamd.com)
--file-v4 IPv4 zone file (default: ./asn.zone)
--file-v6 IPv6 zone (default: ./asn6.zone)
+ --bgpdump Use bgpdump utility instead of NET::MRT
--help Brief help message
--man Full documentation
More information about the Commits
mailing list