#!/usr/bin/perl

BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";

use strict;
use Getopt::Long;

use xCAT::Utils;

Getopt::Long::Configure("bundling");
$Getopt::Long::ignorecase = 0;

my @allowed_ports = ("22", "53", "69", "80", "443", "873", "3001", "3002");

if(scalar(@ARGV) == 0)
{
    &usage();
    exit 0;
}

if (
    !GetOptions(
                'private=s'     => \$::opt_private,
                'public=s'  => \$::opt_public,
                'nat'  => \$::opt_nat,
                'ports=s'  => \$::opt_ports,
    )
  )
{

    &usage();
    exit 1;
}

# Invalid input arguments
if(scalar(@ARGV) > 0)
{
    print "Unknown options @ARGV\n";
    &usage();
    exit 1;
}
# --private and --public must be used together
if(($::opt_private && !$::opt_public) || (!$::opt_private && $::opt_public))
{
    print "Flags --private and --public must be used together\n";
    &usage();
    exit 1;
}

if ($::opt_nat && (!$::opt_private || !$::opt_public))
{
    print "Flags --private and --public must be used with --nat\n";
    &usage();
    exit 1;
}

# Add additional ports into the list
if ($::opt_ports)
{
    my @addports = split /,/, $::opt_ports;
    foreach my $aport (@addports)
    {
        if (!grep (/^$aport$/, @allowed_ports))
        {
            push @allowed_ports, $aport;
        }
    }
}

if (-f "/etc/redhat-release")
{

    #From Redhat7, firewalld is used as the default firewall service, 
    #however, it does not support complex firewall rules. 
    #So we'd better disable firewalld and use iptables service
    #iptables service is shiped in iptables-services package in rhels7 iso,
    #which is not installed by default, it should be installed

    if ( -f "/usr/lib/systemd/system/firewalld.service" ){
          if (system("systemctl is-active firewalld > /dev/null 2>&1") == 0){
             print "firewalld is running, stopping firewalld service \n";
             if(system ("service firewalld stop") ==0 )
                {
                   print "\n[success]\n";
                }
             else
                {
                   print "\n[failed]\n";
                   exit 1
                }
          }

          if (system("systemctl is-enabled firewalld > /dev/null 2>&1") == 0){
             print "disabling firewalld service \n";
             if( system("systemctl disable firewalld") ==0)
               {
                   print "\n[success]\n";
               }
             else
               {
                   print "\n[failed]\n";
                   exit 1
               }
          }
    }

    if ( (! -f "/usr/lib/systemd/system/iptables.service") && (! -f "/etc/init.d/iptables")){
       print "please install iptables-services (for redhat 7) or iptables package first by running:\n";
       print "on redhat7:\tyum install iptables-services\n";
       print "    others:\tyum install iptables\n";
       exit 1
    }

    if($::opt_private && $::opt_public)
    {
        &setup_ip_forwarding();
        my $cmd = &generate_iptables_conf($::opt_private, $::opt_public);
        system($cmd);
    }

    if ($::opt_nat)
    {
        my $cmd = &generate_nat_conf($::opt_private, $::opt_public);
        system($cmd);
    }

    # iptables configuration should be persistent through reboots
    my $cmd = "iptables-save > /etc/sysconfig/iptables";
    system($cmd);

    # restart iptables
    #$cmd = "service iptables restart";
    #system($cmd);
    xCAT::Utils->restartservice("iptables");

    # iptables should be stared on reboot
    #$cmd = "chkconfig iptables on";
    #system($cmd);
    xCAT::Utils->enableservice("iptables");
}
elsif (-f "/etc/SuSE-release")
{
    my $conffile;
    my $conf;
    if($::opt_private && $::opt_public)
    {
        &setup_ip_forwarding();
        $conffile = "/etc/sysconfig/SuSEfirewall2";
        my @privates = split /,/, $::opt_private;
        my $privintf = join(' ', @privates);
        
        # Configuration content
        # if the public and private interface is the same,
        # do not set FW_DEV_EXT
        if ($::opt_public ne $privintf)
        {
            $conf .= "FW_DEV_EXT=\"$::opt_public\"\n";
        }
        $conf .= "FW_DEV_INT=\"$privintf\"\n";
        $conf .= "FW_MASQUERADE=\"yes\"\n";
        $conf .= "FW_PROTECT_FROM_INT=\"no\"\n";
        $conf .= "FW_MASQ_DEV=\"$::opt_public\"\n";
        $conf .= "FW_MASQ_NETS=\"0/0\"\n";

        my $ports = join(' ', @allowed_ports);
        $conf .= "FW_SERVICES_EXT_TCP=\"$ports\"\n";
        $conf .= "FW_SERVICES_EXT_UDP=\"$ports\"\n";
        $conf .= "FW_TRUSTED_NETS=\"127.0.0.1 0/0,icmp\"\n";
    }

    if ($::opt_nat)
    {
        $conf .= "FW_ROUTE=\"yes\"\n";
    }
    open(CONFFILE, ">>$conffile");
    print CONFFILE $conf;
    close(CONFFILE);

    # restart firewall 
    #my $cmd = "service SuSEfirewall2_setup restart";
    #system($cmd);
    xCAT::Utils->restartservice("firewall");

    # SuSEfirewall2_setup should be stared on reboot
    #$cmd = "chkconfig SuSEfirewall2_init on";
    #system($cmd);
    #$cmd = "chkconfig SuSEfirewall2_setup on";
    #system($cmd);
    xCAT::Utils->enableservice("firewall");
}
else
{
    #Ubuntu: FIXME
}

exit 0;

sub usage()
{
        print "Usage:\n    configfirewall --private <private_interfaces> --public <pubic_interface> [--nat] [--ports <ports_allowed>]\n";
}

sub generate_iptables_conf()
{
    my ($privateintf, $publicintf) = @_;

    my $cmd;
    my @privates = split /,/, $privateintf;

    #General setup
    $cmd .= "iptables -A INPUT -i lo -j ACCEPT;";
    $cmd .= "iptables -A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT;";
    $cmd .= "iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT;";

    # Setup for public interface
    foreach my $port (@allowed_ports)
    {
        $cmd .= "iptables -A INPUT -i $publicintf -m state --state NEW -p tcp --dport $port -j ACCEPT;";
        $cmd .= "iptables -A INPUT -i $publicintf -m state --state NEW -p udp --dport $port -j ACCEPT;";
    }
    # Reject settings for public interface 
    $cmd .= "iptables -A INPUT -i $publicintf -j REJECT --reject-with icmp-port-unreachable;";

    # Setup for private interfaces
    foreach my $intf (@privates)
    {
        $cmd .= "iptables -A INPUT -i $intf -j ACCEPT;";
    }
    return $cmd;
}

sub setup_ip_forwarding()
{
    # Enable ip forwarding
    my $conf_file="/etc/sysctl.conf";
    `grep "net.ipv4.ip_forward" $conf_file`;
    if ($? == 0) {
        `sed -i "s/^net.ipv4.ip_forward = .*/net.ipv4.ip_forward = 1/" $conf_file`;
    } else {
        `echo "net.ipv4.ip_forward = 1" >> $conf_file`;
    }
    `sysctl -e -p $conf_file`;
}
sub generate_nat_conf()
{
    my ($privintf, $pubintf) = @_;

    my $cmd;
    my @privates = split /,/, $privintf; 

    # Setup forward for public interface
    $cmd .= "iptables -t nat -A POSTROUTING -o $pubintf -j MASQUERADE;";

    # Setup forward for each private interface
    foreach my $intf (@privates)
    {
        $cmd .= "iptables -A FORWARD -i $intf -j ACCEPT;";
        $cmd .= "iptables -A FORWARD -i $intf -o $pubintf -m state --state RELATED,ESTABLISHED -j ACCEPT;"; 
    }
    return $cmd;
}