This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "IPFire 2.x development tree".
The branch, next has been updated via 824dc93601e03f14d6acaffe11fb578fe2667394 (commit) via 9f80e810729347d23386ae9837927bb2c8b9f1fe (commit) via d98aa95a55254a1c220390cf7ae389e68ea1f2c0 (commit) via 7bb66417fa6908750cf083e90805d7e498ff161e (commit) via 1c3044d72c4097227a63754c7673c485320230d2 (commit) via 292cad90f72fa27cbd8417c89a75097d8cec6e3b (commit) via 0e53d8a991af6cfb1b2982c20a663a5aa0fedc84 (commit) via 6e87f0aa53e12c4849ae36ec6c11f718b9d297f0 (commit) via 8f4f4634df1057e99b35a127ccb22a343d1f2274 (commit) via b05ec50ac97ef1d29190aab983284d2e1444ddda (commit) via 6178953be5c93975c069314cfe3b20cd2d684746 (commit) via a2b3eba9f5a16e8a7042f6e2029c377db5da7e9a (commit) via 1f9e7b53b74a139cf10a95c40995f887cfb24407 (commit) via 8531b94ae07f0f9d6897d2a03f5cf713131b2b13 (commit) via 68d1eb101713a381788a2f566156131c91095275 (commit) via 97ab0569bd6d7a421771175fa75bf064826cc3c1 (commit) via b57edbd8ecc9ee86f3a4f9df4608d962add492b2 (commit) from cbc492f516c0dc8077b2e41f336776aca552e256 (commit)
Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below.
- Log ----------------------------------------------------------------- commit 824dc93601e03f14d6acaffe11fb578fe2667394 Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 22:50:29 2014 +0100
firewall: Add a trailing space to all log prefixes for better readability.
commit 9f80e810729347d23386ae9837927bb2c8b9f1fe Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 22:46:17 2014 +0100
firewall: rules.pl: Remove unused variable $time_constraints.
commit d98aa95a55254a1c220390cf7ae389e68ea1f2c0 Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 22:44:26 2014 +0100
firewall: rules.pl: Replace some hardcoded chain names.
commit 7bb66417fa6908750cf083e90805d7e498ff161e Merge: 1c3044d cbc492f Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 22:38:09 2014 +0100
Merge branch 'next' of ssh://git.ipfire.org/pub/git/ipfire-2.x into next
commit 1c3044d72c4097227a63754c7673c485320230d2 Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 22:35:27 2014 +0100
firewall: Resurrect port forwardings with different external ports.
commit 292cad90f72fa27cbd8417c89a75097d8cec6e3b Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 20:48:58 2014 +0100
firewall: Telnet uses TCP
commit 0e53d8a991af6cfb1b2982c20a663a5aa0fedc84 Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 20:40:00 2014 +0100
firewall: Make OpenVPN access also possible when INPUT policy is REJECT.
commit 6e87f0aa53e12c4849ae36ec6c11f718b9d297f0 Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 20:37:44 2014 +0100
firewall: Allow accessing port forwardings from internal networks.
commit 8f4f4634df1057e99b35a127ccb22a343d1f2274 Author: Michael Tremer michael.tremer@ipfire.org Date: Sun Mar 2 18:23:28 2014 +0100
firewall: rules.pl: Refactored entire script.
commit b05ec50ac97ef1d29190aab983284d2e1444ddda Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 20:20:56 2014 +0100
firewall: rules.pl: Cleanup time constraints generation.
commit 6178953be5c93975c069314cfe3b20cd2d684746 Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 19:54:14 2014 +0100
firewall: rules.pl: Cleanup rule generation.
Various perl coding errors that have been suppressed by "no warnings uninitialized" have been fixed and lots of helper variables have been introduced to make it much more clearer what the code is actually doing.
commit a2b3eba9f5a16e8a7042f6e2029c377db5da7e9a Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 18:23:52 2014 +0100
general-functions.pl: Fix wrong perl syntax.
commit 1f9e7b53b74a139cf10a95c40995f887cfb24407 Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 18:18:40 2014 +0100
firewall: rules.pl: Remove $command and introduce $IPTABLES.
commit 8531b94ae07f0f9d6897d2a03f5cf713131b2b13 Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 18:07:39 2014 +0100
firewall: rules.pl: Remove command line args parsing and rest from old debugging mode.
commit 68d1eb101713a381788a2f566156131c91095275 Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 18:03:58 2014 +0100
firewall: rules.pl: Introduce a more slink debugging mode.
commit 97ab0569bd6d7a421771175fa75bf064826cc3c1 Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 17:54:22 2014 +0100
firewall: rules.pl: Fix some coding style.
commit b57edbd8ecc9ee86f3a4f9df4608d962add492b2 Author: Michael Tremer michael.tremer@ipfire.org Date: Sat Mar 1 17:49:22 2014 +0100
firewall: rules.pl: Remove totally bloated debug mode.
-----------------------------------------------------------------------
Summary of changes: config/cfgroot/general-functions.pl | 2 +- config/firewall/firewall-policy | 33 +- config/firewall/rules.pl | 1186 +++++++++++++++++++---------------- config/fwhosts/customservices | 2 +- src/initscripts/init.d/firewall | 35 +- 5 files changed, 685 insertions(+), 573 deletions(-)
Difference in files: diff --git a/config/cfgroot/general-functions.pl b/config/cfgroot/general-functions.pl index 9a5e671..386b047 100644 --- a/config/cfgroot/general-functions.pl +++ b/config/cfgroot/general-functions.pl @@ -1039,7 +1039,7 @@ sub GetIcmpDescription ($) { 'SKIP', 'Photur', #40 'Experimental'); - if ($index>41) {return 'unknown'} else {return @icmp_description[$index]}; + if ($index>41) {return 'unknown'} else {return $icmp_description[$index]}; }
sub GetCoreUpdateVersion() { diff --git a/config/firewall/firewall-policy b/config/firewall/firewall-policy index faf177c..b820a7f 100755 --- a/config/firewall/firewall-policy +++ b/config/firewall/firewall-policy @@ -55,26 +55,27 @@ esac HAVE_OPENVPN="true"
# INPUT + +# OpenVPN INPUT +# Allow direct access to the internal IP addresses of the firewall +# from remote subnets if forward policy is allowed. +case "${HAVE_OPENVPN},${POLICY}" in + true,MODE1) ;; + true,*) + iptables -A POLICYIN -i tun+ -j ACCEPT + ;; +esac + case "${FWPOLICY2}" in REJECT) if [ "${DROPINPUT}" = "on" ]; then - iptables -A POLICYIN -m limit --limit 10/minute -j LOG --log-prefix "REJECT_INPUT" + iptables -A POLICYIN -m limit --limit 10/minute -j LOG --log-prefix "REJECT_INPUT " fi iptables -A POLICYIN -j REJECT --reject-with icmp-host-unreachable -m comment --comment "DROP_INPUT" ;; *) # DROP - # OpenVPN - # Allow direct access to the internal IP addresses of the firewall - # from remote subnets if forward policy is allowed. - case "${HAVE_OPENVPN},${POLICY}" in - true,MODE1) ;; - true,*) - iptables -A POLICYIN -i tun+ -j ACCEPT - ;; - esac - if [ "${DROPINPUT}" = "on" ]; then - iptables -A POLICYIN -m limit --limit 10/minute -j LOG --log-prefix "DROP_INPUT" + iptables -A POLICYIN -m limit --limit 10/minute -j LOG --log-prefix "DROP_INPUT " fi iptables -A POLICYIN -j DROP -m comment --comment "DROP_INPUT" ;; @@ -86,13 +87,13 @@ case "${POLICY}" in case "${FWPOLICY}" in REJECT) if [ "${DROPFORWARD}" = "on" ]; then - iptables -A POLICYFWD -m limit --limit 10/minute -j LOG --log-prefix "REJECT_FORWARD" + iptables -A POLICYFWD -m limit --limit 10/minute -j LOG --log-prefix "REJECT_FORWARD " fi iptables -A POLICYFWD -j REJECT --reject-with icmp-host-unreachable -m comment --comment "DROP_FORWARD" ;; *) # DROP if [ "${DROPFORWARD}" = "on" ]; then - iptables -A POLICYFWD -m limit --limit 10/minute -j LOG --log-prefix "DROP_FORWARD" + iptables -A POLICYFWD -m limit --limit 10/minute -j LOG --log-prefix "DROP_FORWARD " fi iptables -A POLICYFWD -j DROP -m comment --comment "DROP_FORWARD" ;; @@ -119,13 +120,13 @@ case "${POLICY1}" in case "${FWPOLICY1}" in REJECT) if [ "${DROPOUTGOING}" = "on" ]; then - iptables -A POLICYOUT -m limit --limit 10/minute -j LOG --log-prefix "REJECT_OUTPUT" + iptables -A POLICYOUT -m limit --limit 10/minute -j LOG --log-prefix "REJECT_OUTPUT " fi iptables -A POLICYOUT -j REJECT --reject-with icmp-host-unreachable -m comment --comment "DROP_OUTPUT" ;; *) # DROP if [ "${DROPOUTGOING}" == "on" ]; then - iptables -A POLICYOUT -m limit --limit 10/minute -j LOG --log-prefix "DROP_OUTPUT" + iptables -A POLICYOUT -m limit --limit 10/minute -j LOG --log-prefix "DROP_OUTPUT " fi iptables -A POLICYOUT -j DROP -m comment --comment "DROP_OUTPUT" ;; diff --git a/config/firewall/rules.pl b/config/firewall/rules.pl index 845da1f..182c948 100755 --- a/config/firewall/rules.pl +++ b/config/firewall/rules.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/perl -w ############################################################################### # # # IPFire.org - A linux based firewall # @@ -20,32 +20,40 @@ ###############################################################################
use strict; -use Time::Local; -no warnings 'uninitialized';
-# enable only the following on debugging purpose -#use warnings; -#use CGI::Carp 'fatalsToBrowser'; +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/lang.pl"; +require "/usr/lib/firewall/firewall-lib.pl"; + +# Set to one to enable debugging mode. +my $DEBUG = 0; + +my $IPTABLES = "iptables --wait"; + +# iptables chains +my $CHAIN_INPUT = "INPUTFW"; +my $CHAIN_FORWARD = "FORWARDFW"; +my $CHAIN_OUTPUT = "OUTGOINGFW"; +my $CHAIN = $CHAIN_FORWARD; +my $CHAIN_NAT_SOURCE = "NAT_SOURCE"; +my $CHAIN_NAT_DESTINATION = "NAT_DESTINATION"; +my $CHAIN_MANGLE_NAT_DESTINATION_FIX = "NAT_DESTINATION"; +my @VALID_CHAINS = ($CHAIN_INPUT, $CHAIN_FORWARD, $CHAIN_OUTPUT); + +my @PROTOCOLS = ("tcp", "udp", "icmp", "igmp", "ah", "esp", "gre", "ipv6", "ipip"); +my @PROTOCOLS_WITH_PORTS = ("tcp", "udp"); + +my @VALID_TARGETS = ("ACCEPT", "DROP", "REJECT");
my %fwdfwsettings=(); my %defaultNetworks=(); -my %configfwdfw=(); -my %color=(); -my %icmptypes=(); -my %ovpnSettings=(); +my %configfwdfw=();; my %customgrp=(); -our %sourcehash=(); -our %targethash=(); -my @timeframe=(); my %configinputfw=(); my %configoutgoingfw=(); my %confignatfw=(); my %aliases=(); -my @DPROT=(); my @p2ps=(); -require '/var/ipfire/general-functions.pl'; -require "${General::swroot}/lang.pl"; -require "/usr/lib/firewall/firewall-lib.pl";
my $configfwdfw = "${General::swroot}/firewall/config"; my $configinput = "${General::swroot}/firewall/input"; @@ -53,16 +61,6 @@ my $configoutgoing = "${General::swroot}/firewall/outgoing"; my $p2pfile = "${General::swroot}/firewall/p2protocols"; my $configgrp = "${General::swroot}/fwhosts/customgroups"; my $netsettings = "${General::swroot}/ethernet/settings"; -my $errormessage = ''; -my $orange = ''; -my $green = ''; -my $blue = ''; -my ($TYPE,$PROT,$SPROT,$DPROT,$SPORT,$DPORT,$TIME,$TIMEFROM,$TIMETILL,$SRC_TGT); -my $CHAIN = "FORWARDFW"; -my $conexists = 'off'; -my $command = 'iptables --wait -A'; -my $dnat =''; -my $snat ='';
&General::readhash("${General::swroot}/firewall/settings", %fwdfwsettings); &General::readhash("$netsettings", %defaultNetworks); @@ -72,59 +70,66 @@ my $snat =''; &General::readhasharray($configgrp, %customgrp); &General::get_aliases(%aliases);
-#check if we have an internetconnection -open (CONN,"/var/ipfire/red/iface"); -my $con = <CONN>; -close(CONN); -if (-f "/var/ipfire/red/active"){ - $conexists='on'; +# MAIN +&main(); + +sub main { + # Flush all chains. + &flush(); + + # Reload firewall rules. + &preparerules(); + + # Load P2P block rules. + &p2pblock(); + + # Reload firewall policy. + run("/usr/sbin/firewall-policy"); } -open (CONN1,"/var/ipfire/red/local-ipaddress"); -my $redip = <CONN1>; -close(CONN1); -################# -# DEBUG/TEST # -################# -my $MODE=0; # 0 - normal operation - # 1 - print configline and rules to console - # -################# -my $param=shift; - -if($param eq 'flush'){ - if ($MODE eq '1'){ - print " Flushing chains...\n"; - } - &flush; -}else{ - if ($MODE eq '1'){ - print " Flushing chains...\n"; - } - &flush; - if ($MODE eq '1'){ - print " Preparing rules...\n"; - } - &preparerules; - if($MODE eq '0'){ - if ($fwdfwsettings{'POLICY'} eq 'MODE1'){ - &p2pblock; - system ("/usr/sbin/firewall-policy"); - }elsif($fwdfwsettings{'POLICY'} eq 'MODE2'){ - &p2pblock; - system ("/usr/sbin/firewall-policy"); + +sub run { + # Executes or prints the given shell command. + my $command = shift; + + if ($DEBUG) { + print "$command\n"; + } else { + system "$command"; + + if ($?) { + print_error("ERROR: $command"); } } } -sub flush -{ - system ("iptables --wait -F FORWARDFW"); - system ("iptables --wait -F INPUTFW"); - system ("iptables --wait -F OUTGOINGFW"); - system ("iptables --wait -t nat -F NAT_DESTINATION"); - system ("iptables --wait -t nat -F NAT_SOURCE"); + +sub print_error { + my $message = shift; + + print STDERR "$message\n"; +} + +sub print_rule { + my $hash = shift; + + print "\nRULE:"; + + my $i = 0; + foreach (@$hash) { + printf(" %2d: %s", $i++, $_); + } + print "\n"; +} + +sub flush { + run("$IPTABLES -F $CHAIN_INPUT"); + run("$IPTABLES -F $CHAIN_FORWARD"); + run("$IPTABLES -F $CHAIN_OUTPUT"); + run("$IPTABLES -t nat -F $CHAIN_NAT_SOURCE"); + run("$IPTABLES -t nat -F $CHAIN_NAT_DESTINATION"); + run("$IPTABLES -t mangle -F $CHAIN_MANGLE_NAT_DESTINATION_FIX"); } -sub preparerules -{ + +sub preparerules { if (! -z "${General::swroot}/firewall/config"){ &buildrules(%configfwdfw); } @@ -135,360 +140,286 @@ sub preparerules &buildrules(%configoutgoingfw); } } -sub buildrules -{ - my $hash=shift; - my $STAG; - my $natip; - my $snatport; - my $fireport; - my $nat; - my $fwaccessdport; - my $natchain; - my $icmptype; - foreach my $key (sort {$a <=> $b} keys %$hash){ - next if (($$hash{$key}[6] eq 'RED' || $$hash{$key}[6] eq 'RED1') && $conexists eq 'off' ); - $command="iptables --wait -A"; - if ($$hash{$key}[28] eq 'ON'){ - $command='iptables --wait -t nat -A'; - $natip=&get_nat_ip($$hash{$key}[29],$$hash{$key}[31]); - if($$hash{$key}[31] eq 'dnat'){ - $nat='DNAT'; - if ($$hash{$key}[30] =~ /|/){ - $$hash{$key}[30]=~ tr/|/,/; - $fireport='-m multiport --dport '.$$hash{$key}[30]; - }else{ - $fireport='--dport '.$$hash{$key}[30] if ($$hash{$key}[30]>0); - } - }else{ - $nat='SNAT'; - } + +sub buildrules { + my $hash = shift; + + foreach my $key (sort {$a <=> $b} keys %$hash) { + # Skip disabled rules. + next unless ($$hash{$key}[2] eq 'ON'); + + if ($DEBUG) { + print_rule($$hash{$key}); } - $STAG=''; - if($$hash{$key}[2] eq 'ON'){ - #get source ip's - if ($$hash{$key}[3] eq 'cust_grp_src'){ - foreach my $grp (sort {$a <=> $b} keys %customgrp){ - if($customgrp{$grp}[0] eq $$hash{$key}[4]){ - &get_address($customgrp{$grp}[3],$customgrp{$grp}[2],"src"); - } - } - }else{ - &get_address($$hash{$key}[3],$$hash{$key}[4],"src"); + + # Check if the target is valid. + my $target = $$hash{$key}[0]; + if (!$target ~~ @VALID_TARGETS) { + print_error("Invalid target '$target' for rule $key"); + next; + } + + # Check if the chain is valid. + my $chain = $$hash{$key}[1]; + if (!$chain ~~ @VALID_CHAINS) { + print_error("Invalid chain '$chain' in rule $key"); + next; + } + + # Collect all sources. + my @sources = &get_addresses($hash, $key, "src"); + + # Collect all destinations. + my @destinations = &get_addresses($hash, $key, "tgt"); + + # Check if logging should be enabled. + my $LOG = ($$hash{$key}[17] eq 'ON'); + + # Check if NAT is enabled and initialize variables, that we use for that. + my $NAT = ($$hash{$key}[28] eq 'ON'); + my $NAT_MODE; + if ($NAT) { + $NAT_MODE = uc($$hash{$key}[31]); + } + + # Set up time constraints. + my @time_options = (); + if ($$hash{$key}[18] eq 'ON') { + push(@time_options, ("-m", "time")); + + # Select all days of the week this match is active. + my @weekdays = (); + if ($$hash{$key}[19] ne '') { + push (@weekdays, "Mon"); } - #get target ip's - if ($$hash{$key}[5] eq 'cust_grp_tgt'){ - foreach my $grp (sort {$a <=> $b} keys %customgrp){ - if($customgrp{$grp}[0] eq $$hash{$key}[6]){ - &get_address($customgrp{$grp}[3],$customgrp{$grp}[2],"tgt"); - } - } - }elsif($$hash{$key}[5] eq 'ipfire' ){ - if($$hash{$key}[6] eq 'GREEN'){ - $targethash{$key}[0]=$defaultNetworks{'GREEN_ADDRESS'}; - } - if($$hash{$key}[6] eq 'BLUE'){ - $targethash{$key}[0]=$defaultNetworks{'BLUE_ADDRESS'}; - } - if($$hash{$key}[6] eq 'ORANGE'){ - $targethash{$key}[0]=$defaultNetworks{'ORANGE_ADDRESS'}; - } - if($$hash{$key}[6] eq 'ALL'){ - $targethash{$key}[0]='0.0.0.0/0'; - } - if($$hash{$key}[6] eq 'RED' || $$hash{$key}[6] eq 'RED1'){ - open(FILE, "/var/ipfire/red/local-ipaddress")or die "Couldn't open local-ipaddress"; - $targethash{$key}[0]= <FILE>; - close(FILE); - }else{ - foreach my $alias (sort keys %aliases){ - if ($$hash{$key}[6] eq $alias){ - $targethash{$key}[0]=$aliases{$alias}{'IPT'}; - } - } - } - }else{ - &get_address($$hash{$key}[5],$$hash{$key}[6],"tgt"); + if ($$hash{$key}[20] ne '') { + push (@weekdays, "Tue"); } - ##get source prot and port - $SRC_TGT='SRC'; - $SPORT = &get_port($hash,$key); - $SRC_TGT=''; - - ##get target prot and port - $DPROT=&get_prot($hash,$key); - - if ($DPROT eq ''){$DPROT=' ';} - @DPROT=split(",",$DPROT); - - #get time if defined - if($$hash{$key}[18] eq 'ON'){ - my ($time1,$time2,$daylight); - my $daylight=$$hash{$key}[28]; - $time1=&get_time($$hash{$key}[26],$daylight); - $time2=&get_time($$hash{$key}[27],$daylight); - if($$hash{$key}[19] ne ''){push (@timeframe,"Mon");} - if($$hash{$key}[20] ne ''){push (@timeframe,"Tue");} - if($$hash{$key}[21] ne ''){push (@timeframe,"Wed");} - if($$hash{$key}[22] ne ''){push (@timeframe,"Thu");} - if($$hash{$key}[23] ne ''){push (@timeframe,"Fri");} - if($$hash{$key}[24] ne ''){push (@timeframe,"Sat");} - if($$hash{$key}[25] ne ''){push (@timeframe,"Sun");} - $TIME=join(",",@timeframe); - - $TIMEFROM="--timestart $time1 "; - $TIMETILL="--timestop $time2 "; - $TIME="-m time --weekdays $TIME $TIMEFROM $TIMETILL"; + if ($$hash{$key}[21] ne '') { + push (@weekdays, "Wed"); } - if ($MODE eq '1'){ - print "NR:$key "; - foreach my $i (0 .. $#{$$hash{$key}}){ - print "$i: $$hash{$key}[$i] "; - } - print "\n"; - print"##################################\n"; - #print rules to console - foreach my $DPROT (@DPROT){ - $DPORT = &get_port($hash,$key,$DPROT); - if ($DPROT ne 'TCP' && $DPROT ne 'UDP' && $DPROT ne 'ICMP' ){ - $DPORT=''; + if ($$hash{$key}[22] ne '') { + push (@weekdays, "Thu"); + } + if ($$hash{$key}[23] ne '') { + push (@weekdays, "Fri"); + } + if ($$hash{$key}[24] ne '') { + push (@weekdays, "Sat"); + } + if ($$hash{$key}[25] ne '') { + push (@weekdays, "Sun"); + } + if (@weekdays) { + push(@time_options, ("--weekdays", join(",", @weekdays))); + } + + # Convert start time. + my $time_start = &format_time($$hash{$key}[26]); + if ($time_start) { + push(@time_options, ("--timestart", $time_start)); + } + + # Convert end time. + my $time_stop = &format_time($$hash{$key}[27]); + if ($time_stop) { + push(@time_options, ("--timestop", $time_stop)); + } + } + + # Check which protocols are used in this rule and so that we can + # later group rules by protocols. + my @protocols = &get_protocols($hash, $key); + if (!@protocols) { + print_error("Invalid protocol configuration for rule $key"); + next; + } + + foreach my $protocol (@protocols) { + # Check if the given protocol is supported. + if (($protocol ne "all") && (!$protocol ~~ @PROTOCOLS)) { + print_error("Protocol $protocol is not supported (rule $key)"); + next; + } + + # Prepare protocol options (like ICMP types, ports, etc...). + my @protocol_options = &get_protocol_options($hash, $key, $protocol); + + # Check if this protocol knows ports. + my $protocol_has_ports = ($protocol ~~ @PROTOCOLS_WITH_PORTS); + + foreach my $source (@sources) { + foreach my $destination (@destinations) { + # Skip invalid rules. + next if (!$source || !$destination || ($destination eq "none")); + + # Array with iptables arguments. + my @options = (); + + # Append protocol. + if ($protocol ne "all") { + push(@options, ("-p", $protocol)); + push(@options, @protocol_options); } - $PROT=$DPROT; - $PROT="-p $PROT" if ($PROT ne '' && $PROT ne ' '); - foreach my $a (sort keys %sourcehash){ - foreach my $b (sort keys %targethash){ - if(! $sourcehash{$a}[0] || ! $targethash{$b}[0] || ($natip eq '-d ' && $$hash{$key}[28] eq 'ON') || (!$natip && $$hash{$key}[28] eq 'ON')){ - #Skip rules when no RED IP is set (DHCP,DSL) - next; - } - next if ($targethash{$b}[0] eq 'none'); - $STAG=''; - if ($sourcehash{$a}[0] ne $targethash{$b}[0] && $targethash{$b}[0] ne 'none' || $sourcehash{$a}[0] eq '0.0.0.0/0.0.0.0'){ - if($DPROT ne ''){ - if(substr($sourcehash{$a}[0], 3, 3) ne 'mac' && $sourcehash{$a}[0] ne ''){ $STAG="-s";} - #Process ICMP RULE - if(substr($DPORT, 2, 4) eq 'icmp'){ - my @icmprule= split(",",substr($DPORT, 12,)); - foreach (@icmprule){ - $icmptype="--icmp-type "; - if ($_ eq "BLANK") { - $icmptype=""; - $_=""; - } - if ($$hash{$key}[17] eq 'ON'){ - print "$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $icmptype $_ $TIME -j LOG\n"; - } - print "$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $icmptype $_ $TIME -j $$hash{$key}[0]\n"; - } - #PROCESS DNAT RULE (Portforward) - }elsif($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'dnat'){ - $natchain='NAT_DESTINATION'; - if ($$hash{$key}[17] eq 'ON'){ - print "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $TIME -j LOG --log-prefix 'DNAT' \n"; - } - my ($ip,$sub) =split("/",$targethash{$b}[0]); - #Process NAT with servicegroup used - if ($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'dnat' && $$hash{$key}[14] eq 'cust_srvgrp'){ - print "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $TIME -j $nat --to-destination $ip $DPORT\n"; - $fwaccessdport=$DPORT; - }else{ - print "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $TIME -j $nat --to-destination $ip$DPORT\n"; - $DPORT =~ s/-/:/g; - if ($DPORT){ - $fwaccessdport="--dport ".substr($DPORT,1,); - }elsif(! $DPORT && $$hash{$key}[30] ne ''){ - if ($$hash{$key}[30]=~m/|/i){ - $$hash{$key}[30] =~ s/|/,/g; - $fwaccessdport="-m multiport --dport $$hash{$key}[30]"; - }else{ - $fwaccessdport="--dport $$hash{$key}[30]"; - } - } - } - print "iptables --wait -A FORWARDFW $PROT $STAG $sourcehash{$a}[0] -d $ip $fwaccessdport $TIME -j $$hash{$key}[0]\n"; - next; - #PROCESS SNAT RULE - }elsif($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'snat'){ - $natchain='NAT_SOURCE'; - if ($$hash{$key}[17] eq 'ON' ){ - print "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j LOG --log-prefix 'SNAT' \n"; - } - print "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j $nat --to-source $natip\n"; - } - #PROCESS EVERY OTHER RULE (If NOT ICMP, else the rule would be applied double) - if ($PROT ne '-p ICMP'){ - if ($$hash{$key}[17] eq 'ON' && $$hash{$key}[28] ne 'ON'){ - print "$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j LOG\n"; - } - print "iptables --wait -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j $$hash{$key}[0]\n"; - } - #PROCESS Prot ICMP and type = All ICMP-Types - if ($PROT eq '-p ICMP' && $$hash{$key}[9] eq 'All ICMP-Types'){ - if ($$hash{$key}[17] eq 'ON' && $$hash{$key}[28] ne 'ON'){ - print "$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j LOG\n"; - } - print "iptables --wait -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j $$hash{$key}[0]\n"; - } + + # Prepare source options. + my @source_options = (); + if ($source =~ /mac/) { + push(@source_options, $source); + } else { + push(@source_options, ("-s", $source)); + } + + # Prepare destination options. + my @destination_options = ("-d", $destination); + + # Add time constraint options. + push(@options, @time_options); + + # Process NAT rules. + if ($NAT) { + my $nat_address = &get_nat_address($$hash{$key}[29]); + + # Skip NAT rules if the NAT address is unknown + # (i.e. no internet connection has been established, yet). + next unless ($nat_address); + + # Destination NAT + if ($NAT_MODE eq "DNAT") { + # Make port-forwardings useable from the internal networks. + &add_dnat_mangle_rules($nat_address, @options); + + my @nat_options = @options; + push(@nat_options, @source_options); + push(@nat_options, ("-d", $nat_address)); + + my ($dnat_address, $dnat_mask) = split("/", $destination); + @destination_options = ("-d", $dnat_address); + + if ($protocol_has_ports) { + my $dnat_port = &get_dnat_target_port($hash, $key); + + if ($dnat_port) { + $dnat_address .= ":$dnat_port"; } } - } - } - print"\n"; - } - }elsif($MODE eq '0'){ - foreach my $DPROT (@DPROT){ - $DPORT = &get_port($hash,$key,$DPROT); - $PROT=$DPROT; - $PROT="-p $PROT" if ($PROT ne '' && $PROT ne ' '); - if ($DPROT ne 'TCP' && $DPROT ne'UDP' && $DPROT ne 'ICMP' ){ - $DPORT=''; - } - foreach my $a (sort keys %sourcehash){ - foreach my $b (sort keys %targethash){ - if(! $sourcehash{$a}[0] || ! $targethash{$b}[0] || ($natip eq '-d ' && $$hash{$key}[28] eq 'ON') || (!$natip && $$hash{$key}[28] eq 'ON')){ - #Skip rules when no RED IP is set (DHCP,DSL) - next; + + if ($LOG) { + run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options -j LOG --log-prefix 'DNAT '"); } - next if ($targethash{$b}[0] eq 'none'); - $STAG=''; - if ($sourcehash{$a}[0] ne $targethash{$b}[0] && $targethash{$b}[0] ne 'none' || $sourcehash{$a}[0] eq '0.0.0.0/0.0.0.0'){ - if($DPROT ne ''){ - if(substr($sourcehash{$a}[0], 3, 3) ne 'mac' && $sourcehash{$a}[0] ne ''){ $STAG="-s";} - #Process ICMP RULE - if(substr($DPORT, 2, 4) eq 'icmp'){ - my @icmprule= split(",",substr($DPORT, 12,)); - foreach (@icmprule){ - $icmptype="--icmp-type "; - if ($_ eq "BLANK") { - $icmptype=""; - $_=""; - } - if ($$hash{$key}[17] eq 'ON'){ - system ("$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $icmptype $_ $TIME -j LOG"); - } - system ("$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $icmptype $_ $TIME -j $$hash{$key}[0]"); - } - #PROCESS DNAT RULE (Portforward) - }elsif($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'dnat'){ - $natchain='NAT_DESTINATION'; - if ($$hash{$key}[17] eq 'ON'){ - system "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $TIME -j LOG --log-prefix 'DNAT' \n"; - } - my ($ip,$sub) =split("/",$targethash{$b}[0]); - #Process NAT with servicegroup used - if ($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'dnat' && $$hash{$key}[14] eq 'cust_srvgrp'){ - system "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $TIME -j $nat --to-destination $ip $DPORT\n"; - $fwaccessdport=$DPORT; - }else{ - system "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $TIME -j $nat --to-destination $ip$DPORT\n"; - $DPORT =~ s/-/:/g; - if ($DPORT){ - $fwaccessdport="--dport ".substr($DPORT,1,); - }elsif(! $DPORT && $$hash{$key}[30] ne ''){ - if ($$hash{$key}[30]=~m/|/i){ - $$hash{$key}[30] =~ s/|/,/g; - $fwaccessdport="-m multiport --dport $$hash{$key}[30]"; - }else{ - $fwaccessdport="--dport $$hash{$key}[30]"; - } - } - } - system "iptables --wait -A FORWARDFW $PROT $STAG $sourcehash{$a}[0] -d $ip $fwaccessdport $TIME -j $$hash{$key}[0]\n"; - next; - #PROCESS SNAT RULE - }elsif($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'snat'){ - $natchain='NAT_SOURCE'; - if ($$hash{$key}[17] eq 'ON' ){ - system "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j LOG --log-prefix 'SNAT' \n"; - } - system "$command $natchain $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j $nat --to-source $natip\n"; - } - #PROCESS EVERY OTHER RULE (If NOT ICMP, else the rule would be applied double) - if ($PROT ne '-p ICMP'){ - if ($$hash{$key}[17] eq 'ON' && $$hash{$key}[28] ne 'ON'){ - system "$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j LOG\n"; - } - system "iptables --wait -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j $$hash{$key}[0]\n"; - } - #PROCESS Prot ICMP and type = All ICMP-Types - if ($PROT eq '-p ICMP' && $$hash{$key}[9] eq 'All ICMP-Types'){ - if ($$hash{$key}[17] eq 'ON' && $$hash{$key}[28] ne 'ON'){ - system "$command $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j LOG\n"; - } - system "iptables --wait -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $TIME -j $$hash{$key}[0]\n"; - } - } + run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options -j DNAT --to-destination $dnat_address"); + + # Source NAT + } elsif ($NAT_MODE eq "SNAT") { + my @nat_options = @options; + + push(@nat_options, @source_options); + push(@nat_options, @destination_options); + + if ($LOG) { + run("$IPTABLES -t nat -A $CHAIN_NAT_SOURCE @nat_options -j LOG --log-prefix 'SNAT '"); } + run("$IPTABLES -t nat -A $CHAIN_NAT_SOURCE @nat_options -j SNAT --to-source $nat_address"); } } + + push(@options, @source_options); + push(@options, @destination_options); + + # Insert firewall rule. + if ($LOG && !$NAT) { + run("$IPTABLES -A $chain @options -j LOG"); + } + run("$IPTABLES -A $chain @options -j $target"); } } } - %sourcehash=(); - %targethash=(); - undef $TIME; - undef $TIMEFROM; - undef $TIMETILL; - undef $fireport; } } -sub get_nat_ip -{ - my $val=shift; - my $type=shift; - my $result; - if($val eq 'RED' || $val eq 'GREEN' || $val eq 'ORANGE' || $val eq 'BLUE'){ - $result=$defaultNetworks{$val.'_ADDRESS'}; - }elsif($val eq 'ALL'){ - $result='-i '.$con; - }elsif($val eq 'Default IP' && $type eq 'dnat'){ - $result='-d '.$redip; - }elsif($val eq 'Default IP' && $type eq 'snat'){ - $result=$redip; - }else{ - foreach my $al (sort keys %aliases){ - if($val eq $al && $type eq 'dnat'){ - $result='-d '.$aliases{$al}{'IPT'}; - }elsif($val eq $al && $type eq 'snat'){ - $result=$aliases{$al}{'IPT'}; - } + +sub get_external_interface() { + open(IFACE, "/var/ipfire/red/iface") or return ""; + my $iface = <IFACE>; + close(IFACE); + + return $iface; +} + +sub get_external_address() { + open(ADDR, "/var/ipfire/red/local-ipaddress") or return ""; + my $address = <ADDR>; + close(ADDR); + + return $address; +} + +sub get_alias { + my $id = shift; + + foreach my $alias (sort keys %aliases) { + if ($id eq $alias) { + return $aliases{$alias}; } } - return $result; } -sub get_time -{ - my $val=shift; - my $val1=shift; - my $time; - my $minutes; - my $ruletime; - $minutes = &utcmin($val); - $ruletime = $minutes + &time_get_utc($val); - if ($ruletime < 0){$ruletime +=1440;} - if ($ruletime > 1440){$ruletime -=1440;} - $time=sprintf "%02d:%02d", $ruletime / 60, $ruletime % 60; - return $time; + +sub get_nat_address { + my $zone = shift; + + # Any static address of any zone. + if ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") { + return $defaultNetworks{$zone . "_ADDRESS"}; + + } elsif ($zone eq "Default IP") { + return &get_external_address(); + + } else { + return &get_alias($zone); + } + + print_error("Could not find NAT address"); +} + +# Formats the given timestamp into the iptables format which is "hh:mm" UTC. +sub format_time { + my $val = shift; + + # Convert the given time into minutes. + my $minutes = &time_convert_to_minutes($val); + + # Move the timestamp into UTC. + $minutes += &time_utc_offset(); + + # Make sure $minutes is between 00:00 and 23:59. + if ($minutes < 0) { + $minutes += 1440; + } + + if ($minutes > 1440) { + $minutes -= 1440; + } + + # Format as hh:mm. + return sprintf("%02d:%02d", $minutes / 60, $minutes % 60); } -sub time_get_utc -{ - # Calculates the UTCtime from a given time - my $val=shift; - my @localtime=localtime(time); - my @gmtime=gmtime(time); - my $diff = ($gmtime[2]*60+$gmtime[1]%60)-($localtime[2]*60+$localtime[1]%60); - return $diff; + +# Calculates the offsets in minutes from the local timezone to UTC. +sub time_utc_offset { + my @localtime = localtime(time); + my @gmtime = gmtime(time); + + return ($gmtime[2] * 60 + $gmtime[1] % 60) - ($localtime[2] * 60 + $localtime[1] % 60); } -sub utcmin -{ - my $ruletime=shift; - my ($hrs,$min) = split(":",$ruletime); - my $newtime = $hrs*60+$min; - return $newtime; + +# Takes a timestamp like "14:00" and converts it into minutes since midnight. +sub time_convert_to_minutes { + my ($hrs, $min) = split(":", shift); + + return ($hrs * 60) + $min; } -sub p2pblock -{ - my $P2PSTRING; + +sub p2pblock { + my $P2PSTRING = ""; my $DO; open( FILE, "< $p2pfile" ) or die "Unable to read $p2pfile"; @p2ps = <FILE>; @@ -508,167 +439,328 @@ sub p2pblock } } } - if ($MODE eq 1){ - if($P2PSTRING){ - print"/sbin/iptables --wait -A FORWARDFW $CMD $P2PSTRING -j $DO\n"; + + if($P2PSTRING) { + run("$IPTABLES -A FORWARDFW $CMD $P2PSTRING -j $DO"); + } +} + +sub get_addresses { + my $hash = shift; + my $key = shift; + my $type = shift; + + my @addresses = (); + my $addr_type; + my $value; + my $group_name; + + if ($type eq "src") { + $addr_type = $$hash{$key}[3]; + $value = $$hash{$key}[4]; + + } elsif ($type eq "tgt") { + $addr_type = $$hash{$key}[5]; + $value = $$hash{$key}[6]; + } + + if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) { + foreach my $grp (sort {$a <=> $b} keys %customgrp) { + if ($customgrp{$grp}[0] eq $value) { + my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type); + + if (@address) { + push(@addresses, @address); + } + } } - }else{ - if($P2PSTRING){ - system("/sbin/iptables --wait -A FORWARDFW $CMD $P2PSTRING -j $DO"); + } else { + my @address = &get_address($addr_type, $value, $type); + + if (@address) { + push(@addresses, @address); } } + + return @addresses; } -sub get_address -{ - my $base=shift; #source of checking ($configfwdfw{$key}[x] or groupkey - my $base2=shift; - my $type=shift; #src or tgt - my $hash; - if ($type eq 'src'){ - $hash=%sourcehash; - }else{ - $hash=%targethash; - } - my $key = &General::findhasharraykey($hash); - if($base eq 'src_addr' || $base eq 'tgt_addr' ){ - if (&General::validmac($base2)){ - $$hash{$key}[0] = "-m mac --mac-source $base2"; - }else{ - $$hash{$key}[0] = $base2; + +sub get_address { + my $key = shift; + my $value = shift; + my $type = shift; + + my @ret = (); + + # If the user manually typed an address, we just check if it is a MAC + # address. Otherwise, we assume that it is an IP address. + if ($key ~~ ["src_addr", "tgt_addr"]) { + if (&General::validmac($value)) { + push(@ret, "-m mac --mac-source $value"); + } else { + push(@ret, $value); } - }elsif($base eq 'std_net_src' || $base eq 'std_net_tgt' || $base eq 'Standard Network'){ - $$hash{$key}[0]=&fwlib::get_std_net_ip($base2,$con); - }elsif($base eq 'cust_net_src' || $base eq 'cust_net_tgt' || $base eq 'Custom Network'){ - $$hash{$key}[0]=&fwlib::get_net_ip($base2); - }elsif($base eq 'cust_host_src' || $base eq 'cust_host_tgt' || $base eq 'Custom Host'){ - $$hash{$key}[0]=&fwlib::get_host_ip($base2,$type); - }elsif($base eq 'ovpn_net_src' || $base eq 'ovpn_net_tgt' || $base eq 'OpenVPN static network'){ - $$hash{$key}[0]=&fwlib::get_ovpn_net_ip($base2,1); - }elsif($base eq 'ovpn_host_src' ||$base eq 'ovpn_host_tgt' || $base eq 'OpenVPN static host'){ - $$hash{$key}[0]=&fwlib::get_ovpn_host_ip($base2,33); - }elsif($base eq 'ovpn_n2n_src' ||$base eq 'ovpn_n2n_tgt' || $base eq 'OpenVPN N-2-N'){ - $$hash{$key}[0]=&fwlib::get_ovpn_n2n_ip($base2,11); - }elsif($base eq 'ipsec_net_src' || $base eq 'ipsec_net_tgt' || $base eq 'IpSec Network'){ - $$hash{$key}[0]=&fwlib::get_ipsec_net_ip($base2,11); - }elsif($base eq 'ipfire_src' ){ - if($base2 eq 'GREEN'){ - $$hash{$key}[0]=$defaultNetworks{'GREEN_ADDRESS'}; + + # If a default network interface (GREEN, BLUE, etc.) is selected, we + # try to get the corresponding address of the network. + } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) { + my $external_interface = &get_external_interface(); + + my $network_address = &fwlib::get_std_net_ip($value, $external_interface); + if ($network_address) { + push(@ret, $network_address); } - if($base2 eq 'BLUE'){ - $$hash{$key}[0]=$defaultNetworks{'BLUE_ADDRESS'}; + + # Custom networks. + } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) { + my $network_address = &fwlib::get_net_ip($value); + if ($network_address) { + push(@ret, $network_address); } - if($base2 eq 'ORANGE'){ - $$hash{$key}[0]=$defaultNetworks{'ORANGE_ADDRESS'}; + + # Custom hosts. + } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) { + my $host_address = &fwlib::get_host_ip($value, $type); + if ($host_address) { + push(@ret, $host_address); } - if($base2 eq 'ALL'){ - $$hash{$key}[0]='0.0.0.0/0'; + + # OpenVPN networks. + } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) { + my $network_address = &fwlib::get_ovpn_net_ip($value, 1); + if ($network_address) { + push(@ret, $network_address); } - if($base2 eq 'RED' || $base2 eq 'RED1'){ - open(FILE, "/var/ipfire/red/local-ipaddress"); - $$hash{$key}[0]= <FILE>; - close(FILE); - }else{ - foreach my $alias (sort keys %aliases){ - if ($base2 eq $alias){ - $$hash{$key}[0]=$aliases{$alias}{'IPT'}; - } + + # OpenVPN hosts. + } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) { + my $host_address = &fwlib::get_ovpn_host_ip($value, 33); + if ($host_address) { + push(@ret, $host_address); + } + + # OpenVPN N2N. + } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) { + my $network_address = &fwlib::get_ovpn_n2n_ip($value, 11); + if ($network_address) { + push(@ret, $network_address); + } + + # IPsec networks. + } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) { + my $network_address = &fwlib::get_ipsec_net_ip($value, 11); + if ($network_address) { + push(@ret, $network_address); + } + + # The firewall's own IP addresses. + } elsif ($key ~~ ["ipfire", "ipfire_src"]) { + # ALL + if ($value eq "ALL") { + push(@ret, "0/0"); + + # GREEN + } elsif ($value eq "GREEN") { + push(@ret, $defaultNetworks{"GREEN_ADDRESS"}); + + # BLUE + } elsif ($value eq "BLUE") { + push(@ret, $defaultNetworks{"BLUE_ADDRESS"}); + + # ORANGE + } elsif ($value eq "ORANGE") { + push(@ret, $defaultNetworks{"ORANGE_ADDRESS"}); + + # RED + } elsif ($value ~~ ["RED", "RED1"]) { + my $address = &get_external_address(); + if ($address) { + push(@ret, $address); + } + + # Aliases + } else { + my %alias = &get_alias($value); + if (%alias) { + push(@ret, $alias{"IPT"}); } } + + # If nothing was selected, we assume "any". + } else { + push(@ret, "0/0"); } + + return @ret; } -sub get_prot -{ - my $hash=shift; - my $key=shift; - #check AH,GRE,ESP or ICMP - if ($$hash{$key}[7] ne 'ON' && $$hash{$key}[11] ne 'ON'){ - return "$$hash{$key}[8]"; - } - if ($$hash{$key}[7] eq 'ON' || $$hash{$key}[11] eq 'ON'){ - #check if servicegroup or service - if($$hash{$key}[14] eq 'cust_srv'){ - return &fwlib::get_srv_prot($$hash{$key}[15]); - }elsif($$hash{$key}[14] eq 'cust_srvgrp'){ - return &fwlib::get_srvgrp_prot($$hash{$key}[15]); - }elsif (($$hash{$key}[10] ne '' || $$hash{$key}[15] ne '') && $$hash{$key}[8] eq ''){ #when ports are used and prot set to "all" - return "TCP,UDP"; - }elsif (($$hash{$key}[10] ne '' || $$hash{$key}[15] ne '') && ($$hash{$key}[8] eq 'TCP' || $$hash{$key}[8] eq 'UDP')){ #when ports are used and prot set to "tcp" or "udp" - return "$$hash{$key}[8]"; - }elsif (($$hash{$key}[10] eq '' && $$hash{$key}[15] eq '') && $$hash{$key}[8] ne 'ICMP'){ #when ports are NOT used and prot NOT set to "ICMP" - return "$$hash{$key}[8]"; - }else{ - return "$$hash{$key}[8]"; + +sub get_protocols { + my $hash = shift; + my $key = shift; + + my $uses_source_ports = ($$hash{$key}[7] eq "ON"); + my $uses_services = ($$hash{$key}[11] eq "ON"); + + my @protocols = (); + + # Rules which don't have source ports or services (like ICMP, ESP, ...). + if (!$uses_source_ports && !$uses_services) { + push(@protocols, $$hash{$key}[8]); + + # Rules which either use ports or services. + } elsif ($uses_source_ports || $uses_services) { + # Check if service group or service + if ($$hash{$key}[14] eq 'cust_srv') { + push(@protocols, &fwlib::get_srv_prot($$hash{$key}[15])); + + } elsif($$hash{$key}[14] eq 'cust_srvgrp'){ + my $protos = &fwlib::get_srvgrp_prot($$hash{$key}[15]); + push(@protocols, split(",", $protos)); + + } else { + # Fetch the protocol for this rule. + my $protocol = lc($$hash{$key}[8]); + + # Fetch source and destination ports for this rule. + my $source_ports = $$hash{$key}[10]; + my $destination_ports = $$hash{$key}[15]; + + # Check if ports are set for protocols which do not support ports. + if (!($protocol ~~ @PROTOCOLS_WITH_PORTS) && ($source_ports || $destination_ports)) { + print_error("$protocol does not support ports"); + return (); + } + + push(@protocols, $protocol); } } - #DNAT - if ($SRC_TGT eq '' && $$hash{$key}[31] eq 'dnat' && $$hash{$key}[11] eq '' && $$hash{$key}[12] ne ''){ - return "$$hash{$key}[8]"; + + # Remove all empty elements + @protocols = map { $_ ? $_ : () } @protocols; + + # If no protocol has been defined, we assume "all". + if (!@protocols) { + push(@protocols, "all"); } + + # Make all protocol names lowercase. + @protocols = map { lc } @protocols; + + return @protocols; } -sub get_port -{ - my $hash=shift; - my $key=shift; - my $prot=shift; - #Get manual defined Ports from SOURCE - if ($$hash{$key}[7] eq 'ON' && $SRC_TGT eq 'SRC'){ - if ($$hash{$key}[10] ne ''){ - $$hash{$key}[10] =~ s/|/,/g; - if(index($$hash{$key}[10],",") > 0){ - return "-m multiport --sport $$hash{$key}[10] "; - }else{ - if($$hash{$key}[28] ne 'ON' || ($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'snat') ||($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'dnat') ){ - return "--sport $$hash{$key}[10] "; - }else{ - return ":$$hash{$key}[10]"; - } - } - } - #Get manual ports from TARGET - }elsif($$hash{$key}[11] eq 'ON' && $SRC_TGT eq ''){ - if($$hash{$key}[14] eq 'TGT_PORT'){ - if ($$hash{$key}[15] ne ''){ - $$hash{$key}[15] =~ s/|/,/g; - if(index($$hash{$key}[15],",") > 0){ - return "-m multiport --dport $$hash{$key}[15] "; - }else{ - if($$hash{$key}[28] ne 'ON' || ($$hash{$key}[28] eq 'ON' && $$hash{$key}[31] eq 'snat') ){ - return "--dport $$hash{$key}[15] "; - }else{ - $$hash{$key}[15] =~ s/:/-/g; - return ":$$hash{$key}[15]"; - } - } - } - #Get ports defined in custom Service (firewall-groups) - }elsif($$hash{$key}[14] eq 'cust_srv'){ - if ($prot ne 'ICMP'){ - if($$hash{$key}[31] eq 'dnat' && $$hash{$key}[28] eq 'ON'){ - my $ports =&fwlib::get_srv_port($$hash{$key}[15],1,$prot); - $ports =~ s/:/-/g; - return ":".$ports - }else{ - return "--dport ".&fwlib::get_srv_port($$hash{$key}[15],1,$prot); - } - }elsif($prot eq 'ICMP' && $$hash{$key}[11] eq 'ON'){ #When PROT is ICMP and "use targetport is checked, this is an icmp-service - return "--icmp-type ".&fwlib::get_srv_port($$hash{$key}[15],3,$prot); - } - #Get ports from services which are used in custom servicegroups (firewall-groups) - }elsif($$hash{$key}[14] eq 'cust_srvgrp'){ - if ($prot ne 'ICMP'){ - return &fwlib::get_srvgrp_port($$hash{$key}[15],$prot); + +sub get_protocol_options { + my $hash = shift; + my $key = shift; + my $protocol = shift; + my @options = (); + + # Process source ports. + my $use_src_ports = ($$hash{$key}[7] eq "ON"); + my $src_ports = $$hash{$key}[10]; + + if ($use_src_ports && $src_ports) { + push(@options, &format_ports($src_ports, "src")); + } + + # Process destination ports. + my $use_dst_ports = ($$hash{$key}[11] eq "ON"); + my $use_dnat = (($$hash{$key}[28] eq "ON") && ($$hash{$key}[31] eq "dnat")); + + if ($use_dst_ports) { + my $dst_ports_mode = $$hash{$key}[14]; + my $dst_ports = $$hash{$key}[15]; + + if (($dst_ports_mode eq "TGT_PORT") && $dst_ports) { + if ($use_dnat && $$hash{$key}[30]) { + $dst_ports = $$hash{$key}[30]; } - elsif($prot eq 'ICMP'){ - return &fwlib::get_srvgrp_port($$hash{$key}[15],$prot); + push(@options, &format_ports($dst_ports, "dst")); + + } elsif ($dst_ports_mode eq "cust_srv") { + if ($protocol eq "ICMP") { + push(@options, ("--icmp-type", &fwlib::get_srv_port($dst_ports, 3, "ICMP"))); + } else { + $dst_ports = &fwlib::get_srv_port($dst_ports, 1, uc($protocol)); + push(@options, &format_ports($dst_ports, "dst")); } + + } elsif ($dst_ports_mode eq "cust_srvgrp") { + push(@options, &fwlib::get_srvgrp_port($dst_ports, uc($protocol))); } } - #CHECK ICMP - if ($$hash{$key}[7] ne 'ON' && $$hash{$key}[11] ne 'ON' && $SRC_TGT eq ''){ - if($$hash{$key}[9] ne '' && $$hash{$key}[9] ne 'All ICMP-Types'){ - return "--icmp-type $$hash{$key}[9] "; - }elsif($$hash{$key}[9] eq 'All ICMP-Types'){ - return; + + # Check if a single ICMP type is selected. + if (!$use_src_ports && !$use_dst_ports && $protocol eq "icmp") { + my $icmp_type = $$hash{$key}[9]; + + if (($icmp_type ne "All ICMP-Types") && $icmp_type) { + push(@options, ("--icmp-type", $icmp_type)); + } + } + + return @options; +} + +sub format_ports { + my $ports = shift; + my $type = shift; + + my $arg; + if ($type eq "src") { + $arg = "--sport"; + } elsif ($type eq "dst") { + $arg = "--dport"; + } + + my @options = (); + + if ($ports =~ /|/) { + $ports =~ s/|/,/g; + push(@options, ("-m", "multiport")); + } + + if ($ports) { + push(@options, ($arg, $ports)); + } + + return @options; +} + +sub get_dnat_target_port { + my $hash = shift; + my $key = shift; + + if ($$hash{$key}[14] eq "TGT_PORT") { + my $port = $$hash{$key}[15]; + my $external_port = $$hash{$key}[30]; + + if ($external_port && ($port ne $external_port)) { + return $$hash{$key}[15]; } } } + +sub add_dnat_mangle_rules { + my $nat_address = shift; + my @options = @_; + + my $mark = 0; + foreach my $zone ("GREEN", "BLUE", "ORANGE") { + $mark++; + + # Skip rule if not all required information exists. + next unless (exists $defaultNetworks{$zone . "_NETADDRESS"}); + next unless (exists $defaultNetworks{$zone . "_NETMASK"}); + + my @mangle_options = @options; + + my $netaddress = $defaultNetworks{$zone . "_NETADDRESS"}; + $netaddress .= "/" . $defaultNetworks{$zone . "_NETMASK"}; + + push(@mangle_options, ("-s", $netaddress, "-d", $nat_address)); + push(@mangle_options, ("-j", "MARK", "--set-mark", $mark)); + + run("$IPTABLES -t mangle -A $CHAIN_MANGLE_NAT_DESTINATION_FIX @mangle_options"); + } +} diff --git a/config/fwhosts/customservices b/config/fwhosts/customservices index 7f9ae3a..9b25a72 100644 --- a/config/fwhosts/customservices +++ b/config/fwhosts/customservices @@ -28,7 +28,7 @@ 12,NetBIOS Name Service,137,TCP,BLANK,0 15,IMAP,143,TCP,BLANK,0 8,HTTP,80,TCP,BLANK,0 -4,Telnet,23,UDP,BLANK,0 +4,Telnet,23,TCP,BLANK,0 34,DNS (TCP),53,TCP,,0 19,FTPS data,989,TCP,BLANK,0 5,SMTP,25,TCP,BLANK,0 diff --git a/src/initscripts/init.d/firewall b/src/initscripts/init.d/firewall index dd67889..e87952b 100644 --- a/src/initscripts/init.d/firewall +++ b/src/initscripts/init.d/firewall @@ -189,14 +189,6 @@ iptables_init() { iptables -t nat -N NAT_SOURCE iptables -t nat -A POSTROUTING -j NAT_SOURCE
- # RED chain, used for the red interface - iptables -N REDINPUT - iptables -A INPUT -j REDINPUT - iptables -N REDFORWARD - iptables -A FORWARD -j REDFORWARD - iptables -t nat -N REDNAT - iptables -t nat -A POSTROUTING -j REDNAT - # Custom prerouting chains (for transparent proxy) iptables -t nat -N SQUID iptables -t nat -A PREROUTING -j SQUID @@ -205,12 +197,39 @@ iptables_init() { iptables -t nat -N NAT_DESTINATION iptables -t nat -A PREROUTING -j NAT_DESTINATION
+ iptables -t mangle -N NAT_DESTINATION + iptables -t mangle -A PREROUTING -j NAT_DESTINATION + + iptables -t nat -N NAT_DESTINATION_FIX + iptables -t nat -A POSTROUTING -j NAT_DESTINATION_FIX + + iptables -t nat -A NAT_DESTINATION_FIX \ + -m mark --mark 1 -j SNAT --to-source "${GREEN_ADDRESS}" + + if [ -n "${BLUE_ADDRESS}" ]; then + iptables -t nat -A NAT_DESTINATION_FIX \ + -m mark --mark 2 -j SNAT --to-source "${BLUE_ADDRESS}" + fi + + if [ -n "${ORANGE_ADDRESS}" ]; then + iptables -t nat -A NAT_DESTINATION_FIX \ + -m mark --mark 3 -j SNAT --to-source "${ORANGE_ADDRESS}" + fi + # upnp chain for our upnp daemon iptables -t nat -N UPNPFW iptables -t nat -A PREROUTING -j UPNPFW iptables -N UPNPFW iptables -A FORWARD -m conntrack --ctstate NEW -j UPNPFW
+ # RED chain, used for the red interface + iptables -N REDINPUT + iptables -A INPUT -j REDINPUT + iptables -N REDFORWARD + iptables -A FORWARD -j REDFORWARD + iptables -t nat -N REDNAT + iptables -t nat -A POSTROUTING -j REDNAT + # Apply OpenVPN firewall rules /usr/local/bin/openvpnctrl --firewall-rules
hooks/post-receive -- IPFire 2.x development tree