Firewall: DNAT - Show right DNAT interface in ruletable
[people/teissler/ipfire-2.x.git] / config / firewall / firewall-lib.pl
CommitLineData
2a81ab0d
AM
1#!/usr/bin/perl
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
5bee9a9d 5# Copyright (C) 2013 Alexander Marx <amarx@ipfire.org> #
2a81ab0d
AM
6# #
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. #
11# #
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. #
16# #
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/>. #
19# #
20###############################################################################
2a81ab0d
AM
21
22use strict;
23no warnings 'uninitialized';
24
25package fwlib;
26
27my %customnetwork=();
28my %customhost=();
29my %customgrp=();
30my %customservice=();
31my %customservicegrp=();
32my %ccdnet=();
33my %ccdhost=();
34my %ipsecconf=();
35my %ipsecsettings=();
36my %netsettings=();
37my %ovpnsettings=();
4e54e3c6
AM
38my %defaultNetworks=();
39my %aliases=();
2a81ab0d
AM
40
41require '/var/ipfire/general-functions.pl';
42
43my $confignet = "${General::swroot}/fwhosts/customnetworks";
44my $confighost = "${General::swroot}/fwhosts/customhosts";
45my $configgrp = "${General::swroot}/fwhosts/customgroups";
46my $configsrv = "${General::swroot}/fwhosts/customservices";
47my $configsrvgrp = "${General::swroot}/fwhosts/customservicegrp";
48my $configccdnet = "${General::swroot}/ovpn/ccd.conf";
49my $configccdhost = "${General::swroot}/ovpn/ovpnconfig";
50my $configipsec = "${General::swroot}/vpn/config";
51my $configovpn = "${General::swroot}/ovpn/settings";
52my $val;
53my $field;
fd169d0a 54my $netsettings = "${General::swroot}/ethernet/settings";
2a81ab0d
AM
55
56&General::readhash("/var/ipfire/ethernet/settings", \%netsettings);
57&General::readhash("${General::swroot}/ovpn/settings", \%ovpnsettings);
58&General::readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
fd169d0a 59&General::readhash("$netsettings", \%defaultNetworks);
2a81ab0d
AM
60
61&General::readhasharray("$confignet", \%customnetwork);
62&General::readhasharray("$confighost", \%customhost);
63&General::readhasharray("$configgrp", \%customgrp);
64&General::readhasharray("$configccdnet", \%ccdnet);
65&General::readhasharray("$configccdhost", \%ccdhost);
66&General::readhasharray("$configipsec", \%ipsecconf);
67&General::readhasharray("$configsrv", \%customservice);
68&General::readhasharray("$configsrvgrp", \%customservicegrp);
69
70sub get_srv_prot
71{
72 my $val=shift;
992394d5 73 foreach my $key (sort {$a <=> $b} keys %customservice){
2a81ab0d
AM
74 if($customservice{$key}[0] eq $val){
75 if ($customservice{$key}[0] eq $val){
76 return $customservice{$key}[2];
77 }
78 }
79 }
80}
81sub get_srvgrp_prot
82{
83 my $val=shift;
84 my @ips=();
85 my $tcp;
86 my $udp;
87 my $icmp;
992394d5 88 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
2a81ab0d
AM
89 if($customservicegrp{$key}[0] eq $val){
90 if (&get_srv_prot($customservicegrp{$key}[2]) eq 'TCP'){
91 $tcp=1;
92 }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'UDP'){
93 $udp=1;
94 }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'ICMP'){
95 $icmp=1;
82b837cf
AM
96 }else{
97 #Protocols used in servicegroups
98 push (@ips,$customservicegrp{$key}[2]);
99 }
2a81ab0d
AM
100 }
101 }
102 if ($tcp eq '1'){push (@ips,'TCP');}
103 if ($udp eq '1'){push (@ips,'UDP');}
104 if ($icmp eq '1'){push (@ips,'ICMP');}
105 my $back=join(",",@ips);
106 return $back;
107
108}
2a81ab0d
AM
109sub get_srv_port
110{
111 my $val=shift;
112 my $field=shift;
113 my $prot=shift;
992394d5 114 foreach my $key (sort {$a <=> $b} keys %customservice){
14bcb9a2
AM
115 if($customservice{$key}[0] eq $val && $customservice{$key}[2] eq $prot){
116 return $customservice{$key}[$field];
2a81ab0d
AM
117 }
118 }
119}
120sub get_srvgrp_port
121{
122 my $val=shift;
123 my $prot=shift;
124 my $back;
125 my $value;
126 my @ips=();
992394d5 127 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
2a81ab0d
AM
128 if($customservicegrp{$key}[0] eq $val){
129 if ($prot ne 'ICMP'){
130 $value=&get_srv_port($customservicegrp{$key}[2],1,$prot);
131 }elsif ($prot eq 'ICMP'){
132 $value=&get_srv_port($customservicegrp{$key}[2],3,$prot);
133 }
134 push (@ips,$value) if ($value ne '') ;
135 }
136 }
137 if($prot ne 'ICMP'){
138 if ($#ips gt 0){$back="-m multiport --dports ";}else{$back="--dport ";}
139 }elsif ($prot eq 'ICMP'){
140 $back="--icmp-type ";
141 }
142
143 $back.=join(",",@ips);
144 return $back;
145}
146sub get_ipsec_net_ip
147{
148 my $val=shift;
149 my $field=shift;
992394d5 150 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
2a81ab0d
AM
151 if($ipsecconf{$key}[1] eq $val){
152 return $ipsecconf{$key}[$field];
153 }
154 }
155}
156sub get_ipsec_host_ip
157{
158 my $val=shift;
159 my $field=shift;
992394d5 160 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
2a81ab0d
AM
161 if($ipsecconf{$key}[1] eq $val){
162 return $ipsecconf{$key}[$field];
163 }
164 }
165}
166sub get_ovpn_n2n_ip
167{
168 my $val=shift;
169 my $field=shift;
992394d5 170 foreach my $key (sort {$a <=> $b} keys %ccdhost){
2a81ab0d
AM
171 if($ccdhost{$key}[1] eq $val){
172 return $ccdhost{$key}[$field];
173 }
174 }
175}
176sub get_ovpn_host_ip
177{
178 my $val=shift;
179 my $field=shift;
992394d5 180 foreach my $key (sort {$a <=> $b} keys %ccdhost){
2a81ab0d
AM
181 if($ccdhost{$key}[1] eq $val){
182 return $ccdhost{$key}[$field];
183 }
184 }
185}
186sub get_ovpn_net_ip
187{
188
189 my $val=shift;
190 my $field=shift;
992394d5 191 foreach my $key (sort {$a <=> $b} keys %ccdnet){
2a81ab0d
AM
192 if($ccdnet{$key}[0] eq $val){
193 return $ccdnet{$key}[$field];
194 }
195 }
196}
197sub get_grp_ip
198{
199 my $val=shift;
200 my $src=shift;
992394d5 201 foreach my $key (sort {$a <=> $b} keys %customgrp){
2a81ab0d
AM
202 if ($customgrp{$key}[0] eq $val){
203 &get_address($customgrp{$key}[3],$src);
204 }
205 }
206
207}
208sub get_std_net_ip
209{
210 my $val=shift;
ddcec9d3 211 my $con=shift;
2a81ab0d
AM
212 if ($val eq 'ALL'){
213 return "0.0.0.0/0.0.0.0";
214 }elsif($val eq 'GREEN'){
215 return "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
216 }elsif($val eq 'ORANGE'){
217 return "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
218 }elsif($val eq 'BLUE'){
219 return "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
62fc8511 220 }elsif($val eq 'RED'){
ddcec9d3 221 return "0.0.0.0/0 -o $con";
2a81ab0d
AM
222 }elsif($val =~ /OpenVPN/i){
223 return "$ovpnsettings{'DOVPN_SUBNET'}";
224 }elsif($val =~ /IPsec/i){
225 return "$ipsecsettings{'RW_NET'}";
5d7faa45
AM
226 }elsif($val eq 'IPFire'){
227 return ;
2a81ab0d
AM
228 }
229}
230sub get_net_ip
231{
232 my $val=shift;
992394d5 233 foreach my $key (sort {$a <=> $b} keys %customnetwork){
2a81ab0d
AM
234 if($customnetwork{$key}[0] eq $val){
235 return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
236 }
237 }
238}
239sub get_host_ip
240{
241 my $val=shift;
242 my $src=shift;
992394d5 243 foreach my $key (sort {$a <=> $b} keys %customhost){
2a81ab0d
AM
244 if($customhost{$key}[0] eq $val){
245 if ($customhost{$key}[1] eq 'mac' && $src eq 'src'){
246 return "-m mac --mac-source $customhost{$key}[2]";
247 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'src'){
248 return "$customhost{$key}[2]";
249 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'tgt'){
250 return "$customhost{$key}[2]";
251 }elsif($customhost{$key}[1] eq 'mac' && $src eq 'tgt'){
252 return "none";
253 }
254 }
255 }
256}
fd169d0a
AM
257sub get_addresses
258{
4e54e3c6
AM
259 my $hash = shift;
260 my $key = shift;
261 my $type = shift;
262
263 my @addresses = ();
264 my $addr_type;
265 my $value;
266 my $group_name;
267
268 if ($type eq "src") {
269 $addr_type = $$hash{$key}[3];
270 $value = $$hash{$key}[4];
271
272 } elsif ($type eq "tgt") {
273 $addr_type = $$hash{$key}[5];
274 $value = $$hash{$key}[6];
275 }
276
277 if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) {
278 foreach my $grp (sort {$a <=> $b} keys %customgrp) {
279 if ($customgrp{$grp}[0] eq $value) {
280 my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type);
281
282 if (@address) {
283 push(@addresses, @address);
284 }
285 }
286 }
287 } else {
288 my @address = &get_address($addr_type, $value, $type);
289
290 if (@address) {
291 push(@addresses, @address);
292 }
293 }
294
295 return @addresses;
296}
fd169d0a
AM
297sub get_address
298{
4e54e3c6
AM
299 my $key = shift;
300 my $value = shift;
301 my $type = shift;
302
303 my @ret = ();
304
305 # If the user manually typed an address, we just check if it is a MAC
306 # address. Otherwise, we assume that it is an IP address.
307 if ($key ~~ ["src_addr", "tgt_addr"]) {
308 if (&General::validmac($value)) {
309 push(@ret, "-m mac --mac-source $value");
310 } else {
311 push(@ret, $value);
312 }
313
314 # If a default network interface (GREEN, BLUE, etc.) is selected, we
315 # try to get the corresponding address of the network.
316 } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) {
317 my $external_interface = &get_external_interface();
318
319 my $network_address = &get_std_net_ip($value, $external_interface);
320 if ($network_address) {
321 push(@ret, $network_address);
322 }
323
324 # Custom networks.
325 } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
326 my $network_address = &get_net_ip($value);
327 if ($network_address) {
328 push(@ret, $network_address);
329 }
330
331 # Custom hosts.
332 } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
333 my $host_address = &get_host_ip($value, $type);
334 if ($host_address) {
335 push(@ret, $host_address);
336 }
337
338 # OpenVPN networks.
339 } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) {
340 my $network_address = &get_ovpn_net_ip($value, 1);
341 if ($network_address) {
342 push(@ret, $network_address);
343 }
344
345 # OpenVPN hosts.
346 } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
347 my $host_address = &get_ovpn_host_ip($value, 33);
348 if ($host_address) {
349 push(@ret, $host_address);
350 }
351
352 # OpenVPN N2N.
353 } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) {
354 my $network_address = &get_ovpn_n2n_ip($value, 11);
355 if ($network_address) {
356 push(@ret, $network_address);
357 }
358
359 # IPsec networks.
360 } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
361 my $network_address = &get_ipsec_net_ip($value, 11);
362 if ($network_address) {
363 push(@ret, $network_address);
364 }
365
366 # The firewall's own IP addresses.
367 } elsif ($key ~~ ["ipfire", "ipfire_src"]) {
368 # ALL
369 if ($value eq "ALL") {
370 push(@ret, "0/0");
371
372 # GREEN
373 } elsif ($value eq "GREEN") {
374 push(@ret, $defaultNetworks{"GREEN_ADDRESS"});
375
376 # BLUE
377 } elsif ($value eq "BLUE") {
378 push(@ret, $defaultNetworks{"BLUE_ADDRESS"});
379
380 # ORANGE
381 } elsif ($value eq "ORANGE") {
382 push(@ret, $defaultNetworks{"ORANGE_ADDRESS"});
383
384 # RED
385 } elsif ($value ~~ ["RED", "RED1"]) {
386 my $address = &get_external_address();
387 if ($address) {
388 push(@ret, $address);
389 }
390
391 # Aliases
392 } else {
393 my %alias = &get_alias($value);
394 if (%alias) {
395 push(@ret, $alias{"IPT"});
396 }
397 }
398
399 # If nothing was selected, we assume "any".
400 } else {
401 push(@ret, "0/0");
402 }
403
404 return @ret;
405}
fd169d0a
AM
406sub get_external_interface()
407{
4e54e3c6
AM
408 open(IFACE, "/var/ipfire/red/iface") or return "";
409 my $iface = <IFACE>;
410 close(IFACE);
411
412 return $iface;
413}
fd169d0a
AM
414sub get_external_address()
415{
4e54e3c6
AM
416 open(ADDR, "/var/ipfire/red/local-ipaddress") or return "";
417 my $address = <ADDR>;
418 close(ADDR);
419
420 return $address;
421}
fd169d0a
AM
422sub get_alias
423{
4e54e3c6
AM
424 my $id = shift;
425
426 foreach my $alias (sort keys %aliases) {
427 if ($id eq $alias) {
428 return $aliases{$alias};
429 }
430 }
431}
fd169d0a
AM
432sub get_nat_address
433{
4e54e3c6
AM
434 my $zone = shift;
435 my $source = shift;
436
437 # Any static address of any zone.
438 if ($zone eq "AUTO") {
fd169d0a 439 if ($source && ($source !~ m/mac/i )) {
4e54e3c6
AM
440 my $firewall_ip = &get_internal_firewall_ip_address($source, 1);
441 if ($firewall_ip) {
442 return $firewall_ip;
443 }
444
445 $firewall_ip = &get_matching_firewall_address($source, 1);
446 if ($firewall_ip) {
447 return $firewall_ip;
448 }
449 }
450
451 return &get_external_address();
452
453 } elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
454 return $defaultNetworks{$zone . "_ADDRESS"};
455
456 } elsif ($zone eq "Default IP") {
457 return &get_external_address();
458
459 } else {
460 return &get_alias($zone);
461 }
462
463 print_error("Could not find NAT address");
464}
fd169d0a
AM
465sub get_internal_firewall_ip_addresses
466{
4e54e3c6
AM
467 my $use_orange = shift;
468
469 my @zones = ("GREEN", "BLUE");
470 if ($use_orange) {
471 push(@zones, "ORANGE");
472 }
473
474 my @addresses = ();
475 for my $zone (@zones) {
476 next unless (exists $defaultNetworks{$zone . "_ADDRESS"});
477
478 my $zone_address = $defaultNetworks{$zone . "_ADDRESS"};
479 push(@addresses, $zone_address);
480 }
481
482 return @addresses;
483}
fd169d0a
AM
484sub get_matching_firewall_address
485{
4e54e3c6
AM
486 my $addr = shift;
487 my $use_orange = shift;
488
489 my ($address, $netmask) = split("/", $addr);
490
491 my @zones = ("GREEN", "BLUE");
492 if ($use_orange) {
493 push(@zones, "ORANGE");
494 }
495
496 foreach my $zone (@zones) {
497 next unless (exists $defaultNetworks{$zone . "_ADDRESS"});
498
499 my $zone_subnet = $defaultNetworks{$zone . "_NETADDRESS"};
500 my $zone_mask = $defaultNetworks{$zone . "_NETMASK"};
501
502 if (&General::IpInSubnet($address, $zone_subnet, $zone_mask)) {
503 return $defaultNetworks{$zone . "_ADDRESS"};
504 }
505 }
506
507 return 0;
508}
fd169d0a
AM
509sub get_internal_firewall_ip_address
510{
4e54e3c6
AM
511 my $subnet = shift;
512 my $use_orange = shift;
513
514 my ($net_address, $net_mask) = split("/", $subnet);
515 if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
516 return 0;
517 }
518
519 my @addresses = &get_internal_firewall_ip_addresses($use_orange);
520 foreach my $zone_address (@addresses) {
521 if (&General::IpInSubnet($zone_address, $net_address, $net_mask)) {
522 return $zone_address;
523 }
524 }
525
526 return 0;
527}
528
2a81ab0d 529return 1;