diff --git a/config/cfgroot/general-functions.pl b/config/cfgroot/general-functions.pl index 6994f333d..66286ed1e 100644 --- a/config/cfgroot/general-functions.pl +++ b/config/cfgroot/general-functions.pl @@ -26,6 +26,8 @@ $General::swroot = 'CONFIG_ROOT'; $General::noipprefix = 'noipg-'; $General::adminmanualurl = 'http://wiki.ipfire.org'; +require "${General::swroot}/network-functions.pl"; + # # log ("message") use default 'ipcop' tag # log ("tag","message") use your tag @@ -281,21 +283,10 @@ sub validip } } -sub validmask -{ - my $mask = $_[0]; +sub validmask { + my $mask = shift; - # secord part an ip? - if (&validip($mask)) { - return 1; } - # second part a number? - if (/^0/) { - return 0; } - if (!($mask =~ /^\d+$/)) { - return 0; } - if ($mask >= 0 && $mask <= 32) { - return 1; } - return 0; + return &Network::check_netmask($mask) or &Network::check_prefix($mask); } sub validipormask @@ -316,24 +307,12 @@ sub validipormask return &validmask($mask); } -sub subtocidr -{ - #gets: Subnet in decimal (255.255.255.0) - #Gives: 24 (The cidr of network) - my ($byte1, $byte2, $byte3, $byte4) = split(/\./, $_[0].".0.0.0.0"); - my $num = ($byte1 * 16777216) + ($byte2 * 65536) + ($byte3 * 256) + $byte4; - my $bin = unpack("B*", pack("N", $num)); - my $count = ($bin =~ tr/1/1/); - return $count; +sub subtocidr { + return &Network::convert_netmask2prefix(shift); } -sub cidrtosub -{ - #gets: Cidr of network (20-30 for ccd) - #Konverts 30 to 255.255.255.252 e.g - my $cidr=$_[0]; - my $netmask = &Net::IPv4Addr::ipv4_cidr2msk($cidr); - return "$netmask"; +sub cidrtosub { + return &Network::convert_prefix2netmask(shift); } sub iporsubtodec @@ -408,15 +387,8 @@ sub iporsubtocidr return 3; } -sub getnetworkip -{ - #Gets: IP, CIDR (10.10.10.0-255, 24) - #Gives: 10.10.10.0 - my ($ccdip,$ccdsubnet) = @_; - my $ip_address_binary = &Socket::inet_pton( AF_INET,$ccdip ); - my $netmask_binary = &Socket::inet_pton(AF_INET,&iporsubtodec($ccdsubnet)); - my $network_address = &Socket::inet_ntop( AF_INET,$ip_address_binary & $netmask_binary ); - return $network_address; +sub getnetworkip { + return &Network::get_netaddress(shift); } sub getccdbc @@ -431,46 +403,20 @@ sub getccdbc return $broadcast_address; } -sub ip2dec -{ - my $ip_num; - my $ip=$_[0]; - if ( $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/ ) { - $ip_num = (($1*256**3) + ($2*256**2) + ($3*256) + $4); - } else { - $ip_num = -1; - } - $ip_num = (($1*256**3) + ($2*256**2) + ($3*256) + $4); - return($ip_num); +sub ip2dec { + return &Network::ip2bin(shift); } -sub dec2ip -{ - my $ip; - my $ip_num=$_[0]; - my $o1=$ip_num%256; - $ip_num=int($ip_num/256); - my $o2=$ip_num%256; - $ip_num=int($ip_num/256); - my $o3=$ip_num%256; - $ip_num=int($ip_num/256); - my $o4=$ip_num%256; - $ip="$o4.$o3.$o2.$o1"; - return ($ip); +sub dec2ip { + return &Network::bin2ip(shift); } -sub getnextip -{ - my $decip=&ip2dec($_[0]); - $decip=$decip+4; - return &dec2ip($decip); +sub getnextip { + return &Network::find_next_ip_address(shift, 4); } -sub getlastip -{ - my $decip=&ip2dec($_[0]); - $decip--; - return &dec2ip($decip); +sub getlastip { + return &Network::find_next_ip_address(shift, -1); } sub validipandmask @@ -766,28 +712,12 @@ sub validportrange # used to check a port range } } -# Test if IP is within a subnet -# Call: IpInSubnet (Addr, Subnet, Subnet Mask) -# Subnet can be an IP of the subnet: 10.0.0.0 or 10.0.0.1 -# Everything in dottted notation -# Return: TRUE/FALSE -sub IpInSubnet -{ +sub IpInSubnet { my $addr = shift; my $network = shift; my $netmask = shift; - my $addr_num = &Socket::inet_pton(AF_INET,$addr); - my $network_num = &Socket::inet_pton(AF_INET,$network); - my $netmask_num = &Socket::inet_pton(AF_INET,$netmask); - - # Find start address - my $network_start = $network_num & $netmask_num; - - # Find end address - my $network_end = $network_start ^ ~$netmask_num; - - return (($addr_num ge $network_start) && ($addr_num le $network_end)); + return &Network::ip_address_in_network($addr, "$network/$netmask"); } # @@ -795,32 +725,25 @@ sub IpInSubnet # Call: NextIP ('1.1.1.1'); # Return: '1.1.1.2' # -sub NextIP -{ - return &Socket::inet_ntoa( pack("N", 1 + unpack('N', &Socket::inet_aton(shift)) - ) - ); +sub NextIP { + return &Network::find_next_ip_address(shift, 1); } -sub NextIP2 -{ - return &Socket::inet_ntoa( pack("N", 4 + unpack('N', &Socket::inet_aton(shift)) - ) - ); + +sub NextIP2 { + return &Network::find_next_ip_address(shift, 4); } -sub ipcidr -{ + +sub ipcidr { my ($ip,$cidr) = &Net::IPv4Addr::ipv4_parse(shift); return "$ip\/$cidr"; } -sub ipcidr2msk -{ +sub ipcidr2msk { my ($ip,$cidr) = &Net::IPv4Addr::ipv4_parse(shift); my $netmask = &Net::IPv4Addr::ipv4_cidr2msk($cidr); return "$ip\/$netmask"; } - sub validemail { my $mail = shift; return 0 if ( $mail !~ /^[0-9a-zA-Z\.\-\_]+\@[0-9a-zA-Z\.\-]+$/ ); diff --git a/config/cfgroot/network-functions.pl b/config/cfgroot/network-functions.pl new file mode 100644 index 000000000..029ffc09e --- /dev/null +++ b/config/cfgroot/network-functions.pl @@ -0,0 +1,306 @@ +#!/usr/bin/perl -w +############################################################################ +# # +# This file is part of the IPFire Firewall. # +# # +# IPFire 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. # +# # +# IPFire 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 IPFire; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +# Copyright (C) 2014 IPFire Team . # +# # +############################################################################ + +package Network; + +use Socket; + +my %PREFIX2NETMASK = ( + 32 => "255.255.255.255", + 31 => "255.255.255.254", + 30 => "255.255.255.252", + 29 => "255.255.255.248", + 28 => "255.255.255.240", + 27 => "255.255.255.224", + 26 => "255.255.255.192", + 25 => "255.255.255.128", + 24 => "255.255.255.0", + 23 => "255.255.254.0", + 22 => "255.255.252.0", + 21 => "255.255.248.0", + 20 => "255.255.240.0", + 19 => "255.255.224.0", + 18 => "255.255.192.0", + 17 => "255.255.128.0", + 16 => "255.255.0.0", + 15 => "255.254.0.0", + 14 => "255.252.0.0", + 13 => "255.248.0.0", + 12 => "255.240.0.0", + 11 => "255.224.0.0", + 10 => "255.192.0.0", + 9 => "255.128.0.0", + 8 => "255.0.0.0", + 7 => "254.0.0.0", + 6 => "252.0.0.0", + 5 => "248.0.0.0", + 4 => "240.0.0.0", + 3 => "224.0.0.0", + 2 => "192.0.0.0", + 1 => "128.0.0.0", + 0 => "0.0.0.0" +); + +my %NETMASK2PREFIX = reverse(%PREFIX2NETMASK); + +# Takes an IP address in dotted decimal notation and +# returns a 32 bit integer representing that IP addresss. +# Will return undef for invalid inputs. +sub ip2bin($) { + my $address = shift; + + # This function returns undef for undefined input. + if (!defined $address) { + return undef; + } + + my $address_bin = &Socket::inet_pton(AF_INET, $address); + if ($address_bin) { + $address_bin = unpack('N', $address_bin); + } + + return $address_bin; +} + +# Does the reverse of ip2bin(). +# Will return undef for invalid inputs. +sub bin2ip($) { + my $address_bin = shift; + + # This function returns undef for undefined input. + if (!defined $address_bin) { + return undef; + } + + my $address = pack('N', $address_bin); + if ($address) { + $address = &Socket::inet_ntop(AF_INET, $address); + } + + return $address; +} + +# Takes a network in either a.b.c.d/a.b.c.d or a.b.c.d/e notation +# and will return an 32 bit integer representing the start +# address and an other one representing the network mask. +sub network2bin($) { + my $network = shift; + + my ($address, $netmask) = split(/\//, $network, 2); + + if (&check_prefix($netmask)) { + $netmask = &convert_prefix2netmask($netmask); + } + + my $address_bin = &ip2bin($address); + my $netmask_bin = &ip2bin($netmask); + + my $network_start = $address_bin & $netmask_bin; + + return ($network_start, $netmask_bin); +} + +# Returns True for all valid IP addresses +sub check_ip_address($) { + my $address = shift; + + # Normalise the IP address and compare the result with + # the input - which should obviously the same. + my $normalised_address = &_normalise_ip_address($address); + + return ((defined $normalised_address) && ($address eq $normalised_address)); +} + +# Returns True for all valid prefixes. +sub check_prefix($) { + my $prefix = shift; + + return (exists $PREFIX2NETMASK{$prefix}); +} + +# Returns True for all valid subnet masks. +sub check_netmask($) { + my $netmask = shift; + + return (exists $NETMASK2PREFIX{$netmask}); +} + +# Returns True for all valid inputs like a.b.c.d/a.b.c.d. +sub check_ip_address_and_netmask($$) { + my $network = shift; + + my ($address, $netmask) = split(/\//, $network, 2); + + # Check if the IP address is fine. + # + my $result = &check_ip_address($address); + unless ($result) { + return $result; + } + + return &check_netmask($netmask); +} + +# For internal use only. Will take an IP address and +# return it in a normalised style. Like 8.8.8.010 -> 8.8.8.8. +sub _normalise_ip_address($) { + my $address = shift; + + my $address_bin = &ip2bin($address); + if (!defined $address_bin) { + return undef; + } + + return &bin2ip($address_bin); +} + +# Returns the prefix for the given subnet mask. +sub convert_netmask2prefix($) { + my $netmask = shift; + + if (exists $NETMASK2PREFIX{$netmask}) { + return $NETMASK2PREFIX{$netmask}; + } + + return undef; +} + +# Returns the subnet mask for the given prefix. +sub convert_prefix2netmask($) { + my $prefix = shift; + + if (exists $PREFIX2NETMASK{$prefix}) { + return $PREFIX2NETMASK{$prefix}; + } + + return undef; +} + +# Takes an IP address and an offset and +# will return the offset'th IP address. +sub find_next_ip_address($$) { + my $address = shift; + my $offset = shift; + + my $address_bin = &ip2bin($address); + $address_bin += $offset; + + return &bin2ip($address_bin); +} + +# Returns the network address of the given network. +sub get_netaddress($) { + my $network = shift; + my ($network_bin, $netmask_bin) = &network2bin($network); + + if (defined $network_bin) { + return &bin2ip($network_bin); + } + + return undef; +} + +# Returns the broadcast of the given network. +sub get_broadcast($) { + my $network = shift; + my ($network_bin, $netmask_bin) = &network2bin($network); + + return &bin2ip($network_bin ^ ~$netmask_bin); +} + +# Returns True if $address is in $network. +sub ip_address_in_network($$) { + my $address = shift; + my $network = shift; + + my $address_bin = &ip2bin($address); + return undef unless (defined $address_bin); + + my ($network_bin, $netmask_bin) = &network2bin($network); + + # Find end address + my $broadcast_bin = $network_bin ^ ~$netmask_bin; + + return (($address_bin ge $network_bin) && ($address_bin le $broadcast_bin)); +} + +1; + +# Remove the next line to enable the testsuite +__END__ + +sub assert($) { + my $ret = shift; + + if ($ret) { + return; + } + + print "ASSERTION ERROR"; + exit(1); +} + +sub testsuite() { + my $result; + + my $address1 = &ip2bin("8.8.8.8"); + assert($address1 == 134744072); + + my $address2 = &bin2ip($address1); + assert($address2 eq "8.8.8.8"); + + # Check if valid IP addresses are correctly recognised. + foreach my $address ("1.2.3.4", "192.168.180.1", "127.0.0.1") { + if (!&check_ip_address($address)) { + print "$address is not correctly recognised as a valid IP address!\n"; + exit 1; + }; + } + + # Check if invalid IP addresses are correctly found. + foreach my $address ("456.2.3.4", "192.768.180.1", "127.1", "1", "a.b.c.d", "1.2.3.4.5", "1.2.3.4/12") { + if (&check_ip_address($address)) { + print "$address is recognised as a valid IP address!\n"; + exit 1; + }; + } + + $result = &check_ip_address_and_netmask("192.168.180.0/255.255.255.0"); + assert($result); + + $result = &convert_netmask2prefix("255.255.254.0"); + assert($result == 23); + + $result = &convert_prefix2netmask(8); + assert($result eq "255.0.0.0"); + + $result = &find_next_ip_address("1.2.3.4", 2); + assert($result eq "1.2.3.6"); + + $result = &ip_address_in_network("10.0.1.4", "10.0.0.0/8"); + assert($result); + + return 0; +} + +&testsuite(); diff --git a/config/firewall/rules.pl b/config/firewall/rules.pl index c0ddcb2d6..aa8870cdc 100755 --- a/config/firewall/rules.pl +++ b/config/firewall/rules.pl @@ -291,6 +291,7 @@ sub buildrules { foreach my $src (@sources) { # Skip invalid source. + next unless (defined $src); next unless ($src); # Sanitize source. @@ -301,6 +302,7 @@ sub buildrules { foreach my $dst (@destinations) { # Skip invalid rules. + next unless (defined $dst); next if (!$dst || ($dst eq "none")); # Sanitize destination. diff --git a/config/rootfiles/common/configroot b/config/rootfiles/common/configroot index 6afe6cdf8..8e5aff828 100644 --- a/config/rootfiles/common/configroot +++ b/config/rootfiles/common/configroot @@ -117,6 +117,7 @@ var/ipfire/modem #var/ipfire/modem/defaults #var/ipfire/modem/settings var/ipfire/modem-lib.pl +var/ipfire/network-functions.pl var/ipfire/net-traffic #var/ipfire/net-traffic/net-traffic-admin.pl #var/ipfire/net-traffic/net-traffic-lib.pl diff --git a/lfs/configroot b/lfs/configroot index fcb08b0d4..e0bb346d0 100644 --- a/lfs/configroot +++ b/lfs/configroot @@ -77,6 +77,7 @@ $(TARGET) : # Copy initial configfiles cp $(DIR_SRC)/config/cfgroot/header.pl $(CONFIG_ROOT)/ cp $(DIR_SRC)/config/cfgroot/general-functions.pl $(CONFIG_ROOT)/ + cp $(DIR_SRC)/config/cfgroot/network-functions.pl $(CONFIG_ROOT)/ cp $(DIR_SRC)/config/cfgroot/lang.pl $(CONFIG_ROOT)/ cp $(DIR_SRC)/config/cfgroot/countries.pl $(CONFIG_ROOT)/ cp $(DIR_SRC)/config/cfgroot/graphs.pl $(CONFIG_ROOT)/