]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - config/firewall/firewall-lib.pl
core125: Ship JSON-C
[people/pmueller/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';
42
43my $confignet = "${General::swroot}/fwhosts/customnetworks";
44my $confighost = "${General::swroot}/fwhosts/customhosts";
45my $configgrp = "${General::swroot}/fwhosts/customgroups";
b9ca2fa6 46my $configgeoipgrp = "${General::swroot}/fwhosts/customgeoipgrp";
2a81ab0d
AM
47my $configsrv = "${General::swroot}/fwhosts/customservices";
48my $configsrvgrp = "${General::swroot}/fwhosts/customservicegrp";
49my $configccdnet = "${General::swroot}/ovpn/ccd.conf";
50my $configccdhost = "${General::swroot}/ovpn/ovpnconfig";
51my $configipsec = "${General::swroot}/vpn/config";
52my $configovpn = "${General::swroot}/ovpn/settings";
53my $val;
54my $field;
fd169d0a 55my $netsettings = "${General::swroot}/ethernet/settings";
2a81ab0d
AM
56
57&General::readhash("/var/ipfire/ethernet/settings", \%netsettings);
58&General::readhash("${General::swroot}/ovpn/settings", \%ovpnsettings);
59&General::readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
2a81ab0d
AM
60
61&General::readhasharray("$confignet", \%customnetwork);
62&General::readhasharray("$confighost", \%customhost);
63&General::readhasharray("$configgrp", \%customgrp);
b9ca2fa6 64&General::readhasharray("$configgeoipgrp", \%customgeoipgrp);
2a81ab0d
AM
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);
085a20ec 70&General::get_aliases(\%aliases);
2a81ab0d
AM
71
72sub get_srv_prot
73{
74 my $val=shift;
992394d5 75 foreach my $key (sort {$a <=> $b} keys %customservice){
2a81ab0d
AM
76 if($customservice{$key}[0] eq $val){
77 if ($customservice{$key}[0] eq $val){
78 return $customservice{$key}[2];
79 }
80 }
81 }
82}
83sub get_srvgrp_prot
84{
85 my $val=shift;
86 my @ips=();
87 my $tcp;
88 my $udp;
89 my $icmp;
992394d5 90 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
2a81ab0d
AM
91 if($customservicegrp{$key}[0] eq $val){
92 if (&get_srv_prot($customservicegrp{$key}[2]) eq 'TCP'){
93 $tcp=1;
94 }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'UDP'){
95 $udp=1;
96 }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'ICMP'){
97 $icmp=1;
82b837cf
AM
98 }else{
99 #Protocols used in servicegroups
100 push (@ips,$customservicegrp{$key}[2]);
101 }
2a81ab0d
AM
102 }
103 }
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);
108 return $back;
109
110}
2a81ab0d
AM
111sub get_srv_port
112{
113 my $val=shift;
114 my $field=shift;
115 my $prot=shift;
992394d5 116 foreach my $key (sort {$a <=> $b} keys %customservice){
14bcb9a2
AM
117 if($customservice{$key}[0] eq $val && $customservice{$key}[2] eq $prot){
118 return $customservice{$key}[$field];
2a81ab0d
AM
119 }
120 }
121}
122sub get_srvgrp_port
123{
124 my $val=shift;
125 my $prot=shift;
126 my $back;
127 my $value;
128 my @ips=();
992394d5 129 foreach my $key (sort {$a <=> $b} keys %customservicegrp){
2a81ab0d
AM
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);
135 }
136 push (@ips,$value) if ($value ne '') ;
137 }
138 }
139 if($prot ne 'ICMP'){
140 if ($#ips gt 0){$back="-m multiport --dports ";}else{$back="--dport ";}
141 }elsif ($prot eq 'ICMP'){
142 $back="--icmp-type ";
143 }
144
145 $back.=join(",",@ips);
146 return $back;
147}
148sub get_ipsec_net_ip
149{
150 my $val=shift;
151 my $field=shift;
992394d5 152 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
8b20ca2d
AM
153 #adapt $val to reflect real name without subnet (if rule with only one ipsec subnet is created)
154 my @tmpval = split (/\|/, $val);
155 $val = $tmpval[0];
2a81ab0d
AM
156 if($ipsecconf{$key}[1] eq $val){
157 return $ipsecconf{$key}[$field];
158 }
159 }
160}
161sub get_ipsec_host_ip
162{
163 my $val=shift;
164 my $field=shift;
992394d5 165 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
2a81ab0d
AM
166 if($ipsecconf{$key}[1] eq $val){
167 return $ipsecconf{$key}[$field];
168 }
169 }
170}
171sub get_ovpn_n2n_ip
172{
173 my $val=shift;
174 my $field=shift;
992394d5 175 foreach my $key (sort {$a <=> $b} keys %ccdhost){
2a81ab0d
AM
176 if($ccdhost{$key}[1] eq $val){
177 return $ccdhost{$key}[$field];
178 }
179 }
180}
181sub get_ovpn_host_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_net_ip
192{
193
194 my $val=shift;
195 my $field=shift;
992394d5 196 foreach my $key (sort {$a <=> $b} keys %ccdnet){
2a81ab0d
AM
197 if($ccdnet{$key}[0] eq $val){
198 return $ccdnet{$key}[$field];
199 }
200 }
201}
202sub get_grp_ip
203{
204 my $val=shift;
205 my $src=shift;
992394d5 206 foreach my $key (sort {$a <=> $b} keys %customgrp){
2a81ab0d
AM
207 if ($customgrp{$key}[0] eq $val){
208 &get_address($customgrp{$key}[3],$src);
209 }
210 }
211
212}
213sub get_std_net_ip
214{
215 my $val=shift;
ddcec9d3 216 my $con=shift;
2a81ab0d
AM
217 if ($val eq 'ALL'){
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'}";
62fc8511 225 }elsif($val eq 'RED'){
48f07c19 226 return "0.0.0.0/0";
2a81ab0d
AM
227 }elsif($val =~ /OpenVPN/i){
228 return "$ovpnsettings{'DOVPN_SUBNET'}";
229 }elsif($val =~ /IPsec/i){
230 return "$ipsecsettings{'RW_NET'}";
5d7faa45
AM
231 }elsif($val eq 'IPFire'){
232 return ;
2a81ab0d
AM
233 }
234}
48f07c19
AM
235sub get_interface
236{
237 my $net=shift;
238 if($net eq "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"){
239 return "$netsettings{'GREEN_DEV'}";
240 }
241 if($net eq "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"){
242 return "$netsettings{'ORANGE_DEV'}";
243 }
244 if($net eq "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"){
245 return "$netsettings{'BLUE_DEV'}";
246 }
a21f2f6a
MT
247 if($net eq "0.0.0.0/0") {
248 return &get_external_interface();
48f07c19
AM
249 }
250 return "";
251}
2a81ab0d
AM
252sub get_net_ip
253{
254 my $val=shift;
992394d5 255 foreach my $key (sort {$a <=> $b} keys %customnetwork){
2a81ab0d
AM
256 if($customnetwork{$key}[0] eq $val){
257 return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
258 }
259 }
260}
261sub get_host_ip
262{
263 my $val=shift;
264 my $src=shift;
992394d5 265 foreach my $key (sort {$a <=> $b} keys %customhost){
2a81ab0d
AM
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'){
274 return "none";
275 }
276 }
277 }
278}
fd169d0a
AM
279sub get_addresses
280{
4e54e3c6
AM
281 my $hash = shift;
282 my $key = shift;
283 my $type = shift;
284
285 my @addresses = ();
286 my $addr_type;
287 my $value;
288 my $group_name;
289
290 if ($type eq "src") {
291 $addr_type = $$hash{$key}[3];
292 $value = $$hash{$key}[4];
293
294 } elsif ($type eq "tgt") {
295 $addr_type = $$hash{$key}[5];
296 $value = $$hash{$key}[6];
297 }
298
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);
303
b9ca2fa6
AM
304 if (@address) {
305 push(@addresses, @address);
306 }
307 }
308 }
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);
314
4e54e3c6
AM
315 if (@address) {
316 push(@addresses, @address);
317 }
318 }
319 }
320 } else {
321 my @address = &get_address($addr_type, $value, $type);
322
323 if (@address) {
324 push(@addresses, @address);
325 }
326 }
327
328 return @addresses;
329}
fd169d0a
AM
330sub get_address
331{
4e54e3c6
AM
332 my $key = shift;
333 my $value = shift;
334 my $type = shift;
335
336 my @ret = ();
337
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)) {
48f07c19 342 push(@ret, ["-m mac --mac-source $value", ""]);
4e54e3c6 343 } else {
48f07c19 344 push(@ret, [$value, ""]);
4e54e3c6
AM
345 }
346
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();
351
352 my $network_address = &get_std_net_ip($value, $external_interface);
48f07c19 353
4e54e3c6 354 if ($network_address) {
48f07c19
AM
355 my $interface = &get_interface($network_address);
356 push(@ret, [$network_address, $interface]);
4e54e3c6
AM
357 }
358
359 # Custom networks.
360 } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
361 my $network_address = &get_net_ip($value);
362 if ($network_address) {
48f07c19 363 push(@ret, [$network_address, ""]);
4e54e3c6
AM
364 }
365
366 # Custom hosts.
367 } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
368 my $host_address = &get_host_ip($value, $type);
369 if ($host_address) {
48f07c19 370 push(@ret, [$host_address, ""]);
4e54e3c6
AM
371 }
372
373 # OpenVPN networks.
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) {
48f07c19 377 push(@ret, [$network_address, ""]);
4e54e3c6
AM
378 }
379
380 # OpenVPN hosts.
381 } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
382 my $host_address = &get_ovpn_host_ip($value, 33);
383 if ($host_address) {
48f07c19 384 push(@ret, [$host_address, ""]);
4e54e3c6
AM
385 }
386
387 # OpenVPN N2N.
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) {
48f07c19 391 push(@ret, [$network_address, ""]);
4e54e3c6
AM
392 }
393
394 # IPsec networks.
395 } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
8b20ca2d
AM
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], ""]);
400 }else{
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, ""]);
405 }
4e54e3c6
AM
406 }
407
408 # The firewall's own IP addresses.
409 } elsif ($key ~~ ["ipfire", "ipfire_src"]) {
410 # ALL
411 if ($value eq "ALL") {
48f07c19 412 push(@ret, ["0/0", ""]);
4e54e3c6
AM
413
414 # GREEN
415 } elsif ($value eq "GREEN") {
48f07c19 416 push(@ret, [$netsettings{"GREEN_ADDRESS"}, ""]);
4e54e3c6
AM
417
418 # BLUE
419 } elsif ($value eq "BLUE") {
48f07c19 420 push(@ret, [$netsettings{"BLUE_ADDRESS"}, ""]);
4e54e3c6
AM
421
422 # ORANGE
423 } elsif ($value eq "ORANGE") {
48f07c19 424 push(@ret, [$netsettings{"ORANGE_ADDRESS"}, ""]);
4e54e3c6
AM
425
426 # RED
427 } elsif ($value ~~ ["RED", "RED1"]) {
428 my $address = &get_external_address();
429 if ($address) {
48f07c19 430 push(@ret, [$address, ""]);
4e54e3c6
AM
431 }
432
433 # Aliases
434 } else {
085a20ec
MT
435 my $alias = &get_alias($value);
436 if ($alias) {
48f07c19 437 push(@ret, [$alias, ""]);
4e54e3c6
AM
438 }
439 }
440
b9ca2fa6
AM
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();
445
446 push(@ret, ["-m geoip --src-cc $value", "$external_interface"]);
447
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();
452
453 push(@ret, ["-m geoip --dst-cc $value", "$external_interface"]);
454
4e54e3c6
AM
455 # If nothing was selected, we assume "any".
456 } else {
48f07c19 457 push(@ret, ["0/0", ""]);
4e54e3c6
AM
458 }
459
460 return @ret;
461}
fd169d0a
AM
462sub get_external_interface()
463{
4e54e3c6
AM
464 open(IFACE, "/var/ipfire/red/iface") or return "";
465 my $iface = <IFACE>;
466 close(IFACE);
467
468 return $iface;
469}
fd169d0a
AM
470sub get_external_address()
471{
4e54e3c6
AM
472 open(ADDR, "/var/ipfire/red/local-ipaddress") or return "";
473 my $address = <ADDR>;
474 close(ADDR);
475
476 return $address;
477}
fd169d0a
AM
478sub get_alias
479{
4e54e3c6
AM
480 my $id = shift;
481
482 foreach my $alias (sort keys %aliases) {
483 if ($id eq $alias) {
085a20ec 484 return $aliases{$alias}{"IPT"};
4e54e3c6
AM
485 }
486 }
487}
085a20ec
MT
488
489sub get_nat_address {
4e54e3c6
AM
490 my $zone = shift;
491 my $source = shift;
492
493 # Any static address of any zone.
494 if ($zone eq "AUTO") {
fd169d0a 495 if ($source && ($source !~ m/mac/i )) {
4e54e3c6
AM
496 my $firewall_ip = &get_internal_firewall_ip_address($source, 1);
497 if ($firewall_ip) {
498 return $firewall_ip;
499 }
500
501 $firewall_ip = &get_matching_firewall_address($source, 1);
502 if ($firewall_ip) {
503 return $firewall_ip;
504 }
505 }
506
507 return &get_external_address();
508
509 } elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
c71499d8 510 return $netsettings{$zone . "_ADDRESS"};
4e54e3c6 511
085a20ec 512 } elsif ($zone ~~ ["Default IP", "ALL"]) {
4e54e3c6
AM
513 return &get_external_address();
514
515 } else {
085a20ec
MT
516 my $alias = &get_alias($zone);
517 unless ($alias) {
518 $alias = &get_external_address();
519 }
520 return $alias;
4e54e3c6
AM
521 }
522
523 print_error("Could not find NAT address");
524}
085a20ec 525
fd169d0a
AM
526sub get_internal_firewall_ip_addresses
527{
4e54e3c6
AM
528 my $use_orange = shift;
529
530 my @zones = ("GREEN", "BLUE");
531 if ($use_orange) {
532 push(@zones, "ORANGE");
533 }
534
535 my @addresses = ();
536 for my $zone (@zones) {
c71499d8 537 next unless (exists $netsettings{$zone . "_ADDRESS"});
4e54e3c6 538
c71499d8 539 my $zone_address = $netsettings{$zone . "_ADDRESS"};
4e54e3c6
AM
540 push(@addresses, $zone_address);
541 }
542
543 return @addresses;
544}
fd169d0a
AM
545sub get_matching_firewall_address
546{
4e54e3c6
AM
547 my $addr = shift;
548 my $use_orange = shift;
549
550 my ($address, $netmask) = split("/", $addr);
551
552 my @zones = ("GREEN", "BLUE");
553 if ($use_orange) {
554 push(@zones, "ORANGE");
555 }
556
557 foreach my $zone (@zones) {
c71499d8 558 next unless (exists $netsettings{$zone . "_ADDRESS"});
4e54e3c6 559
c71499d8
AM
560 my $zone_subnet = $netsettings{$zone . "_NETADDRESS"};
561 my $zone_mask = $netsettings{$zone . "_NETMASK"};
4e54e3c6
AM
562
563 if (&General::IpInSubnet($address, $zone_subnet, $zone_mask)) {
c71499d8 564 return $netsettings{$zone . "_ADDRESS"};
4e54e3c6
AM
565 }
566 }
567
568 return 0;
569}
fd169d0a
AM
570sub get_internal_firewall_ip_address
571{
4e54e3c6
AM
572 my $subnet = shift;
573 my $use_orange = shift;
574
575 my ($net_address, $net_mask) = split("/", $subnet);
576 if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
577 return 0;
578 }
579
aa5f4b65
MT
580 # Convert net mask into correct format for &General::IpInSubnet().
581 $net_mask = &General::iporsubtodec($net_mask);
582
4e54e3c6
AM
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;
587 }
588 }
589
590 return 0;
591}
592
593c3227
SS
593sub get_geoip_locations() {
594 # Path to the directory which contains the binary geoip
595 # databases.
596 my $directory="/usr/share/xt_geoip/LE";
597
598 # Array to store the final country list.
599 my @country_codes = ();
600
601 # Open location and do a directory listing.
602 opendir(DIR, "$directory");
603 my @locations = readdir(DIR);
604 closedir(DIR);
605
606 # Loop through the directory listing, and cut of the file extensions.
607 foreach my $location (sort @locations) {
608 # skip . and ..
609 next if($location =~ /^\.$/);
610 next if($location =~ /^\.\.$/);
611
612 # Remove whitespaces.
613 chomp($location);
614
615 # Cut-off file extension.
192a8266 616 my ($country_code, $extension) = split(/\./, $location);
593c3227
SS
617
618 # Add country code to array.
192a8266 619 push(@country_codes, $country_code);
593c3227
SS
620 }
621
622 # Return final array.
623 return @country_codes;
624}
625
2a81ab0d 626return 1;