# MARK masks
my $NAT_MASK = 0x0f000000;
+# Country code, which is used to mark hostile networks.
+my $HOSTILE_CCODE = "XD";
+
my %fwdfwsettings=();
my %fwoptions = ();
my %defaultNetworks=();
my %locationsettings = (
"LOCATIONBLOCK_ENABLED" => "off"
);
-my %loaded_ipset_lists=();
+my %ipset_loaded_sets = ();
+my @ipset_used_sets = ();
my $configfwdfw = "${General::swroot}/firewall/config";
my $configinput = "${General::swroot}/firewall/input";
# Get all available locations.
my @locations = &Location::Functions::get_locations();
+# Name or the RED interface.
+my $RED_DEV = &General::get_red_interface();
+
my @log_limit_options = &make_log_limit_options();
my $POLICY_INPUT_ALLOWED = 0;
&main();
sub main {
+ # Get currently used ipset sets.
+ &ipset_get_sets();
+
# Flush all chains.
&flush();
- # Destroy all existing ipsets.
- run("$IPSET destroy");
-
# Prepare firewall rules.
if (! -z "${General::swroot}/firewall/input"){
&buildrules(\%configinputfw);
# Load Location block rules.
&locationblock();
+ # Load rules to block hostile networks.
+ &drop_hostile_networks();
+
# Reload firewall policy.
run("/usr/sbin/firewall-policy");
+ # Cleanup not longer needed ipset sets.
+ &ipset_cleanup();
+
#Reload firewall.local if present
if ( -f '/etc/sysconfig/firewall.local'){
run("/etc/sysconfig/firewall.local reload");
run("$IPTABLES -t nat -F $CHAIN_NAT_SOURCE");
run("$IPTABLES -t nat -F $CHAIN_NAT_DESTINATION");
run("$IPTABLES -t mangle -F $CHAIN_MANGLE_NAT_DESTINATION_FIX");
-
- # Flush LOCATIONBLOCK chain.
- run("$IPTABLES -F LOCATIONBLOCK");
}
sub buildrules {
}
sub locationblock {
- # The LOCATIONBLOCK chain now gets flushed by the flush() function.
+ # Flush LOCATIONBLOCK chain.
+ run("$IPTABLES -F LOCATIONBLOCK");
# If location blocking is not enabled, we are finished here.
if ($locationsettings{'LOCATIONBLOCK_ENABLED'} ne "on") {
&ipset_restore($location);
# Call iptables and create rule to use the loaded ipset list.
- run("$IPTABLES -A LOCATIONBLOCK -m set --match-set CC_$location src -j DROP");
+ run("$IPTABLES -A LOCATIONBLOCK -m set --match-set $location src -j DROP");
}
}
}
+sub drop_hostile_networks () {
+ # Flush the HOSTILE firewall chain.
+ run("$IPTABLES -F HOSTILE");
+
+ # If dropping hostile networks is not enabled, we are finished here.
+ if ($fwoptions{'DROPHOSTILE'} ne "on") {
+ # Exit function.
+ return;
+ }
+
+ # Exit if there is no red interface.
+ return unless($RED_DEV);
+
+ # Call function to load the network list of hostile networks.
+ &ipset_restore($HOSTILE_CCODE);
+
+ # Check traffic in incoming/outgoing direction and drop if it matches
+ run("$IPTABLES -A HOSTILE -i $RED_DEV -m set --match-set $HOSTILE_CCODE src -j HOSTILE_DROP");
+ run("$IPTABLES -A HOSTILE -o $RED_DEV -m set --match-set $HOSTILE_CCODE dst -j HOSTILE_DROP");
+}
+
sub get_protocols {
my $hash = shift;
my $key = shift;
return 0;
}
+sub ipset_get_sets () {
+ # Get all currently used ipset lists and store them in an array.
+ my @output = `$IPSET -n list`;
+
+ # Loop through the temporary array.
+ foreach my $set (@output) {
+ # Remove any newlines.
+ chomp($set);
+
+ # Add the set the array of used sets.
+ push(@ipset_used_sets, $set);
+ }
+
+ # Display used sets in debug mode.
+ if($DEBUG) {
+ print "Used ipset sets:\n";
+ print "@ipset_used_sets\n\n";
+ }
+}
+
sub ipset_restore ($) {
- my ($list) = @_;
+ my ($set) = @_;
- my $file_prefix = "ipset4";
- my $db_file = "$Location::Functions::ipset_db_directory/$list.$file_prefix";
+ # Empty variable to store the db file, which should be
+ # restored by ipset.
+ my $db_file;
- # Check if the network list already has been loaded.
- if($loaded_ipset_lists{$list}) {
+ # Check if the set already has been loaded.
+ if($ipset_loaded_sets{$set}) {
# It already has been loaded - so there is nothing to do.
return;
}
- # Check if the generated file exists.
- if (-f $db_file) {
- # Run ipset and restore the list of the given country code.
- run("$IPSET restore < $db_file");
+ # Check if the given set name is a country code.
+ if($set ~~ @locations) {
+ # Libloc adds the IP type (v4 or v6) as part of the set and file name.
+ my $loc_set = "$set" . "v4";
+
+ # The bare filename equals the set name.
+ my $filename = $loc_set;
+
+ # Libloc uses "ipset" as file extension.
+ my $file_extension = "ipset";
+
+ # Generate full path and filename for the ipset db file.
+ my $db_file = "$Location::Functions::ipset_db_directory/$filename.$file_extension";
+
+ # Call function to restore/load the set.
+ &ipset_call_restore($db_file);
+
+ # Check if the set is already loaded (has been used before).
+ if ($set ~~ @ipset_used_sets) {
+ # The sets contains the IP type (v4 or v6) as part of the name.
+ # The firewall rules matches against sets without that extension. So we safely
+ # can swap or rename the sets to use the new ones.
+ run("$IPSET swap $loc_set $set");
+ } else {
+ # If the set is not loaded, we have to rename it to proper use it.
+ run("$IPSET rename $loc_set $set");
+ }
+ }
+
+ # Store the restored set to the hash to prevent from loading it again.
+ $ipset_loaded_sets{$set} = "1";
+}
+
+sub ipset_call_restore ($) {
+ my ($file) = @_;
- # Store the restored list name to the hash to prevent from loading it again.
- $loaded_ipset_lists{$list} = "1";
+ # Check if the requested file exists.
+ if (-f $file) {
+ # Run ipset and restore the given set.
+ run("$IPSET restore -f $file");
+ }
+}
+
+sub ipset_cleanup () {
+ # Loop through the array of used sets.
+ foreach my $set (@ipset_used_sets) {
+ # Check if this set is still in use.
+ #
+ # In this case an entry in the loaded sets hash exists.
+ unless($ipset_loaded_sets{$set}) {
+ # Entry does not exist, so this set is not longer
+ # used and can be destroyed.
+ run("$IPSET destroy $set");
+ }
}
}