#!/usr/bin/perl5 # ip-leecher.pl # (C) 2003 Kai Schlichting - All rights reserved # # V1.1 20031101 New route-server: route-views.wide.routeviews.org # Route-server removed from default: route-server.bbnplanet.net (dead?) # Deal properly with Net::Telnet::Cisco problem: it ignores the returned # "% Network not in table" and pretends that it's been a time-out # # # history: # V1.0 - first public release # $cfg_rs = "route-views2.oregon-ix.net"; $cfg_rs = ""; $cfg_rs_login = ""; $cfg_rs_pass = ""; my $route; # my %rs_stats; # dbmopen (%rs_stats,"rs_stats",0600); # foreach $key (keys %rs_stats) { # print "router: $key, num: $rs_stats{$key}{'num'}, time: $rs_stats{$key}{'time'}\n"; # } @cfg_route_servers = ( # 'route-views.oregon-ix.net', 'route-views2.oregon-ix.net', 'route-views.wide.routeviews.org', 'route-server.cerf.net', 'route-server.exodus.net', 'route-server-eu.exodus.net', 'route-server.as5388.net', 'route-server.gblx.net', 'route-server.colt.net', # 'route-server.ip.att.net', # sometimes unreliable (doesn't return anything for proper queries) # 'route-server.bbnplanet.net', # down since 10/2003? # 'route-server.opentransit.net', # can't use it, as they current don't allow 'term length 0' # 'zebra.swinog.ch', # unstable? swinog.ch and frnog.org ) ; my $ip = shift ; # or STDIN while (1) { ($route,$asn) = net2asn_lookup2 ($ip); if ( $asn =~ /error\s(.*)$/ ) { print"Error: $1\n"; } else { print "$ip announced via route $route from AS $asn\n"; last; } print "retrying due to error.\n"; } # dbmclose (%rs_stats); exit; sub net2asn_lookup2 { # this sub determines the announcing autonomous system number (ASN) # and announced prefix for a given IP number based on information # obtained from route-servers or dedicated Cisco router, accessible # via telnet. # returns list of matched route and either: # comma-separated list of ASNs or # 'none' or # 'error ...' (error message) my $ip = $_[0]; my $sn = "net2asn_lookup2"; # sub name my $router; my $login; my $pass; my @route_servers; my $num_rs; my $session; my $timeout = 15; # seconds - some route-servers are VERY busy, but we have no time my $buf_size = 1024 * 1024; # make our buffers a bit bigger my $as; my $cmd; my @output; my $out_lines; my @as_list; my $line; my %as_list; my $route; my $return; my $query_time; my $start = time; use Net::Telnet::Cisco; # http://nettelnetcisco.sourceforge.net/docs.html # www.CPAN.org is your friend installing this module: # 1. Run the CPAN shell from your command-line: # $ perl -MCPAN -e shell # 2. If this is your first time running CPAN, it will ask you a number of setup questions. # 3. Install the module: # cpan> install Net::Telnet::Cisco $router = $cfg_rs; # use your own router whenever possible to avoid killing the route-servers # most route-servers PROHIBIT scripted/automated use - you may be banned from # accessing them PERMANENTLY if the operators deem you to abuse their resources! # if in doubt, make arrangements with an operator (or owner of a non-public # route-server or owner of a BGP4-speaking router) $login = $cfg_rs_login; # login for route-server, if any $pass = $cfg_rs_pass; # password for route-server, if any if ($router eq "") { $num_rs = (@cfg_route_servers); $router = @cfg_route_servers[(rand $num_rs)]; print "# Randomly selected router $router\n"; } else { print "# Using router $router\n"; } # open the session unless ( $session = Net::Telnet::Cisco->new(Host => $router, errmode => "return") ) { return ("return","error cant connect to $router"); } $session->max_buffer_length($buf_size); # $session->errmode("return"); # don't die on errors, but return # print "# Logging into router $router\n"; # log in unless ( $session->login($login,$pass) ) { return ("none","error logging into $router"); } # print "# Setting NO PAGING\n"; # no paging unless ( $session->cmd(String => 'terminal length 0', Timeout => 10) ) { return ("none","error setting term length"); } $cmd = "sh ip bgp $ip" ; # print "# using command: $cmd\n"; unless ( @output = $session->cmd(String => $cmd, Timeout => $timeout) ) { if ( (time - $start) < $timeout ) { # Net::Telnet::Cisco ignores the returned "% Network not in table" and pretends that it's been a time-out return ("none","none"); } else { return ("none","none"); } } $query_time = time - $start; $out_lines = @output; LINE: foreach $line (@output) { # print "got line: $line"; if ($line =~ /^\s\s\d+.*$/ ) { # we got a line with an AS path # print "got interesting line: $line"; $line =~ s/,\s\(.*// ; # the forestanding regexp properly handles cases like: # 17233 7018 22057, (received & used) # 2493 3602 1239 3356 6517, (aggregated by 6517 172.31.30.1) # 7500 2516 3356 6517 {22551,25918}, (aggregated by 6517 172.31.68.2) if ( $line =~ /^.*\s(\d+)$/ ) { # this weeds out the last AS, and deals with a special case: # seen on route-server.ip.att.net: # " 12.161.130.230" as the first line after the route $as = $1; # print "IP $ip in network $route originates from AS $as\n"; $as_list{$as} = ""; # to weed out duplicates } elsif ( $line =~ /^.*\s\{(.*?)\}$/ ) { # aggregation case like: "... 0 11608 2914 3356 14390 {22714,27481} i" # this will only ever work if we are within the longest prefix overing this IP $as = $1; # print "special aggregation case: adding ASN's $as\n"; my @multiple = split /,/,$as ; my $n; foreach $n (@multiple) { $as_list{$n} = ""; } } else { # print "problem separating AS in line $line"; } } elsif ($line =~ /^BGP routing table entry for\s(.*\/\d{1,2}).*$/ ) { # typically the first line returned $route = $1; # print "IP $ip is in originating route: $route\n" } elsif ($line =~ /^.*Network not in table.*$/ ) { # Net::Telnet::Cisco never gets us this - in the future maybe? # this is typically the only line returned # print "IP $ip is not in routing table\n" } else { # de-comment this for debugging # print "ERROR: can't parse/ignoring line $line"; } } # foreach $line # print "\nFinal: IP $ip is in network $route, origin AS(s):"; foreach $as (keys %as_list) { # print " $as"; $return .= "$as,"; } # print "\n\n"; # my $old_time_avrg = $rs_stats{"$router time"} || 0 ; # my $old_time_num = $rs_stats{"$router num"} || 0 ; # my $new_time_avrg = ($old_time_avrg * 0.80) + ($query_time * 0.20); # count current result as 20% # print "Lookup in $query_time seconds with $router - history: $old_time_num queries, $old_time_avrg s time avrg.\n"; # $rs_stats{"$router time"} = $new_time_avrg; # if (!defined $rs_stats{"$router num"} ) { # $rs_stats{"$router num"} = 1; # } else { # $rs_stats{"$router num"} += 1; # } # print "now storing: time: $rs_stats{\"$router time\"}, number: $rs_stats{\"$router num\"}\n"; if ($return) { # the trailing "," chop $return; return ($route,$return); } else { return ("none"); } }