2 ###############################################################################
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2013 Alexander Marx <amarx@ipfire.org> #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
23 no warnings
'uninitialized';
30 my %customgeoipgrp=();
32 my %customservicegrp=();
41 require '/var/ipfire/general-functions.pl';
43 my $confignet = "${General::swroot}/fwhosts/customnetworks";
44 my $confighost = "${General::swroot}/fwhosts/customhosts";
45 my $configgrp = "${General::swroot}/fwhosts/customgroups";
46 my $configgeoipgrp = "${General::swroot}/fwhosts/customgeoipgrp";
47 my $configsrv = "${General::swroot}/fwhosts/customservices";
48 my $configsrvgrp = "${General::swroot}/fwhosts/customservicegrp";
49 my $configccdnet = "${General::swroot}/ovpn/ccd.conf";
50 my $configccdhost = "${General::swroot}/ovpn/ovpnconfig";
51 my $configipsec = "${General::swroot}/vpn/config";
52 my $configovpn = "${General::swroot}/ovpn/settings";
55 my $netsettings = "${General::swroot}/ethernet/settings";
57 &General
::readhash
("/var/ipfire/ethernet/settings", \
%netsettings);
58 &General
::readhash
("${General::swroot}/ovpn/settings", \
%ovpnsettings);
59 &General
::readhash
("${General::swroot}/vpn/settings", \
%ipsecsettings);
61 &General
::readhasharray
("$confignet", \
%customnetwork);
62 &General
::readhasharray
("$confighost", \
%customhost);
63 &General
::readhasharray
("$configgrp", \
%customgrp);
64 &General
::readhasharray
("$configgeoipgrp", \
%customgeoipgrp);
65 &General
::readhasharray
("$configccdnet", \
%ccdnet);
66 &General
::readhasharray
("$configccdhost", \
%ccdhost);
67 &General
::readhasharray
("$configipsec", \
%ipsecconf);
68 &General
::readhasharray
("$configsrv", \
%customservice);
69 &General
::readhasharray
("$configsrvgrp", \
%customservicegrp);
70 &General
::get_aliases
(\
%aliases);
75 foreach my $key (sort {$a <=> $b} keys %customservice){
76 if($customservice{$key}[0] eq $val){
77 if ($customservice{$key}[0] eq $val){
78 return $customservice{$key}[2];
90 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
91 if($customservicegrp{$key}[0] eq $val){
92 if (&get_srv_prot
($customservicegrp{$key}[2]) eq 'TCP'){
94 }elsif(&get_srv_prot
($customservicegrp{$key}[2]) eq 'UDP'){
96 }elsif(&get_srv_prot
($customservicegrp{$key}[2]) eq 'ICMP'){
99 #Protocols used in servicegroups
100 push (@ips,$customservicegrp{$key}[2]);
104 if ($tcp eq '1'){push (@ips,'TCP');}
105 if ($udp eq '1'){push (@ips,'UDP');}
106 if ($icmp eq '1'){push (@ips,'ICMP');}
107 my $back=join(",",@ips);
116 foreach my $key (sort {$a <=> $b} keys %customservice){
117 if($customservice{$key}[0] eq $val && $customservice{$key}[2] eq $prot){
118 return $customservice{$key}[$field];
129 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
130 if($customservicegrp{$key}[0] eq $val){
131 if ($prot ne 'ICMP'){
132 $value=&get_srv_port
($customservicegrp{$key}[2],1,$prot);
133 }elsif ($prot eq 'ICMP'){
134 $value=&get_srv_port
($customservicegrp{$key}[2],3,$prot);
136 push (@ips,$value) if ($value ne '') ;
140 if ($#ips gt 0){$back="-m multiport --dports ";}else{$back="--dport ";}
141 }elsif ($prot eq 'ICMP'){
142 $back="--icmp-type ";
145 $back.=join(",",@ips);
152 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
153 #adapt $val to reflect real name without subnet (if rule with only one ipsec subnet is created)
154 my @tmpval = split (/\|/, $val);
156 if($ipsecconf{$key}[1] eq $val){
157 return $ipsecconf{$key}[$field];
161 sub get_ipsec_host_ip
165 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
166 if($ipsecconf{$key}[1] eq $val){
167 return $ipsecconf{$key}[$field];
175 foreach my $key (sort {$a <=> $b} keys %ccdhost){
176 if($ccdhost{$key}[1] eq $val){
177 return $ccdhost{$key}[$field];
185 foreach my $key (sort {$a <=> $b} keys %ccdhost){
186 if($ccdhost{$key}[1] eq $val){
187 return $ccdhost{$key}[$field];
196 foreach my $key (sort {$a <=> $b} keys %ccdnet){
197 if($ccdnet{$key}[0] eq $val){
198 return $ccdnet{$key}[$field];
206 foreach my $key (sort {$a <=> $b} keys %customgrp){
207 if ($customgrp{$key}[0] eq $val){
208 &get_address
($customgrp{$key}[3],$src);
218 return "0.0.0.0/0.0.0.0";
219 }elsif($val eq 'GREEN'){
220 return "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
221 }elsif($val eq 'ORANGE'){
222 return "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
223 }elsif($val eq 'BLUE'){
224 return "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
225 }elsif($val eq 'RED'){
227 }elsif($val =~ /OpenVPN/i){
228 return "$ovpnsettings{'DOVPN_SUBNET'}";
229 }elsif($val =~ /IPsec/i){
230 return "$ipsecsettings{'RW_NET'}";
231 }elsif($val eq 'IPFire'){
238 if($net eq "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"){
239 return "$netsettings{'GREEN_DEV'}";
241 if($net eq "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"){
242 return "$netsettings{'ORANGE_DEV'}";
244 if($net eq "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"){
245 return "$netsettings{'BLUE_DEV'}";
247 if($net eq "0.0.0.0/0") {
248 return &get_external_interface
();
255 foreach my $key (sort {$a <=> $b} keys %customnetwork){
256 if($customnetwork{$key}[0] eq $val){
257 return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
265 foreach my $key (sort {$a <=> $b} keys %customhost){
266 if($customhost{$key}[0] eq $val){
267 if ($customhost{$key}[1] eq 'mac' && $src eq 'src'){
268 return "-m mac --mac-source $customhost{$key}[2]";
269 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'src'){
270 return "$customhost{$key}[2]";
271 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'tgt'){
272 return "$customhost{$key}[2]";
273 }elsif($customhost{$key}[1] eq 'mac' && $src eq 'tgt'){
290 if ($type eq "src") {
291 $addr_type = $$hash{$key}[3];
292 $value = $$hash{$key}[4];
294 } elsif ($type eq "tgt") {
295 $addr_type = $$hash{$key}[5];
296 $value = $$hash{$key}[6];
299 if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) {
300 foreach my $grp (sort {$a <=> $b} keys %customgrp) {
301 if ($customgrp{$grp}[0] eq $value) {
302 my @address = &get_address
($customgrp{$grp}[3], $customgrp{$grp}[2], $type);
305 push(@addresses, @address);
309 }elsif ($addr_type ~~ ["cust_geoip_src", "cust_geoip_tgt"] && $value =~ "group:") {
310 $value=substr($value,6);
311 foreach my $grp (sort {$a <=> $b} keys %customgeoipgrp) {
312 if ($customgeoipgrp{$grp}[0] eq $value) {
313 my @address = &get_address
($addr_type, $customgeoipgrp{$grp}[2], $type);
316 push(@addresses, @address);
321 my @address = &get_address
($addr_type, $value, $type);
324 push(@addresses, @address);
338 # If the user manually typed an address, we just check if it is a MAC
339 # address. Otherwise, we assume that it is an IP address.
340 if ($key ~~ ["src_addr", "tgt_addr"]) {
341 if (&General
::validmac
($value)) {
342 push(@ret, ["-m mac --mac-source $value", ""]);
344 push(@ret, [$value, ""]);
347 # If a default network interface (GREEN, BLUE, etc.) is selected, we
348 # try to get the corresponding address of the network.
349 } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) {
350 my $external_interface = &get_external_interface
();
352 my $network_address = &get_std_net_ip
($value, $external_interface);
354 if ($network_address) {
355 my $interface = &get_interface
($network_address);
356 push(@ret, [$network_address, $interface]);
360 } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
361 my $network_address = &get_net_ip
($value);
362 if ($network_address) {
363 push(@ret, [$network_address, ""]);
367 } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
368 my $host_address = &get_host_ip
($value, $type);
370 push(@ret, [$host_address, ""]);
374 } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) {
375 my $network_address = &get_ovpn_net_ip
($value, 1);
376 if ($network_address) {
377 push(@ret, [$network_address, ""]);
381 } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
382 my $host_address = &get_ovpn_host_ip
($value, 33);
384 push(@ret, [$host_address, ""]);
388 } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) {
389 my $network_address = &get_ovpn_n2n_ip
($value, 11);
390 if ($network_address) {
391 push(@ret, [$network_address, ""]);
395 } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
396 #Check if we have multiple subnets and only want one of them
397 if ( $value =~ /\|/ ){
398 my @parts = split(/\|/, $value);
399 push(@ret, [$parts[1], ""]);
401 my $network_address = &get_ipsec_net_ip
($value, 11);
402 my @nets = split(/\|/, $network_address);
403 foreach my $net (@nets) {
404 push(@ret, [$net, ""]);
408 # The firewall's own IP addresses.
409 } elsif ($key ~~ ["ipfire", "ipfire_src"]) {
411 if ($value eq "ALL") {
412 push(@ret, ["0/0", ""]);
415 } elsif ($value eq "GREEN") {
416 push(@ret, [$netsettings{"GREEN_ADDRESS"}, ""]);
419 } elsif ($value eq "BLUE") {
420 push(@ret, [$netsettings{"BLUE_ADDRESS"}, ""]);
423 } elsif ($value eq "ORANGE") {
424 push(@ret, [$netsettings{"ORANGE_ADDRESS"}, ""]);
427 } elsif ($value ~~ ["RED", "RED1"]) {
428 my $address = &get_external_address
();
430 push(@ret, [$address, ""]);
435 my $alias = &get_alias
($value);
437 push(@ret, [$alias, ""]);
441 # Handle rule options with GeoIP as source.
442 } elsif ($key eq "cust_geoip_src") {
443 # Get external interface.
444 my $external_interface = &get_external_interface
();
446 push(@ret, ["-m geoip --src-cc $value", "$external_interface"]);
448 # Handle rule options with GeoIP as target.
449 } elsif ($key eq "cust_geoip_tgt") {
450 # Get external interface.
451 my $external_interface = &get_external_interface
();
453 push(@ret, ["-m geoip --dst-cc $value", "$external_interface"]);
455 # If nothing was selected, we assume "any".
457 push(@ret, ["0/0", ""]);
462 sub get_external_interface
()
464 open(IFACE
, "/var/ipfire/red/iface") or return "";
470 sub get_external_address
()
472 open(ADDR
, "/var/ipfire/red/local-ipaddress") or return "";
473 my $address = <ADDR
>;
482 foreach my $alias (sort keys %aliases) {
484 return $aliases{$alias}{"IPT"};
489 sub get_nat_address
{
493 # Any static address of any zone.
494 if ($zone eq "AUTO") {
495 if ($source && ($source !~ m/mac/i )) {
496 my $firewall_ip = &get_internal_firewall_ip_address
($source, 1);
501 $firewall_ip = &get_matching_firewall_address
($source, 1);
507 return &get_external_address
();
509 } elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
510 return $netsettings{$zone . "_ADDRESS"};
512 } elsif ($zone ~~ ["Default IP", "ALL"]) {
513 return &get_external_address
();
516 my $alias = &get_alias
($zone);
518 $alias = &get_external_address
();
523 print_error
("Could not find NAT address");
526 sub get_internal_firewall_ip_addresses
528 my $use_orange = shift;
530 my @zones = ("GREEN", "BLUE");
532 push(@zones, "ORANGE");
536 for my $zone (@zones) {
537 next unless (exists $netsettings{$zone . "_ADDRESS"});
539 my $zone_address = $netsettings{$zone . "_ADDRESS"};
540 push(@addresses, $zone_address);
545 sub get_matching_firewall_address
548 my $use_orange = shift;
550 my ($address, $netmask) = split("/", $addr);
552 my @zones = ("GREEN", "BLUE");
554 push(@zones, "ORANGE");
557 foreach my $zone (@zones) {
558 next unless (exists $netsettings{$zone . "_ADDRESS"});
560 my $zone_subnet = $netsettings{$zone . "_NETADDRESS"};
561 my $zone_mask = $netsettings{$zone . "_NETMASK"};
563 if (&General
::IpInSubnet
($address, $zone_subnet, $zone_mask)) {
564 return $netsettings{$zone . "_ADDRESS"};
570 sub get_internal_firewall_ip_address
573 my $use_orange = shift;
575 my ($net_address, $net_mask) = split("/", $subnet);
576 if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
580 # Convert net mask into correct format for &General::IpInSubnet().
581 $net_mask = &General
::iporsubtodec
($net_mask);
583 my @addresses = &get_internal_firewall_ip_addresses
($use_orange);
584 foreach my $zone_address (@addresses) {
585 if (&General
::IpInSubnet
($zone_address, $net_address, $net_mask)) {
586 return $zone_address;
593 sub get_geoip_locations
() {
594 # Path to the directory which contains the binary geoip
596 my $directory="/usr/share/xt_geoip/LE";
598 # Array to store the final country list.
599 my @country_codes = ();
601 # Open location and do a directory listing.
602 opendir(DIR
, "$directory");
603 my @locations = readdir(DIR
);
606 # Loop through the directory listing, and cut of the file extensions.
607 foreach my $location (sort @locations) {
609 next if($location =~ /^\.$/);
610 next if($location =~ /^\.\.$/);
612 # Remove whitespaces.
615 # Cut-off file extension.
616 my ($country_code, $extension) = split(/\./, $location);
618 # Add country code to array.
619 push(@country_codes, $country_code);
622 # Return final array.
623 return @country_codes;