From: Michael Tremer Date: Sun, 2 Mar 2014 17:23:28 +0000 (+0100) Subject: firewall: rules.pl: Refactored entire script. X-Git-Url: http://git.ipfire.org/?p=people%2Fteissler%2Fipfire-2.x.git;a=commitdiff_plain;h=8f4f4634df1057e99b35a127ccb22a343d1f2274 firewall: rules.pl: Refactored entire script. --- diff --git a/config/firewall/rules.pl b/config/firewall/rules.pl index b5555d168..d2991b897 100755 --- a/config/firewall/rules.pl +++ b/config/firewall/rules.pl @@ -31,25 +31,27 @@ my $DEBUG = 0; my $IPTABLES = "iptables --wait"; # iptables chains -my $CHAIN = "FORWARDFW"; +my $CHAIN_INPUT = "INPUTFW"; +my $CHAIN_FORWARD = "FORWARDFW"; +my $CHAIN_OUTPUT = "OUTPUTFW"; +my $CHAIN = $CHAIN_FORWARD; my $CHAIN_NAT_SOURCE = "NAT_SOURCE"; my $CHAIN_NAT_DESTINATION = "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=(); my $configfwdfw = "${General::swroot}/firewall/config"; @@ -58,14 +60,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,$SRC_TGT); -my $conexists = 'off'; -my $dnat =''; -my $snat =''; &General::readhash("${General::swroot}/firewall/settings", \%fwdfwsettings); &General::readhash("$netsettings", \%defaultNetworks); @@ -75,19 +69,6 @@ my $snat =''; &General::readhasharray($configgrp, \%customgrp); &General::get_aliases(\%aliases); -#check if we have an internetconnection -open (CONN,"/var/ipfire/red/iface"); -my $con = ; -close(CONN); - -if (-f "/var/ipfire/red/active"){ - $conexists='on'; -} - -open (CONN1,"/var/ipfire/red/local-ipaddress"); -my $redip = ; -close(CONN1); - # MAIN &main(); @@ -122,6 +103,18 @@ sub print_error { 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 FORWARDFW"); run("$IPTABLES -F INPUTFW"); @@ -143,272 +136,249 @@ sub preparerules { } sub buildrules { - my $hash=shift; - my $STAG; - my $snatport; - my $fireport; - 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' ); + 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}); + } + + # 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"); my $time_constraints = ""; - my $natip = ""; # Check if logging should be enabled. - my $LOG = 0; - if ($$hash{$key}[17] eq 'ON') { - $LOG = 1; - } + my $LOG = ($$hash{$key}[17] eq 'ON'); - my $NAT = 0; + # 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]); + } - # Check if NAT is enabled and initialize variables, that we use for that. - if ($$hash{$key}[28] eq 'ON') { - $NAT = 1; - - # Destination NAT - if ($$hash{$key}[31] eq 'dnat') { - $NAT_MODE = "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); - } + # Set up time constraints. + my @time_options = (); + if ($$hash{$key}[18] eq 'ON') { + push(@time_options, ("-m", "time")); - # Source NAT - } elsif ($$hash{$key}[31] eq 'snat') { - $NAT_MODE = "SNAT"; + # Select all days of the week this match is active. + my @weekdays = (); + if ($$hash{$key}[19] ne '') { + push (@weekdays, "Mon"); + } + if ($$hash{$key}[20] ne '') { + push (@weekdays, "Tue"); + } + if ($$hash{$key}[21] ne '') { + push (@weekdays, "Wed"); + } + 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))); + } - } else { - print_error("Invalid NAT mode: $$hash{$key}[31]"); - next; + # Convert start time. + my $time_start = &format_time($$hash{$key}[26]); + if ($time_start) { + push(@time_options, ("--timestart", $time_start)); } - $natip = &get_nat_ip($$hash{$key}[29], $NAT_MODE); + # Convert end time. + my $time_stop = &format_time($$hash{$key}[27]); + if ($time_stop) { + push(@time_options, ("--timestop", $time_stop)); + } } - $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 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; } - #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"); + + # 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); } - } - }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]= ; - close(FILE); - }else{ - foreach my $alias (sort keys %aliases){ - if ($$hash{$key}[6] eq $alias){ - $targethash{$key}[0]=$aliases{$alias}{'IPT'}; - } + + # Append source. + if ($source =~ /mac/) { + push(@options, $source); + } else { + push(@options, ("-s", $source)); } - } - }else{ - &get_address($$hash{$key}[5],$$hash{$key}[6],"tgt"); - } - ##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); + # Append destination for non-DNAT rules. + unless ($NAT && ($NAT_MODE eq "DNAT")) { + push(@options, ("-d", $destination)); + } - if ($DPROT eq ''){$DPROT=' ';} - @DPROT=split(",",$DPROT); + # Add time constraint options. + push(@options, @time_options); - # Set up time constraints. - if ($$hash{$key}[18] eq 'ON') { - my @time_args = ("-m", "time"); + # Process NAT rules. + if ($NAT) { + my $nat_address = &get_nat_address($$hash{$key}[29]); - # Select all days of the week this match is active. - my @weekdays = (); - if ($$hash{$key}[19] ne '') { - push (@weekdays, "Mon"); - } - if ($$hash{$key}[20] ne '') { - push (@weekdays, "Tue"); - } - if ($$hash{$key}[21] ne '') { - push (@weekdays, "Wed"); - } - 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_args, ("--weekdays", join(",", @weekdays))); - } + # Skip NAT rules if the NAT address is unknown + # (i.e. no internet connection has been established, yet). + next unless ($nat_address); - # Convert start time. - my $time_start = &format_time($$hash{$key}[26]); - if ($time_start) { - push(@time_args, ("--timestart", $time_start)); - } + # Destination NAT + if ($NAT_MODE eq "DNAT") { + my ($dnat_address, $dnat_mask) = split("/", $destination); - # Convert end time. - my $time_stop = &format_time($$hash{$key}[27]); - if ($time_stop) { - push(@time_args, ("--timestop", $time_stop)); - } + my @nat_options = @options; + push(@nat_options, ("-d", $nat_address)); + push(@options, ("-d", $dnat_address)); - # Format command line. - $time_constraints = join(" ", @time_args); - } + if ($protocol_has_ports) { + my $dnat_port = &get_dnat_target_port($hash, $key); - 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 ' && $NAT) || (!$natip && $NAT)){ - #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 ($LOG) { - run("$IPTABLES -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $icmptype $_ $time_constraints -j LOG"); - } - run("$IPTABLES -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $icmptype $_ $time_constraints -j $$hash{$key}[0]"); - } - #PROCESS DNAT RULE (Portforward) - } elsif ($NAT && $NAT_MODE eq "DNAT") { - if ($LOG) { - run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $time_constraints -j LOG --log-prefix 'DNAT'"); - } - my ($ip,$sub) =split("/",$targethash{$b}[0]); - #Process NAT with servicegroup used - if ($$hash{$key}[14] eq 'cust_srvgrp') { - run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $time_constraints -j DNAT --to-destination $ip $DPORT"); - $fwaccessdport=$DPORT; - } else { - run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION $PROT $STAG $sourcehash{$a}[0] $SPORT $natip $fireport $time_constraints -j DNAT --to-destination $ip$DPORT"); - $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]"; - } + if ($dnat_port) { + $dnat_address .= ":$dnat_port"; + + # Replace --dport with the translated one. + my @new_options = (); + my $skip_count = 0; + foreach my $option (@options) { + next if ($skip_count-- > 0); + + if ($option eq "--dport") { + push(@new_options, ("--dport", $dnat_port)); + $skip_count = 1; + next; } + + push(@new_options, $option); } - run("$IPTABLES -A FORWARDFW $PROT $STAG $sourcehash{$a}[0] -d $ip $fwaccessdport $time_constraints -j $$hash{$key}[0]"); - next; - #PROCESS SNAT RULE - } elsif ($NAT && $NAT_MODE eq "SNAT") { - if ($LOG) { - run("$IPTABLES -t nat -A $CHAIN_NAT_SOURCE $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $time_constraints -j LOG --log-prefix 'SNAT'"); - } - run("$IPTABLES -t nat -A $CHAIN_NAT_SOURCE $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $time_constraints -j SNAT --to-source $natip"); - } - #PROCESS EVERY OTHER RULE (If NOT ICMP, else the rule would be applied double) - if ($PROT ne '-p ICMP'){ - if ($LOG && !$NAT) { - run("$IPTABLES -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $time_constraints -j LOG"); - } - run("$IPTABLES -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $time_constraints -j $$hash{$key}[0]"); - } - #PROCESS Prot ICMP and type = All ICMP-Types - if ($PROT eq '-p ICMP' && $$hash{$key}[9] eq 'All ICMP-Types'){ - if ($LOG && !$NAT) { - run("$IPTABLES -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $time_constraints -j LOG"); - } - run("$IPTABLES -A $$hash{$key}[1] $PROT $STAG $sourcehash{$a}[0] $SPORT -d $targethash{$b}[0] $DPORT $time_constraints -j $$hash{$key}[0]"); + @options = @new_options; } } + + if ($LOG) { + run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options -j LOG --log-prefix 'DNAT'"); + } + run("$IPTABLES -t nat -A $CHAIN_NAT_DESTINATION @nat_options -j DNAT --to-destination $dnat_address"); + + # Source NAT + } elsif ($NAT_MODE eq "SNAT") { + if ($LOG) { + run("$IPTABLES -t nat -A $CHAIN_NAT_SOURCE @options -j LOG --log-prefix 'SNAT'"); + } + run("$IPTABLES -t nat -A $CHAIN_NAT_SOURCE @options -j SNAT --to-source $nat_address"); } } + + # Insert firewall rule. + if ($LOG && !$NAT) { + run("$IPTABLES -A $chain @options -j LOG"); + } + run("$IPTABLES -A $chain @options -j $target"); } } } - %sourcehash=(); - %targethash=(); - 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 = ; + close(IFACE); + + return $iface; +} + +sub get_external_address() { + open(ADDR, "/var/ipfire/red/local-ipaddress") or return ""; + my $address = ; + 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_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. @@ -476,156 +446,291 @@ sub p2pblock { } } -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; +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 { + my @address = &get_address($addr_type, $value, $type); + + if (@address) { + push(@addresses, @address); + } } - 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; + + return @addresses; +} + +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); + } + + # 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); } - }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'}; + + # 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 'BLUE'){ - $$hash{$key}[0]=$defaultNetworks{'BLUE_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 'ORANGE'){ - $$hash{$key}[0]=$defaultNetworks{'ORANGE_ADDRESS'}; + + # 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 'ALL'){ - $$hash{$key}[0]='0.0.0.0/0'; + + # 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); } - if($base2 eq 'RED' || $base2 eq 'RED1'){ - open(FILE, "/var/ipfire/red/local-ipaddress"); - $$hash{$key}[0]= ; - close(FILE); - }else{ - foreach my $alias (sort keys %aliases){ - if ($base2 eq $alias){ - $$hash{$key}[0]=$aliases{$alias}{'IPT'}; - } + + # 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]"; - } - } +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 ($use_dnat && $$hash{$key}[30]) { + $dst_ports = $$hash{$key}[30]; } - #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); - } - elsif($prot eq 'ICMP'){ - return &fwlib::get_srvgrp_port($$hash{$key}[15],$prot); + + if (($dst_ports_mode eq "TGT_PORT") && $dst_ports) { + 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")); + } + + push(@options, ($arg, $ports)); + + return @options; +} + +sub get_dnat_target_port { + my $hash = shift; + my $key = shift; + + if ($$hash{$key}[14] eq "TGT_PORT") { + return $$hash{$key}[15]; + } }