#!/usr/bin/perl -wT use strict; $|=1; # nobogons.pl - IPv4 address via STDIN, no bogons to STDOUT # also see: http://www.cymru.com/Documents/bogon-bn-agg.txt # $Id: nobogons.pl,v 1.3 2009/10/15 21:22:23 jtk Exp $ use Net::Patricia; use Getopt::Std; use constant FALSE => 0; use constant TRUE => 1; my $pt_bogon = new Net::Patricia; getopts( 'c:q', \ my %opts ); my @bogons; if ( $opts{c} ) { parse_config( $opts{c} ); } else { # current as of 2009-07-27 @bogons = qw( 0.0.0.0/7 2.0.0.0/8 5.0.0.0/8 10.0.0.0/8 14.0.0.0/8 23.0.0.0/8 27.0.0.0/8 31.0.0.0/8 36.0.0.0/7 39.0.0.0/8 42.0.0.0/8 46.0.0.0/8 49.0.0.0/8 50.0.0.0/8 100.0.0.0/6 104.0.0.0/6 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 175.0.0.0/8 176.0.0.0/7 179.0.0.0/8 181.0.0.0/8 182.0.0.0/8 185.0.0.0/8 192.0.2.0/24 192.168.0.0/16 198.18.0.0/15 223.0.0.0/8 224.0.0.0/3 ); for (@bogons) { $pt_bogon->add_string($_); } } # (-q) quick - no input verification # be sure input is a per line, left-aligned, valid IPv4 dotted decimal if ( $opts{q} ) { while ( defined(my $line=<>) ) { chomp $line; # bogon check next if $pt_bogon->match_string($line); # not a bogon, print it out print "$line\n"; } } # default, input validation, slower else { while ( defined(my $line=<>) ) { chomp $line; # valid IPv4 dotted decimal check next if invalid_addr($line); # bogon check next if $pt_bogon->match_string($line); # not a bogon, print it out print "$line\n"; } } # IPv4 octet and address range check sub invalid_addr { my $addr = shift; # skip blank lines or comments next if $addr =~ m{ \A \s* (?: [#] [.]* )? \Z }xms; # remove leading spaces $addr =~ s{ \A \s* }{}xms; # remove trailing spaces $addr =~ s{ \s* \Z }{}xms; # weak IPv4 address format check return TRUE if $addr !~ m{ \A \d{1,3} (?: [.] \d{1,3} ){3} \Z }xms; # each octet should be 0 to 255 my @bytes = split /\./, $addr; for my $byte (@bytes) { return TRUE if $byte > 255; } my $int = unpack( 'N', pack('C4', @bytes) ); return TRUE if $int > 4294967295; # 255.255.255.255 = int 4294967295 } # parse a file that should contain one IPv4 dotted decimail per line sub parse_config { my $filename = shift; open( my $CONFIG_FILE, '<', $filename) or die "Unable to open $filename: $!\n"; while (defined (my $line=<$CONFIG_FILE>) ) { chomp $line; # skip blank lines or comments next if $line =~ m{ \A \s* (?: [#] [.]* )? \Z }xms; # remove leading spaces $line =~ s{ \A \s* }{}xms; # remove trailing spaces $line =~ s{ \s* \Z }{}xms; # skip anything that doesn't look like a CIDR block, compact is OK next if $line !~ m{ \A \d{1,3} (?: [.] \d{1,3} ){1,3} [/] \d{1,2} \Z }xms; # push CIDR block onto bogon array $pt_bogon->add_string($line); } }