#!/bin/sh
# Firewall anchor for all environments
# Nokia
# author: Rene Veldsink / Dieter Knueppel
#
# chkconfig: S 29 0
# processname: iptables
#
# Symbols with an _ as prefix are ment to be private for the local file
#
# The idea behind: Currently we have three chains (system, qos, and user),
#                  where packets are passed through this chain list
#

trap '' INT TERM PIPE HUP QUIT
trap 'rm -rf /tmp/iptables_test' EXIT

while ! mkdir /tmp/iptables_test 2> /dev/null
  do
  sleep 1
done

##############
# some helpers
##############

LOG_ERROR="logger -p kern.crit -t iptables"
LOG_INFO="logger -p kern.info -t iptables"


LOG_CRIT=$LOG_ERROR

_CHAIN_USER="user"
_CHAIN_QOS="qos"
_CHAIN_SYSTEM="system"

SAVE_DIR="/tmp/_firewall/"

tryinclude() {
    if [ -e "$1" ]; then
        . $1
        $LOG_INFO ".. included $1"
        return 1
    fi

    if [ -e "$2" ]; then
        . $2
        $LOG_INFO ".. included $2"
        return 1
    fi
    return 0
}


selective_do() {
    if [ "$1" -eq 1 ]; then
        IPTABLES=$IP4TABLES
        IPVERSION=4
        $2_$3
        if [ "${2}" == "start" ]; then
            handle_sshFtp $IPVERSION ${3}
        fi
        if [ "$HAVE_IPV6" = "1" ]; then
            IPTABLES=$IP6TABLES
            IPVERSION=6
            $2_$3
            if [ "${2}" == "start" ]; then
                handle_sshFtp $IPVERSION ${3}
            fi
            IPTABLES=$IP4TABLES
            IPVERSION=4
        fi
    fi
}

###################################
# param #1 start, stop, status
# param #2 system, qos, user or all
#
# special behaviour for system, because if addresses are
# changed, we have to setup all the other stuff again
###################################
selective_execute() {
    case "$2" in
        $_CHAIN_SYSTEM)
            SUFFIX=$_CHAIN_SYSTEM
            selective_do $HAVE_CHAIN_SYSTEM $1 $_CHAIN_SYSTEM
            SUFFIX=$_CHAIN_QOS
            selective_do $HAVE_CHAIN_QOS    $1 $_CHAIN_QOS
            SUFFIX=$_CHAIN_USER
            selective_do $HAVE_CHAIN_USER   $1 $_CHAIN_USER
            ;;
        $_CHAIN_QOS)
            SUFFIX=$2
            selective_do $HAVE_CHAIN_QOS $1 $2
            ;;
        $_CHAIN_USER)
            SUFFIX=$_CHAIN_SYSTEM
            selective_do $HAVE_CHAIN_SYSTEM $1 $_CHAIN_SYSTEM
            ;;
        *)
            SUFFIX=$_CHAIN_SYSTEM
            selective_do $HAVE_CHAIN_SYSTEM $1 $_CHAIN_SYSTEM
            SUFFIX=$_CHAIN_QOS
            selective_do $HAVE_CHAIN_QOS    $1 $_CHAIN_QOS
            SUFFIX=$_CHAIN_USER
            selective_do $HAVE_CHAIN_USER   $1 $_CHAIN_USER
            ;;
    esac
}

###################################
# common exit function
# param #1 cause text
###################################
iptables_exit() {
    $LOG_INFO "iptables - exit with cause ($1)"
    exit
}
work="/tmp/firewall"
mkdir -p $work

apply_rules(){

    FLUSH_ARG=""
    TABLE_ARG=""
    TABLE="filter"
    if [ "x$1" != "x" ]; then
        TABLE_ARG="-T $1"
        TABLE="$1"
    fi
    mkdir -p ${SAVE_DIR}
    if [[ "${IPVERSION}" == "4" ]];then
    FOUR_FAIL=0
    # Merge RM Adapter file to the saved.
    # Merge apply_fwcfg_settings firewall tables
    SAVE_FILE=${work}/iptables_${SUFFIX}.saved
    echo > $SAVE_FILE
    save iptables $SAVE_FILE
    if [[ $? == 0 ]]; then
        /opt/trs/iptables_getCounter BOTH ${TABLE}${IPVERSION}
        iptables-restore $FLUSH_ARG $TABLE_ARG < $SAVE_FILE
        if [[ $? == 0 ]]; then
          $LOG_INFO "IPTABLES restored.... $FLUSH_ARG $TABLE_ARG ${SAVE_DIR}`basename $SAVE_FILE` "
        else
          FOUR_FAIL=1
          $LOG_INFO "IPTABLES restore failed.... $FLUSH_ARG $TABLE_ARG ${SAVE_DIR}`basename $SAVE_FILE` "
        fi
        mv $SAVE_FILE ${SAVE_DIR}/
    else
        FOUR_FAIL=1
    fi
    else
    SIX_FAIL=0
    SAVE_FILE=${work}/ip6tables_${SUFFIX}.saved
    echo > $SAVE_FILE
    save ip6tables $SAVE_FILE
    if [[ $? == 0 ]]; then
        /opt/trs/iptables_getCounter BOTH ${TABLE}${IPVERSION}
        ip6tables-restore $FLUSH_ARG $TABLE_ARG < $SAVE_FILE
        if [[ $? == 0 ]]; then
            $LOG_INFO "IPTABLES restored.... $FLUSH_ARG $TABLE_ARG ${SAVE_DIR}`basename $SAVE_FILE` "
        else
            SIX_FAIL=1
            $LOG_INFO "IPTABLES restore failed....$FLUSH_ARG $TABLE_ARG  ${SAVE_DIR}`basename $SAVE_FILE` "
        fi
        mv $SAVE_FILE ${SAVE_DIR}/
    else
        SIX_FAIL=1
    fi
    fi
    if [[ $FOUR_FAIL -eq 1 || $SIX_FAIL -eq 1 ]]; then
        RET=1
    else

        RET=0
    fi
}
print_rules(){
        for i in ${work}/*; do
            echo $i
            cat $i
        done
}

read_table()
{
    input="$1"
    table="$2"
    echo "#Generating rules from $1 for $2 table"
    while IFS= read -r var
    do
       if [[ "$var" == "*$table"* ]]; then
           flag=1
       elif [[ "$var" == "COMMIT"* ]]; then
           flag=0
       elif [[ "$flag" == "1" ]]; then
          echo "$var"
       fi
    done < "$input"
}


save() {
    local prefix
    local table
    local rulesFound=1
    local SAVE_FILE=$2
    for file in $(ls ${work}/${1}-*-rules 2>/dev/null);do
        # had problems with bash regex crashes script
        table=$(echo $file | sed -e "s@^${work}/${1}-\(.*\)-rules\$@\1@")

        echo "*${table}" >> "$SAVE_FILE"
        prefix="${1}-${table}"
        if [[ -f ${work}/${prefix}-policy ]]; then
            cat ${work}/${prefix}-policy >> "$SAVE_FILE"
            mv ${work}/${prefix}-policy ${SAVE_DIR}/
        fi

        if [[ -f ${work}/${prefix}-userchains ]]; then
            cat ${work}/${prefix}-userchains >> "$SAVE_FILE"
	    mv ${work}/${prefix}-userchains ${SAVE_DIR}/
        fi

        if [[ -f ${work}/${prefix}-rules ]]; then
            cat ${work}/${prefix}-rules >> "$SAVE_FILE"
            mv ${work}/${prefix}-rules ${SAVE_DIR}/
        fi
        if [ $1 == "iptables" ]; then
            #merge all ipv4 files here.
            read_table $IPTABLES_FILE_RMADAPTER $table >> "$SAVE_FILE"
            read_table $IPTABLES_FILE_RS2 $table >> "$SAVE_FILE"
        else
            read_table $IPTABLES_FILE_RMADAPTER_IPV6 $table >> "$SAVE_FILE"
            read_table $IPTABLES_FILE_RS2_IPV6 $table >> "$SAVE_FILE"
        fi
        echo "COMMIT" >> "$SAVE_FILE"
        rulesFound=0
    done
    natFound=`grep -c "\*nat$" ${SAVE_FILE}`
    if [[ "$natFound" == "0" ]]; then
	    #echo "No NAT file generated   ${IPVERSION} ${1} ${SUFFIX} ${work}/${1}-nat-rules"
        echo "*nat" >> "${SAVE_FILE}"
        echo "COMMIT" >> "${SAVE_FILE}"
    fi
    mv ${work}/${1}-* ${SAVE_DIR}
    return $rulesFound
}
iptables() {
  iptablesD "$@"
}

ip6tables() {
  ip6tablesD "$@"
}

iptablesD() {
  iptablesDX iptables "$@"
}

ip6tablesD() {
  iptablesDX ip6tables "$@"
}

iptablesDX() {
   # Intercept -t
   local table="filter"
   # Initialize our own variables:
   local chain=""
   local policy=""
   local target=""
   local F=$1
   shift
   local args=("$@")
   local OPTIND=1         # Reset in case getopts has been used previously in the shell.

# ignore unknown options since those should be directly consumed by iptables
 while getopts "t:N:P:F:X:Z:D:" opt > /dev/null 2>&1 ; do
     case "$opt" in
     t) table=$OPTARG
     args=(${args[@]:0:(($OPTIND-3))} ${args[@]:$(($OPTIND-1))})
      ;;

     N)  chain=$OPTARG
         echo ":${chain} -" >> "${work}/${F}-${table}-userchains"
         return
        ;;
     P)  chain=$OPTARG
         local arg=("$@")
         policy=${arg[(($OPTIND-1))]}
         echo ":${chain} ${policy}" >> ${work}/${F}-${table}-policy
         return
        ;;
     F) return
        ;;
     X) return
        ;;
     Z) return
        ;;
     D)  return
        ;;
    esac
   done

  shift $((OPTIND-1))
   [ "$1" = "--" ] && shift

   #iptables-restore only handle double quotes
   echo "${args[@]}" | tr \' \"  >> ${work}/${F}-${table}-rules

}
# default implementation for chaining
# since currently we use filter and mangle table, a real chaining isn't
# used. so chaining may has to be improved
user_get_chain () {
    return "ACCEPT"
}

qos_get_chain () {
    $_CHAIN_USER_get_chain
    return $?
}

system_get_chain () {
    $_CHAIN_QOS_get_chain
    return $?
}

enable_ssh4 () {
    IPTABLES=$IP4TABLES_CMD
    $IPTABLES  -D INPUT -i eth1+ -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -D ETH_SECURITY_CHAIN -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -D INPUT -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES  -D MP_TRAFFIC -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -D ETH_SECURITY_CHAIN -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES  -A INPUT -i eth1+ -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -A MP_TRAFFIC -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -I ETH_SECURITY_CHAIN 1 -p tcp --dport 22 -j ACCEPT 2>/dev/null
    touch $ENABLE_SSH
    return 0
}
enable_ssh6 () {
    $IP6TABLES_CMD  -D INPUT -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IP6TABLES_CMD  -D MP_TRAFFIC -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IP6TABLES_CMD  -A MP_TRAFFIC -p tcp --dport 22 -j ACCEPT 2>/dev/null
}

disable_ssh4 () {
    IPTABLES=$IP4TABLES_CMD
    $IPTABLES  -D INPUT -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES  -D ETH_SECURITY_CHAIN -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES  -I INPUT 1 -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES  -I ETH_SECURITY_CHAIN 1 -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES  -D INPUT -i eth1+ -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -D ETH_SECURITY_CHAIN -p tcp --dport 22 -j ACCEPT 2>/dev/null
    $IPTABLES  -D MP_TRAFFIC -p tcp --dport 22 -j ACCEPT 2>/dev/null
    if [ -f $ENABLE_SSH ]; then
        rm -f $ENABLE_SSH
    fi
    return 0
}
disable_ssh6 () {
    $IP6TABLES_CMD  -D INPUT -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IP6TABLES_CMD  -I INPUT 1 -p tcp --dport 22 -j DISCARD_CHAIN 2>/dev/null
    $IP6TABLES_CMD  -D MP_TRAFFIC -p tcp --dport 22 -j ACCEPT 2>/dev/null
    return 0
}

enable_ftp () {
    IPTABLES=$IP4TABLES_CMD
    $IPTABLES -D INPUT  -p tcp --dport 21 -j DISCARD_CHAIN 2>/dev/null
    $IPTABLES -D INPUT -p tcp --dport 21 -j ACCEPT 2>/dev/null
    $IPTABLES -I INPUT 1 -p tcp --dport 21 -j ACCEPT 2>/dev/null
    touch $ENABLE_FTP
    return 0
}
handle_sshFtp(){
        if [ "${2}" = "$_CHAIN_SYSTEM" ]; then
            if [ -f $ENABLE_SSH ]; then
                enable_ssh$1
            else
                disable_ssh$1
            fi
            if [ -f $ENABLE_FTP ]; then
                enable_ftp
            fi
        fi
}
executeStop(){
        $IP4TABLES_CMD -F -t mangle && $IP4TABLES_CMD -X -t mangle && $IP6TABLES_CMD -F -t mangle && $IP6TABLES_CMD -X -t mangle
        if [ "$1" != "qos" ]; then
            $IP4TABLES_CMD -P FORWARD ACCEPT && $IP4TABLES_CMD -P INPUT ACCEPT && $IP4TABLES_CMD -F && $IP4TABLES_CMD -X && $IP6TABLES_CMD -P INPUT ACCEPT && $IP6TABLES_CMD -F && $IP6TABLES_CMD -X
        fi
}


###############
# here we start
###############
OPT_DIR="/opt/trs"
CCS_DIR="/opt/CCS"
TRS_LIB_DEST="${OPT_DIR}/lib64"

export LD_LIBRARY_PATH="${TRS_LIB_DEST}:${CCS_DIR}:/usr/lib64:/lib64:/lib:$LD_LIBRARY_PATH"

#
# get the environment we are running in
#
PROD_WCDMA=1
PROD_WIMAX=2
PROD_LTE_DCM=3
PROD_LTE_WMP=4
PROD_MULTIMODE_DCM=7
PROD_MULTIMODE_WMP=8

#${OPT_DIR}/bin/test_envInfo envInfoProductType > /dev/null
#PROD=$?
PROD=$PROD_LTE_WMP # Hardcode to this value for FZM

case "$PROD" in
    $PROD_WIMAX)
        PROD_STR="wimax"
        ;;
    $PROD_WCDMA)
        PROD_STR="wcdma"
        ;;
    $PROD_LTE_DCM)
        PROD_STR="ltd"
        ;;
    $PROD_LTE_WMP)
        PROD_STR="ltwmp"
        ;;
    $PROD_MULTIMODE_DCM)
        PROD_STR="multimode_dcm"
        ;;
    $PROD_MULTIMODE_WMP)
        PROD_STR="ltwmp"
        ;;
    *)
        $LOG_ERROR "unknow product type"
        exit
        ;;
esac

#
#   read the different config files, i.e.
#       - ip adress parameter of interfaces
#       - qos parameter
#       - debug flags
#
_IPTABLES_ADDRESS_CONFIG_FILE=/tmp/iptables_addresses.conf
_IPTABLES_QOS_CONFIG_FILE=/tmp/iptables_qos.conf
_IPTABLES_QOS_DSCP_PCP_CONFIG_FILE=/tmp/iptables_qos_dscp_vpri.conf
_IPTABLES_DEBUG_FILE=/tmp/iptables_debug

$LOG_INFO "iptables - reading configuration files"

tryinclude $_IPTABLES_ADDRESS_CONFIG_FILE $_IPTABLES_ADDRESS_CONFIG_FILE
HAVE_ADDRESS_CONFIG=$?

tryinclude $_IPTABLES_QOS_CONFIG_FILE $_IPTABLES_QOS_CONFIG_FILE
HAVE_QOS_CONFIG=$?

tryinclude $_IPTABLES_DEBUG_FILE $_IPTABLES_DEBUG_FILE
DEBUG_LOGGING=$?

tryinclude $_IPTABLES_QOS_DSCP_PCP_CONFIG_FILE $_IPTABLES_QOS_DSCP_PCP_CONFIG_FILE
HAVE_QOS_CONFIG=$?

# fixvalues
for varname in \
    IP_PRIM_TRS \
    IP_OCT_INTERNAL \
    IP_MPLANE \
    IP_CMP_CA_SERVER \
    PORT_CMP_CA_SERVER \
    IP_CMP_CR_SERVER \
    PORT_CMP_CR_SERVER \
    IP_LDAP_SERVER \
    PORT_LDAP_SERVER \
    IP_NTP_SERVER1 \
    IP_NTP_SERVER2 \
    IP_NTP_SERVER3 \
    IP_NTP_TOD_CLIENT1 \
    IP_SPLANE \
    IP_UPLANE \
    IP_CPLANE \
    IP_TOPMASTER \
    IP_DNS_SERVER1 \
    IP_DNS_SERVER2 \
    PORT_BFD_SESSION1 \
    IP_BFD_SESSION_PEERONE1 \
    IP_BFD_SESSION_PEERTWO1 \
    PORT_BFD_SESSION2 \
    IP_BFD_SESSION_PEERONE2 \
    IP_BFD_SESSION_PEERTWO2 \
    PORT_BFD_SESSION3 \
    IP_BFD_SESSION_PEERONE3 \
    IP_BFD_SESSION_PEERTWO3 \
    PORT_BFD_SESSION4 \
    IP_BFD_SESSION_PEERONE4 \
    IP_BFD_SESSION_PEERTWO4 \
    PORT_BFD_SESSION5 \
    IP_BFD_SESSION_PEERONE5 \
    IP_BFD_SESSION_PEERTWO5 \
    IP_CMP_CRL_SOURCE_IP_ADDR; do
  eval "value=\${$varname}"
  if [ "x$value" = "x0" ]; then
      unset $varname
  fi
done

#
#    here we include the environment specific stuff
#    where we have three chains, i.e.
#        - system  used for system firewall
#        - qos     used for qos behaviour
#        - user    used for user firewall
#
#    if environment specific rules are available, they will be included
#    where:  iptables_system_wcdma is the specific and iptables_system is the common
#
_CHAIN_DIR=/opt/trs

$LOG_INFO "iptables - reading subsystems"

tryinclude "${_CHAIN_DIR}/iptables_${_CHAIN_USER}_${PROD_STR}" "${_CHAIN_DIR}/iptables_${_CHAIN_USER}"
HAVE_CHAIN_USER=$?

tryinclude "${_CHAIN_DIR}/iptables_${_CHAIN_QOS}_${PROD_STR}" "${_CHAIN_DIR}/iptables_${_CHAIN_QOS}"
HAVE_CHAIN_QOS=$?

tryinclude "${_CHAIN_DIR}/iptables_${_CHAIN_SYSTEM}_${PROD_STR}" "${_CHAIN_DIR}/iptables_${_CHAIN_SYSTEM}"
HAVE_CHAIN_SYSTEM=$?

IP4TABLES_CMD="/usr/sbin/iptables"
IP6TABLES_CMD="/usr/sbin/xtables-multi ip6tables"

#redifine iptables to point to the user defined function.
IP4TABLES=iptables
IPTABLES=iptables
IP6TABLES=ip6tables

IPV6_DISABLED=`/bin/cat /proc/sys/net/ipv6/conf/eth0/disable_ipv6`

 HAVE_IPV6=1

ENABLE_SSH=/tmp/ssh_enabled
ENABLE_FTP=/tmp/trs_ftp_flag
MP_TRAFFIC_SHAPER=/opt/trs/mp_traffic_shaper.sh

if [ -e "$2" ]; then
    _SUBSYSTEM="all"
else
    _SUBSYSTEM="$2"
fi

_OPERATION="$3"

case "$1" in
    start)
        $LOG_INFO "iptables - $1 $_SUBSYSTEM"
        selective_execute start $_SUBSYSTEM
        ;;
    restart|reload)
        $LOG_INFO "iptables - $1 $_SUBSYSTEM"
        selective_execute start $_SUBSYSTEM
        `$MP_TRAFFIC_SHAPER`
        ;;
    stop)
        $LOG_INFO "iptables - $1 $_SUBSYSTEM"
        executeStop $_SUBSYSTEM
        ;;
    status)
        $LOG_INFO "iptables - $1 $_SUBSYSTEM"
        selective_execute status $_SUBSYSTEM
        ;;
    ssh_enable)
        /opt/trs/iptables_getCounter BOTH ALL
        enable_ssh4
        enable_ssh6
        ;;
    ssh_disable)
        /opt/trs/iptables_getCounter BOTH ALL
        disable_ssh4
        disable_ssh6
        ;;
    *)
        $LOG_ERROR "Usage: $0 {start|restart|reload|stop|status}"
        $LOG_ERROR "with optional subsystem <system|qos|user>"
        ;;
esac

$LOG_INFO "iptables finished successfully"
