#!/usr/bin/perl # Copyright (C) 2006-2007 Unregistered DNS # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. sub printrule { my $Address=$_[0]; my $AddressCount=$_[1]; my $FilterType=$_[2]; my $SubnetMaskBin = sprintf "%b", scalar($AddressCount); my $SubnetMaskBinCnt = $SubnetMaskBin; $SubnetMaskBinCnt =~ s/1//g; my $SubnetBit = (32 - length($SubnetMaskBinCnt)); # calc subnet mask my $SubnetMaskBit=(1 << 31); my $SubnetMaskVal=0; for($j=0;$j<$SubnetBit;$j++) { $SubnetMaskVal|=$SubnetMaskBit; $SubnetMaskBit>>=1; } my $SubnetMaskVal1=($SubnetMaskVal >> 24)&255; my $SubnetMaskVal2=($SubnetMaskVal >> 16)&255; my $SubnetMaskVal3=($SubnetMaskVal >> 8)&255; my $SubnetMaskVal4=$SubnetMaskVal&255; # write it if ($FilterType == 1) { # iptables print "\$IPTABLES -A \$FILTERNAME -s $Address/$SubnetBit -j \$TARGET\n"; } elsif ($FilterType == 2) { # ipfw print "\$IPFW add \$RULENUM \$ACTION ip from $Address/$SubnetBit to any\n"; } elsif ($FilterType == 3) { # windows print "routing ip add filter name=\"ローカル エリア接続\" filtertype=INPUT srcaddr=$Address srcmask=$SubnetMaskVal1.$SubnetMaskVal2.$SubnetMaskVal3.$SubnetMaskVal4 dstaddr=0.0.0.0 dstmask=0.0.0.0 proto=any\n"; } } if ($#ARGV < 1) { print STDERR "Usage: countryfilter.pl (Filter Type) (Target Countries)\n"; print STDERR "\n"; print STDERR "(Filter Type): A filter name (\"iptables\" or \"ipfw\" or \"windows\").\n"; print STDERR "(Target Countries): Target countries seperated with comma (ex. \"KR,CN,KP\").\n"; exit; } $FilterTypeStr = lc(@ARGV[0]); @TargetCountry = split(/\,/, @ARGV[1]); if ($FilterTypeStr eq "iptables") { $FilterType=1; } elsif ($FilterTypeStr eq "ipfw") { $FilterType=2; } elsif ($FilterTypeStr eq "windows") { $FilterType=3; } else { print STDERR "Error: Filter type must be either \"iptables\" or \"ipfw\" or \"windows\".\n"; exit; } if ($FilterType != 3) { # not windows print "#!/bin/sh\n\n"; $CommentChar = "#"; } else { $CommentChar = "#"; } print "$CommentChar Country based filter from *NIC database.\n"; print "$CommentChar For APNIC, get from ftp://ftp.apnic.net/pub/apnic/stats/apnic/delegated-apnic-latest .\n"; print "$CommentChar Created: " . localtime(time) . "\n"; print "$CommentChar\n"; print "$CommentChar This filter detects access from contries;\n"; print "$CommentChar "; for($i=0;$i<=$#TargetCountry;$i++) { print @TargetCountry[$i] . " "; } print "\n\n"; print "$CommentChar variables. change these values before run.\n"; if ($FilterType == 1) { # iptables print "IPTABLES=/sbin/iptables\n"; print "FILTERNAME=CKFILTER\n"; print "TARGET=CKFILTERED\n"; } elsif ($FilterType == 2) { # ipfw print "IPFW=/usr/bin/ipfw\n"; print "RULENUM=10000\n"; print "ACTION=deny\n"; } elsif ($FilterType == 3) { # windows print "$CommentChar Nothing. sorry...\n"; print "\n"; print "offline\n"; } print "\n"; while () { @TextArray = split(/\|/, $_); # parse only ipv4, ipv6 if (lc(@TextArray[2]) eq "ipv4") { # check country $found=0; for($i=0;$i<=$#TargetCountry;$i++) { if (lc(@TextArray[1]) eq lc(@TargetCountry[$i])) { # check whether number of addresses is CIDR-ready or not. $SubnetMaskBin = sprintf "%b", scalar(@TextArray[4]); $SubnetMaskBinCnt = $SubnetMaskBin; $SubnetMaskBinCnt =~ s/0//g; if (length($SubnetMaskBinCnt) > 1) { print "$CommentChar Notice: @TextArray[3]/\#@TextArray[4] ($SubnetMaskBin) is not CIDR-ready number; splitted.\n"; } @AddressArray = split(/\./, @TextArray[3]); if ($#AddressArray < 3) { print "$CommentChar Error: \"@TextArray[3]\" should have 4 fields.\n"; last; } $AddressValue = scalar(@AddressArray[0]) << 24; $AddressValue |= scalar(@AddressArray[1]) << 16; $AddressValue |= scalar(@AddressArray[2]) << 8; $AddressValue |= scalar(@AddressArray[3]); for ($i=0;$i 0) { $AddressStr = sprintf "%d.", ($AddressValue >> 24) & 255; $AddressStr .= sprintf "%d.", ($AddressValue >> 16) & 255; $AddressStr .= sprintf "%d.", ($AddressValue >> 8) & 255; $AddressStr .= sprintf "%d", $AddressValue & 255; print "$CommentChar Notice: $AddressStr/\#$AddressCount goes across the subnet boundary. splitted.\n"; while ($AddressCount > 0) { $AddressCountSub = $AddressCount; while (($AddressValue & ($AddressCountSub - 1)) > 0) { $AddressCountSub /= 2; } $AddressStr = sprintf "%d.", ($AddressValue >> 24) & 255; $AddressStr .= sprintf "%d.", ($AddressValue >> 16) & 255; $AddressStr .= sprintf "%d.", ($AddressValue >> 8) & 255; $AddressStr .= sprintf "%d", $AddressValue & 255; printrule($AddressStr, $AddressCountSub, $FilterType); $AddressValue += $AddressCountSub; $AddressCount -= $AddressCountSub; } } else { $AddressStr = sprintf "%d.", ($AddressValue >> 24) & 255; $AddressStr .= sprintf "%d.", ($AddressValue >> 16) & 255; $AddressStr .= sprintf "%d.", ($AddressValue >> 8) & 255; $AddressStr .= sprintf "%d", $AddressValue & 255; $AddressCount = 2 ** (length($SubnetMaskBin) - $i - 1); printrule($AddressStr, $AddressCount, $FilterType); $AddressValue += $AddressCount; } } } last; } } } elsif (lc(@TextArray[3]) eq "ipv6") { } elsif (lc(@TextArray[0]) eq "2") { # database version print "$CommentChar Database version " . @TextArray[2] . "\n"; } } if ($FilterType == 3) { # windows print "routing ip set filter name=\"ローカル エリア接続\" filtertype=INPUT action=FORWARD\n"; print "commit\n"; }