firewall: Write correct rules bound to interface for routes IPsec tunnels
[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=();
b9ca2fa6 30my %customgeoipgrp=();
2a81ab0d
AM
31my %customservice=();
32my %customservicegrp=();
33my %ccdnet=();
34my %ccdhost=();
35my %ipsecconf=();
36my %ipsecsettings=();
37my %netsettings=();
38my %ovpnsettings=();
4e54e3c6 39my %aliases=();
2a81ab0d
AM
40
41require '/var/ipfire/general-functions.pl';
5cf83d56 42require '/var/ipfire/geoip-functions.pl';
2a81ab0d
AM
43
44my $confignet = "${General::swroot}/fwhosts/customnetworks";
45my $confighost = "${General::swroot}/fwhosts/customhosts";
46my $configgrp = "${General::swroot}/fwhosts/customgroups";
b9ca2fa6 47my $configgeoipgrp = "${General::swroot}/fwhosts/customgeoipgrp";
2a81ab0d
AM
48my $configsrv = "${General::swroot}/fwhosts/customservices";
49my $configsrvgrp = "${General::swroot}/fwhosts/customservicegrp";
50my $configccdnet = "${General::swroot}/ovpn/ccd.conf";
51my $configccdhost = "${General::swroot}/ovpn/ovpnconfig";
52my $configipsec = "${General::swroot}/vpn/config";
53my $configovpn = "${General::swroot}/ovpn/settings";
54my $val;
55my $field;
fd169d0a 56my $netsettings = "${General::swroot}/ethernet/settings";
2a81ab0d
AM
57
58&General::readhash("/var/ipfire/ethernet/settings", \%netsettings);
59&General::readhash("${General::swroot}/ovpn/settings", \%ovpnsettings);
60&General::readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
2a81ab0d
AM
61
62&General::readhasharray("$confignet", \%customnetwork);
63&General::readhasharray("$confighost", \%customhost);
64&General::readhasharray("$configgrp", \%customgrp);
b9ca2fa6 65&General::readhasharray("$configgeoipgrp", \%customgeoipgrp);
2a81ab0d
AM
66&General::readhasharray("$configccdnet", \%ccdnet);
67&General::readhasharray("$configccdhost", \%ccdhost);
68&General::readhasharray("$configipsec", \%ipsecconf);
69&General::readhasharray("$configsrv", \%customservice);
70&General::readhasharray("$configsrvgrp", \%customservicegrp);
085a20ec 71&General::get_aliases(\%aliases);
2a81ab0d
AM
72
73sub get_srv_prot
74{
75 my $val=shift;
992394d5 76 foreach my $key (sort {$a <=> $b} keys %customservice){
2a81ab0d
AM
77 if($customservice{$key}[0] eq $val){
78 if ($customservice{$key}[0] eq $val){
79 return $customservice{$key}[2];
80 }
81 }
82 }
83}
84sub get_srvgrp_prot
85{
86 my $val=shift;
87 my @ips=();
88 my $tcp;
89 my $udp;
90 my $icmp;
992394d5 91 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
2a81ab0d
AM
92 if($customservicegrp{$key}[0] eq $val){
93 if (&get_srv_prot($customservicegrp{$key}[2]) eq 'TCP'){
94 $tcp=1;
95 }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'UDP'){
96 $udp=1;
97 }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'ICMP'){
98 $icmp=1;
82b837cf
AM
99 }else{
100 #Protocols used in servicegroups
101 push (@ips,$customservicegrp{$key}[2]);
102 }
2a81ab0d
AM
103 }
104 }
105 if ($tcp eq '1'){push (@ips,'TCP');}
106 if ($udp eq '1'){push (@ips,'UDP');}
107 if ($icmp eq '1'){push (@ips,'ICMP');}
108 my $back=join(",",@ips);
109 return $back;
110
111}
2a81ab0d
AM
112sub get_srv_port
113{
114 my $val=shift;
115 my $field=shift;
116 my $prot=shift;
992394d5 117 foreach my $key (sort {$a <=> $b} keys %customservice){
14bcb9a2
AM
118 if($customservice{$key}[0] eq $val && $customservice{$key}[2] eq $prot){
119 return $customservice{$key}[$field];
2a81ab0d
AM
120 }
121 }
122}
123sub get_srvgrp_port
124{
125 my $val=shift;
126 my $prot=shift;
127 my $back;
128 my $value;
129 my @ips=();
992394d5 130 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
2a81ab0d
AM
131 if($customservicegrp{$key}[0] eq $val){
132 if ($prot ne 'ICMP'){
133 $value=&get_srv_port($customservicegrp{$key}[2],1,$prot);
134 }elsif ($prot eq 'ICMP'){
135 $value=&get_srv_port($customservicegrp{$key}[2],3,$prot);
136 }
137 push (@ips,$value) if ($value ne '') ;
138 }
139 }
140 if($prot ne 'ICMP'){
141 if ($#ips gt 0){$back="-m multiport --dports ";}else{$back="--dport ";}
142 }elsif ($prot eq 'ICMP'){
143 $back="--icmp-type ";
144 }
145
146 $back.=join(",",@ips);
147 return $back;
148}
149sub get_ipsec_net_ip
150{
151 my $val=shift;
152 my $field=shift;
992394d5 153 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
8b20ca2d
AM
154 #adapt $val to reflect real name without subnet (if rule with only one ipsec subnet is created)
155 my @tmpval = split (/\|/, $val);
156 $val = $tmpval[0];
2a81ab0d
AM
157 if($ipsecconf{$key}[1] eq $val){
158 return $ipsecconf{$key}[$field];
159 }
160 }
161}
162sub get_ipsec_host_ip
163{
164 my $val=shift;
165 my $field=shift;
992394d5 166 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
2a81ab0d
AM
167 if($ipsecconf{$key}[1] eq $val){
168 return $ipsecconf{$key}[$field];
169 }
170 }
171}
7ba652af
MT
172sub get_ipsec_id {
173 my $val = shift;
174
175 foreach my $key (keys %ipsecconf) {
176 if ($ipsecconf{$key}[1] eq $val) {
177 return $key;
178 }
179 }
180}
2a81ab0d
AM
181sub get_ovpn_n2n_ip
182{
183 my $val=shift;
184 my $field=shift;
992394d5 185 foreach my $key (sort {$a <=> $b} keys %ccdhost){
2a81ab0d
AM
186 if($ccdhost{$key}[1] eq $val){
187 return $ccdhost{$key}[$field];
188 }
189 }
190}
191sub get_ovpn_host_ip
192{
193 my $val=shift;
194 my $field=shift;
992394d5 195 foreach my $key (sort {$a <=> $b} keys %ccdhost){
2a81ab0d
AM
196 if($ccdhost{$key}[1] eq $val){
197 return $ccdhost{$key}[$field];
198 }
199 }
200}
201sub get_ovpn_net_ip
202{
203
204 my $val=shift;
205 my $field=shift;
992394d5 206 foreach my $key (sort {$a <=> $b} keys %ccdnet){
2a81ab0d
AM
207 if($ccdnet{$key}[0] eq $val){
208 return $ccdnet{$key}[$field];
209 }
210 }
211}
212sub get_grp_ip
213{
214 my $val=shift;
215 my $src=shift;
992394d5 216 foreach my $key (sort {$a <=> $b} keys %customgrp){
2a81ab0d
AM
217 if ($customgrp{$key}[0] eq $val){
218 &get_address($customgrp{$key}[3],$src);
219 }
220 }
221
222}
223sub get_std_net_ip
224{
225 my $val=shift;
ddcec9d3 226 my $con=shift;
2a81ab0d
AM
227 if ($val eq 'ALL'){
228 return "0.0.0.0/0.0.0.0";
229 }elsif($val eq 'GREEN'){
230 return "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
231 }elsif($val eq 'ORANGE'){
232 return "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
233 }elsif($val eq 'BLUE'){
234 return "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
62fc8511 235 }elsif($val eq 'RED'){
48f07c19 236 return "0.0.0.0/0";
2a81ab0d
AM
237 }elsif($val =~ /OpenVPN/i){
238 return "$ovpnsettings{'DOVPN_SUBNET'}";
239 }elsif($val =~ /IPsec/i){
240 return "$ipsecsettings{'RW_NET'}";
5d7faa45
AM
241 }elsif($val eq 'IPFire'){
242 return ;
2a81ab0d
AM
243 }
244}
48f07c19
AM
245sub get_interface
246{
247 my $net=shift;
248 if($net eq "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"){
249 return "$netsettings{'GREEN_DEV'}";
250 }
251 if($net eq "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"){
252 return "$netsettings{'ORANGE_DEV'}";
253 }
254 if($net eq "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"){
255 return "$netsettings{'BLUE_DEV'}";
256 }
a21f2f6a
MT
257 if($net eq "0.0.0.0/0") {
258 return &get_external_interface();
48f07c19
AM
259 }
260 return "";
261}
2a81ab0d
AM
262sub get_net_ip
263{
264 my $val=shift;
992394d5 265 foreach my $key (sort {$a <=> $b} keys %customnetwork){
2a81ab0d
AM
266 if($customnetwork{$key}[0] eq $val){
267 return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
268 }
269 }
270}
271sub get_host_ip
272{
273 my $val=shift;
274 my $src=shift;
992394d5 275 foreach my $key (sort {$a <=> $b} keys %customhost){
2a81ab0d
AM
276 if($customhost{$key}[0] eq $val){
277 if ($customhost{$key}[1] eq 'mac' && $src eq 'src'){
278 return "-m mac --mac-source $customhost{$key}[2]";
279 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'src'){
280 return "$customhost{$key}[2]";
281 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'tgt'){
282 return "$customhost{$key}[2]";
283 }elsif($customhost{$key}[1] eq 'mac' && $src eq 'tgt'){
284 return "none";
285 }
286 }
287 }
288}
fd169d0a
AM
289sub get_addresses
290{
4e54e3c6
AM
291 my $hash = shift;
292 my $key = shift;
293 my $type = shift;
294
295 my @addresses = ();
296 my $addr_type;
297 my $value;
298 my $group_name;
299
300 if ($type eq "src") {
301 $addr_type = $$hash{$key}[3];
302 $value = $$hash{$key}[4];
303
304 } elsif ($type eq "tgt") {
305 $addr_type = $$hash{$key}[5];
306 $value = $$hash{$key}[6];
307 }
308
309 if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) {
310 foreach my $grp (sort {$a <=> $b} keys %customgrp) {
311 if ($customgrp{$grp}[0] eq $value) {
312 my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type);
313
b9ca2fa6
AM
314 if (@address) {
315 push(@addresses, @address);
316 }
317 }
318 }
319 }elsif ($addr_type ~~ ["cust_geoip_src", "cust_geoip_tgt"] && $value =~ "group:") {
320 $value=substr($value,6);
321 foreach my $grp (sort {$a <=> $b} keys %customgeoipgrp) {
322 if ($customgeoipgrp{$grp}[0] eq $value) {
323 my @address = &get_address($addr_type, $customgeoipgrp{$grp}[2], $type);
324
4e54e3c6
AM
325 if (@address) {
326 push(@addresses, @address);
327 }
328 }
329 }
330 } else {
331 my @address = &get_address($addr_type, $value, $type);
332
333 if (@address) {
334 push(@addresses, @address);
335 }
336 }
337
338 return @addresses;
339}
fd169d0a
AM
340sub get_address
341{
4e54e3c6
AM
342 my $key = shift;
343 my $value = shift;
344 my $type = shift;
345
346 my @ret = ();
347
348 # If the user manually typed an address, we just check if it is a MAC
349 # address. Otherwise, we assume that it is an IP address.
350 if ($key ~~ ["src_addr", "tgt_addr"]) {
351 if (&General::validmac($value)) {
48f07c19 352 push(@ret, ["-m mac --mac-source $value", ""]);
4e54e3c6 353 } else {
48f07c19 354 push(@ret, [$value, ""]);
4e54e3c6
AM
355 }
356
357 # If a default network interface (GREEN, BLUE, etc.) is selected, we
358 # try to get the corresponding address of the network.
359 } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) {
360 my $external_interface = &get_external_interface();
361
362 my $network_address = &get_std_net_ip($value, $external_interface);
48f07c19 363
4e54e3c6 364 if ($network_address) {
48f07c19
AM
365 my $interface = &get_interface($network_address);
366 push(@ret, [$network_address, $interface]);
4e54e3c6
AM
367 }
368
369 # Custom networks.
370 } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
371 my $network_address = &get_net_ip($value);
372 if ($network_address) {
48f07c19 373 push(@ret, [$network_address, ""]);
4e54e3c6
AM
374 }
375
376 # Custom hosts.
377 } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
378 my $host_address = &get_host_ip($value, $type);
379 if ($host_address) {
48f07c19 380 push(@ret, [$host_address, ""]);
4e54e3c6
AM
381 }
382
383 # OpenVPN networks.
384 } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) {
385 my $network_address = &get_ovpn_net_ip($value, 1);
386 if ($network_address) {
48f07c19 387 push(@ret, [$network_address, ""]);
4e54e3c6
AM
388 }
389
390 # OpenVPN hosts.
391 } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
392 my $host_address = &get_ovpn_host_ip($value, 33);
393 if ($host_address) {
48f07c19 394 push(@ret, [$host_address, ""]);
4e54e3c6
AM
395 }
396
397 # OpenVPN N2N.
398 } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) {
399 my $network_address = &get_ovpn_n2n_ip($value, 11);
400 if ($network_address) {
48f07c19 401 push(@ret, [$network_address, ""]);
4e54e3c6
AM
402 }
403
404 # IPsec networks.
405 } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
8b20ca2d
AM
406 #Check if we have multiple subnets and only want one of them
407 if ( $value =~ /\|/ ){
408 my @parts = split(/\|/, $value);
409 push(@ret, [$parts[1], ""]);
410 }else{
7ba652af
MT
411 my $interface_mode = &get_ipsec_net_ip($value, 36);
412 if ($interface_mode ~~ ["gre", "vti"]) {
413 my $id = &get_ipsec_id($value);
414 push(@ret, ["0.0.0.0/0", "${interface_mode}${id}"]);
415 } else {
416 my $network_address = &get_ipsec_net_ip($value, 11);
417 my @nets = split(/\|/, $network_address);
418 foreach my $net (@nets) {
419 push(@ret, [$net, ""]);
420 }
8b20ca2d 421 }
4e54e3c6
AM
422 }
423
424 # The firewall's own IP addresses.
425 } elsif ($key ~~ ["ipfire", "ipfire_src"]) {
426 # ALL
427 if ($value eq "ALL") {
48f07c19 428 push(@ret, ["0/0", ""]);
4e54e3c6
AM
429
430 # GREEN
431 } elsif ($value eq "GREEN") {
48f07c19 432 push(@ret, [$netsettings{"GREEN_ADDRESS"}, ""]);
4e54e3c6
AM
433
434 # BLUE
435 } elsif ($value eq "BLUE") {
48f07c19 436 push(@ret, [$netsettings{"BLUE_ADDRESS"}, ""]);
4e54e3c6
AM
437
438 # ORANGE
439 } elsif ($value eq "ORANGE") {
48f07c19 440 push(@ret, [$netsettings{"ORANGE_ADDRESS"}, ""]);
4e54e3c6
AM
441
442 # RED
443 } elsif ($value ~~ ["RED", "RED1"]) {
444 my $address = &get_external_address();
445 if ($address) {
48f07c19 446 push(@ret, [$address, ""]);
4e54e3c6
AM
447 }
448
449 # Aliases
450 } else {
085a20ec
MT
451 my $alias = &get_alias($value);
452 if ($alias) {
48f07c19 453 push(@ret, [$alias, ""]);
4e54e3c6
AM
454 }
455 }
456
b9ca2fa6
AM
457 # Handle rule options with GeoIP as source.
458 } elsif ($key eq "cust_geoip_src") {
459 # Get external interface.
460 my $external_interface = &get_external_interface();
461
462 push(@ret, ["-m geoip --src-cc $value", "$external_interface"]);
463
464 # Handle rule options with GeoIP as target.
465 } elsif ($key eq "cust_geoip_tgt") {
466 # Get external interface.
467 my $external_interface = &get_external_interface();
468
469 push(@ret, ["-m geoip --dst-cc $value", "$external_interface"]);
470
4e54e3c6
AM
471 # If nothing was selected, we assume "any".
472 } else {
48f07c19 473 push(@ret, ["0/0", ""]);
4e54e3c6
AM
474 }
475
476 return @ret;
477}
fd169d0a
AM
478sub get_external_interface()
479{
4e54e3c6
AM
480 open(IFACE, "/var/ipfire/red/iface") or return "";
481 my $iface = <IFACE>;
482 close(IFACE);
483
484 return $iface;
485}
fd169d0a
AM
486sub get_external_address()
487{
4e54e3c6
AM
488 open(ADDR, "/var/ipfire/red/local-ipaddress") or return "";
489 my $address = <ADDR>;
490 close(ADDR);
491
492 return $address;
493}
fd169d0a
AM
494sub get_alias
495{
4e54e3c6
AM
496 my $id = shift;
497
498 foreach my $alias (sort keys %aliases) {
499 if ($id eq $alias) {
085a20ec 500 return $aliases{$alias}{"IPT"};
4e54e3c6
AM
501 }
502 }
503}
085a20ec
MT
504
505sub get_nat_address {
4e54e3c6
AM
506 my $zone = shift;
507 my $source = shift;
508
509 # Any static address of any zone.
510 if ($zone eq "AUTO") {
fd169d0a 511 if ($source && ($source !~ m/mac/i )) {
4e54e3c6
AM
512 my $firewall_ip = &get_internal_firewall_ip_address($source, 1);
513 if ($firewall_ip) {
514 return $firewall_ip;
515 }
516
517 $firewall_ip = &get_matching_firewall_address($source, 1);
518 if ($firewall_ip) {
519 return $firewall_ip;
520 }
521 }
522
523 return &get_external_address();
524
525 } elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
c71499d8 526 return $netsettings{$zone . "_ADDRESS"};
4e54e3c6 527
085a20ec 528 } elsif ($zone ~~ ["Default IP", "ALL"]) {
4e54e3c6
AM
529 return &get_external_address();
530
531 } else {
085a20ec
MT
532 my $alias = &get_alias($zone);
533 unless ($alias) {
534 $alias = &get_external_address();
535 }
536 return $alias;
4e54e3c6
AM
537 }
538
539 print_error("Could not find NAT address");
540}
085a20ec 541
fd169d0a
AM
542sub get_internal_firewall_ip_addresses
543{
4e54e3c6
AM
544 my $use_orange = shift;
545
546 my @zones = ("GREEN", "BLUE");
547 if ($use_orange) {
548 push(@zones, "ORANGE");
549 }
550
551 my @addresses = ();
552 for my $zone (@zones) {
c71499d8 553 next unless (exists $netsettings{$zone . "_ADDRESS"});
4e54e3c6 554
c71499d8 555 my $zone_address = $netsettings{$zone . "_ADDRESS"};
4e54e3c6
AM
556 push(@addresses, $zone_address);
557 }
558
559 return @addresses;
560}
fd169d0a
AM
561sub get_matching_firewall_address
562{
4e54e3c6
AM
563 my $addr = shift;
564 my $use_orange = shift;
565
566 my ($address, $netmask) = split("/", $addr);
567
568 my @zones = ("GREEN", "BLUE");
569 if ($use_orange) {
570 push(@zones, "ORANGE");
571 }
572
573 foreach my $zone (@zones) {
c71499d8 574 next unless (exists $netsettings{$zone . "_ADDRESS"});
4e54e3c6 575
c71499d8
AM
576 my $zone_subnet = $netsettings{$zone . "_NETADDRESS"};
577 my $zone_mask = $netsettings{$zone . "_NETMASK"};
4e54e3c6
AM
578
579 if (&General::IpInSubnet($address, $zone_subnet, $zone_mask)) {
c71499d8 580 return $netsettings{$zone . "_ADDRESS"};
4e54e3c6
AM
581 }
582 }
583
584 return 0;
585}
fd169d0a
AM
586sub get_internal_firewall_ip_address
587{
4e54e3c6
AM
588 my $subnet = shift;
589 my $use_orange = shift;
590
591 my ($net_address, $net_mask) = split("/", $subnet);
592 if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
593 return 0;
594 }
595
aa5f4b65
MT
596 # Convert net mask into correct format for &General::IpInSubnet().
597 $net_mask = &General::iporsubtodec($net_mask);
598
4e54e3c6
AM
599 my @addresses = &get_internal_firewall_ip_addresses($use_orange);
600 foreach my $zone_address (@addresses) {
601 if (&General::IpInSubnet($zone_address, $net_address, $net_mask)) {
602 return $zone_address;
603 }
604 }
605
606 return 0;
607}
608
593c3227 609sub get_geoip_locations() {
8ff42d82 610 return &GeoIP::get_geoip_locations();
593c3227
SS
611}
612
2a81ab0d 613return 1;