]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - config/firewall/firewall-lib.pl
Merge remote-tracking branch 'ms/dhcp-rfc2136-broken-down' into next
[people/pmueller/ipfire-2.x.git] / config / firewall / firewall-lib.pl
1 #!/usr/bin/perl
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2013 Alexander Marx <amarx@ipfire.org> #
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 ###############################################################################
21
22 use strict;
23 no warnings 'uninitialized';
24
25 package fwlib;
26
27 my %customnetwork=();
28 my %customhost=();
29 my %customgrp=();
30 my %customgeoipgrp=();
31 my %customservice=();
32 my %customservicegrp=();
33 my %ccdnet=();
34 my %ccdhost=();
35 my %ipsecconf=();
36 my %ipsecsettings=();
37 my %netsettings=();
38 my %ovpnsettings=();
39 my %aliases=();
40
41 require '/var/ipfire/general-functions.pl';
42
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";
53 my $val;
54 my $field;
55 my $netsettings = "${General::swroot}/ethernet/settings";
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);
60
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);
71
72 sub get_srv_prot
73 {
74 my $val=shift;
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];
79 }
80 }
81 }
82 }
83 sub get_srvgrp_prot
84 {
85 my $val=shift;
86 my @ips=();
87 my $tcp;
88 my $udp;
89 my $icmp;
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'){
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;
98 }else{
99 #Protocols used in servicegroups
100 push (@ips,$customservicegrp{$key}[2]);
101 }
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 }
111 sub get_srv_port
112 {
113 my $val=shift;
114 my $field=shift;
115 my $prot=shift;
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];
119 }
120 }
121 }
122 sub get_srvgrp_port
123 {
124 my $val=shift;
125 my $prot=shift;
126 my $back;
127 my $value;
128 my @ips=();
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);
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 }
148 sub get_ipsec_net_ip
149 {
150 my $val=shift;
151 my $field=shift;
152 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
153 if($ipsecconf{$key}[1] eq $val){
154 return $ipsecconf{$key}[$field];
155 }
156 }
157 }
158 sub get_ipsec_host_ip
159 {
160 my $val=shift;
161 my $field=shift;
162 foreach my $key (sort {$a <=> $b} keys %ipsecconf){
163 if($ipsecconf{$key}[1] eq $val){
164 return $ipsecconf{$key}[$field];
165 }
166 }
167 }
168 sub get_ovpn_n2n_ip
169 {
170 my $val=shift;
171 my $field=shift;
172 foreach my $key (sort {$a <=> $b} keys %ccdhost){
173 if($ccdhost{$key}[1] eq $val){
174 return $ccdhost{$key}[$field];
175 }
176 }
177 }
178 sub get_ovpn_host_ip
179 {
180 my $val=shift;
181 my $field=shift;
182 foreach my $key (sort {$a <=> $b} keys %ccdhost){
183 if($ccdhost{$key}[1] eq $val){
184 return $ccdhost{$key}[$field];
185 }
186 }
187 }
188 sub get_ovpn_net_ip
189 {
190
191 my $val=shift;
192 my $field=shift;
193 foreach my $key (sort {$a <=> $b} keys %ccdnet){
194 if($ccdnet{$key}[0] eq $val){
195 return $ccdnet{$key}[$field];
196 }
197 }
198 }
199 sub get_grp_ip
200 {
201 my $val=shift;
202 my $src=shift;
203 foreach my $key (sort {$a <=> $b} keys %customgrp){
204 if ($customgrp{$key}[0] eq $val){
205 &get_address($customgrp{$key}[3],$src);
206 }
207 }
208
209 }
210 sub get_std_net_ip
211 {
212 my $val=shift;
213 my $con=shift;
214 if ($val eq 'ALL'){
215 return "0.0.0.0/0.0.0.0";
216 }elsif($val eq 'GREEN'){
217 return "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
218 }elsif($val eq 'ORANGE'){
219 return "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
220 }elsif($val eq 'BLUE'){
221 return "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
222 }elsif($val eq 'RED'){
223 return "0.0.0.0/0";
224 }elsif($val =~ /OpenVPN/i){
225 return "$ovpnsettings{'DOVPN_SUBNET'}";
226 }elsif($val =~ /IPsec/i){
227 return "$ipsecsettings{'RW_NET'}";
228 }elsif($val eq 'IPFire'){
229 return ;
230 }
231 }
232 sub get_interface
233 {
234 my $net=shift;
235 if($net eq "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"){
236 return "$netsettings{'GREEN_DEV'}";
237 }
238 if($net eq "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"){
239 return "$netsettings{'ORANGE_DEV'}";
240 }
241 if($net eq "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"){
242 return "$netsettings{'BLUE_DEV'}";
243 }
244 if($net eq "0.0.0.0/0") {
245 return &get_external_interface();
246 }
247 return "";
248 }
249 sub get_net_ip
250 {
251 my $val=shift;
252 foreach my $key (sort {$a <=> $b} keys %customnetwork){
253 if($customnetwork{$key}[0] eq $val){
254 return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
255 }
256 }
257 }
258 sub get_host_ip
259 {
260 my $val=shift;
261 my $src=shift;
262 foreach my $key (sort {$a <=> $b} keys %customhost){
263 if($customhost{$key}[0] eq $val){
264 if ($customhost{$key}[1] eq 'mac' && $src eq 'src'){
265 return "-m mac --mac-source $customhost{$key}[2]";
266 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'src'){
267 return "$customhost{$key}[2]";
268 }elsif($customhost{$key}[1] eq 'ip' && $src eq 'tgt'){
269 return "$customhost{$key}[2]";
270 }elsif($customhost{$key}[1] eq 'mac' && $src eq 'tgt'){
271 return "none";
272 }
273 }
274 }
275 }
276 sub get_addresses
277 {
278 my $hash = shift;
279 my $key = shift;
280 my $type = shift;
281
282 my @addresses = ();
283 my $addr_type;
284 my $value;
285 my $group_name;
286
287 if ($type eq "src") {
288 $addr_type = $$hash{$key}[3];
289 $value = $$hash{$key}[4];
290
291 } elsif ($type eq "tgt") {
292 $addr_type = $$hash{$key}[5];
293 $value = $$hash{$key}[6];
294 }
295
296 if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) {
297 foreach my $grp (sort {$a <=> $b} keys %customgrp) {
298 if ($customgrp{$grp}[0] eq $value) {
299 my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type);
300
301 if (@address) {
302 push(@addresses, @address);
303 }
304 }
305 }
306 }elsif ($addr_type ~~ ["cust_geoip_src", "cust_geoip_tgt"] && $value =~ "group:") {
307 $value=substr($value,6);
308 foreach my $grp (sort {$a <=> $b} keys %customgeoipgrp) {
309 if ($customgeoipgrp{$grp}[0] eq $value) {
310 my @address = &get_address($addr_type, $customgeoipgrp{$grp}[2], $type);
311
312 if (@address) {
313 push(@addresses, @address);
314 }
315 }
316 }
317 } else {
318 my @address = &get_address($addr_type, $value, $type);
319
320 if (@address) {
321 push(@addresses, @address);
322 }
323 }
324
325 return @addresses;
326 }
327 sub get_address
328 {
329 my $key = shift;
330 my $value = shift;
331 my $type = shift;
332
333 my @ret = ();
334
335 # If the user manually typed an address, we just check if it is a MAC
336 # address. Otherwise, we assume that it is an IP address.
337 if ($key ~~ ["src_addr", "tgt_addr"]) {
338 if (&General::validmac($value)) {
339 push(@ret, ["-m mac --mac-source $value", ""]);
340 } else {
341 push(@ret, [$value, ""]);
342 }
343
344 # If a default network interface (GREEN, BLUE, etc.) is selected, we
345 # try to get the corresponding address of the network.
346 } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) {
347 my $external_interface = &get_external_interface();
348
349 my $network_address = &get_std_net_ip($value, $external_interface);
350
351 if ($network_address) {
352 my $interface = &get_interface($network_address);
353 push(@ret, [$network_address, $interface]);
354 }
355
356 # Custom networks.
357 } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
358 my $network_address = &get_net_ip($value);
359 if ($network_address) {
360 push(@ret, [$network_address, ""]);
361 }
362
363 # Custom hosts.
364 } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
365 my $host_address = &get_host_ip($value, $type);
366 if ($host_address) {
367 push(@ret, [$host_address, ""]);
368 }
369
370 # OpenVPN networks.
371 } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) {
372 my $network_address = &get_ovpn_net_ip($value, 1);
373 if ($network_address) {
374 push(@ret, [$network_address, ""]);
375 }
376
377 # OpenVPN hosts.
378 } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
379 my $host_address = &get_ovpn_host_ip($value, 33);
380 if ($host_address) {
381 push(@ret, [$host_address, ""]);
382 }
383
384 # OpenVPN N2N.
385 } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) {
386 my $network_address = &get_ovpn_n2n_ip($value, 11);
387 if ($network_address) {
388 push(@ret, [$network_address, ""]);
389 }
390
391 # IPsec networks.
392 } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
393 my $network_address = &get_ipsec_net_ip($value, 11);
394 my @nets = split(/\|/, $network_address);
395 foreach my $net (@nets) {
396 push(@ret, [$net, ""]);
397 }
398
399 # The firewall's own IP addresses.
400 } elsif ($key ~~ ["ipfire", "ipfire_src"]) {
401 # ALL
402 if ($value eq "ALL") {
403 push(@ret, ["0/0", ""]);
404
405 # GREEN
406 } elsif ($value eq "GREEN") {
407 push(@ret, [$netsettings{"GREEN_ADDRESS"}, ""]);
408
409 # BLUE
410 } elsif ($value eq "BLUE") {
411 push(@ret, [$netsettings{"BLUE_ADDRESS"}, ""]);
412
413 # ORANGE
414 } elsif ($value eq "ORANGE") {
415 push(@ret, [$netsettings{"ORANGE_ADDRESS"}, ""]);
416
417 # RED
418 } elsif ($value ~~ ["RED", "RED1"]) {
419 my $address = &get_external_address();
420 if ($address) {
421 push(@ret, [$address, ""]);
422 }
423
424 # Aliases
425 } else {
426 my $alias = &get_alias($value);
427 if ($alias) {
428 push(@ret, [$alias, ""]);
429 }
430 }
431
432 # Handle rule options with GeoIP as source.
433 } elsif ($key eq "cust_geoip_src") {
434 # Get external interface.
435 my $external_interface = &get_external_interface();
436
437 push(@ret, ["-m geoip --src-cc $value", "$external_interface"]);
438
439 # Handle rule options with GeoIP as target.
440 } elsif ($key eq "cust_geoip_tgt") {
441 # Get external interface.
442 my $external_interface = &get_external_interface();
443
444 push(@ret, ["-m geoip --dst-cc $value", "$external_interface"]);
445
446 # If nothing was selected, we assume "any".
447 } else {
448 push(@ret, ["0/0", ""]);
449 }
450
451 return @ret;
452 }
453 sub get_external_interface()
454 {
455 open(IFACE, "/var/ipfire/red/iface") or return "";
456 my $iface = <IFACE>;
457 close(IFACE);
458
459 return $iface;
460 }
461 sub get_external_address()
462 {
463 open(ADDR, "/var/ipfire/red/local-ipaddress") or return "";
464 my $address = <ADDR>;
465 close(ADDR);
466
467 return $address;
468 }
469 sub get_alias
470 {
471 my $id = shift;
472
473 foreach my $alias (sort keys %aliases) {
474 if ($id eq $alias) {
475 return $aliases{$alias}{"IPT"};
476 }
477 }
478 }
479
480 sub get_nat_address {
481 my $zone = shift;
482 my $source = shift;
483
484 # Any static address of any zone.
485 if ($zone eq "AUTO") {
486 if ($source && ($source !~ m/mac/i )) {
487 my $firewall_ip = &get_internal_firewall_ip_address($source, 1);
488 if ($firewall_ip) {
489 return $firewall_ip;
490 }
491
492 $firewall_ip = &get_matching_firewall_address($source, 1);
493 if ($firewall_ip) {
494 return $firewall_ip;
495 }
496 }
497
498 return &get_external_address();
499
500 } elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
501 return $netsettings{$zone . "_ADDRESS"};
502
503 } elsif ($zone ~~ ["Default IP", "ALL"]) {
504 return &get_external_address();
505
506 } else {
507 my $alias = &get_alias($zone);
508 unless ($alias) {
509 $alias = &get_external_address();
510 }
511 return $alias;
512 }
513
514 print_error("Could not find NAT address");
515 }
516
517 sub get_internal_firewall_ip_addresses
518 {
519 my $use_orange = shift;
520
521 my @zones = ("GREEN", "BLUE");
522 if ($use_orange) {
523 push(@zones, "ORANGE");
524 }
525
526 my @addresses = ();
527 for my $zone (@zones) {
528 next unless (exists $netsettings{$zone . "_ADDRESS"});
529
530 my $zone_address = $netsettings{$zone . "_ADDRESS"};
531 push(@addresses, $zone_address);
532 }
533
534 return @addresses;
535 }
536 sub get_matching_firewall_address
537 {
538 my $addr = shift;
539 my $use_orange = shift;
540
541 my ($address, $netmask) = split("/", $addr);
542
543 my @zones = ("GREEN", "BLUE");
544 if ($use_orange) {
545 push(@zones, "ORANGE");
546 }
547
548 foreach my $zone (@zones) {
549 next unless (exists $netsettings{$zone . "_ADDRESS"});
550
551 my $zone_subnet = $netsettings{$zone . "_NETADDRESS"};
552 my $zone_mask = $netsettings{$zone . "_NETMASK"};
553
554 if (&General::IpInSubnet($address, $zone_subnet, $zone_mask)) {
555 return $netsettings{$zone . "_ADDRESS"};
556 }
557 }
558
559 return 0;
560 }
561 sub get_internal_firewall_ip_address
562 {
563 my $subnet = shift;
564 my $use_orange = shift;
565
566 my ($net_address, $net_mask) = split("/", $subnet);
567 if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
568 return 0;
569 }
570
571 # Convert net mask into correct format for &General::IpInSubnet().
572 $net_mask = &General::iporsubtodec($net_mask);
573
574 my @addresses = &get_internal_firewall_ip_addresses($use_orange);
575 foreach my $zone_address (@addresses) {
576 if (&General::IpInSubnet($zone_address, $net_address, $net_mask)) {
577 return $zone_address;
578 }
579 }
580
581 return 0;
582 }
583
584 sub get_geoip_locations() {
585 # Path to the directory which contains the binary geoip
586 # databases.
587 my $directory="/usr/share/xt_geoip/LE";
588
589 # Array to store the final country list.
590 my @country_codes = ();
591
592 # Open location and do a directory listing.
593 opendir(DIR, "$directory");
594 my @locations = readdir(DIR);
595 closedir(DIR);
596
597 # Loop through the directory listing, and cut of the file extensions.
598 foreach my $location (sort @locations) {
599 # skip . and ..
600 next if($location =~ /^\.$/);
601 next if($location =~ /^\.\.$/);
602
603 # Remove whitespaces.
604 chomp($location);
605
606 # Cut-off file extension.
607 my ($country_code, $extension) = split(/\./, $location);
608
609 # Add country code to array.
610 push(@country_codes, $country_code);
611 }
612
613 # Return final array.
614 return @country_codes;
615 }
616
617 return 1;