#!/bin/bash
# Copyright: 2023 Virtuozzo International GmbH

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


#nft -j list table inet jelastic | jq ".nftables[] | select (.rule.handle == 7 or .rule.handle == 6 ) "


FW_FILTER_TABLE_NAME='filter'
FW_NAT_TABLE_NAME='nat'

FW_FILTER_INPUT_CHAIN='INPUT'
FW_FILTER_OUTPUT_CHAIN='OUTPUT'
FW_FILTER_FORWARD_CHAIN='FORWARD'

FW_FORWARD_CHECKS_CHAIN='fw_checks'
FW_FORWARD_LANS_CHAIN='fw_lans'
FW_FORWARD_ACCOUNTS_CHAIN='fw_accounts'

FW_NAT_POSTROUTING_CHAIN="POSTROUTING"
FW_NAT_PREROUTING_CHAIN="PREROUTING"

#valid values are: ip,ip6,inet
FW_DEFAULT_TABLE_TYPE='ip'

FW_V4_TABLE="ip"
FW_V6_TABLE="ip6"

#valid values accept,drop
FW_DEFAULT_POLICY='drop'
FW_PLATFORM_DEFINED_FILE="/etc/nftables/platform-defined.nft"
FW_RULESET_FILE="/etc/nftables/main.nft"
FW_USER_DEFINED_FILE="/etc/nftables/user-defined.nft"
FW_CONTAINER_DEFINED_FILE="/etc/nftables/container-defined.nft"

# $1 - type
listTables() {
    nft list tables $1 2>/dev/null | sed -E 's/table $1[[:space:]]+//g'
}

# $1 - type
# $2 - name
removeTable() {
    nft delete table $1 $2 2>/dev/null
}

clearTables() {
    #supported tables ip,ip6,inet
    local families=('ip', 'ip6', 'inet')

    for f in ${families[@]} ; do
        tables=($(listTables $f))
        for t in ${tables[@]} ; do
            removeTable $f $t
        done
    done

}

putShabang() {
    echo '#!/usr/sbin/nft -f'
}


setExists() {
    : ${1:?"Missing param: name"};
    out=$(nft -j list sets inet 2>/dev/null | jq '.nftables[] | select(.set) | select(.set.name|test($var))' --arg var $1)
    [[ -n "$out" ]]
}


getNumberOfActiveSets() {
    local mask="${1:='.*'}"
    local re='^[0-9]+$'
    out=$(nft -j list sets 2>/dev/null | jq 'def count(s): reduce s as $i  (0; .+1); count(.nftables[].set| select(.!=null) | select(.name|test($var)))' --arg var $mask)
    if ! [[ $out =~ $re ]] ; then
        echo "0"
    else
        echo $out
    fi
}

# 1 - type
# 2 - name
tableExists() {
    local ttype="${1:=$FW_DEFAULT_TABLE_TYPE}"
    local tname="${2:=$FW_FILTER_TABLE_NAME}"
    out=$(nft -j list tables 2>/dev/null | jq '.nftables[].table | select(.!=null) | select(.family|test($fam)) | select(.name|test($name))' --arg fam $ttype --arg name $tname)
    [[ -n "$out" ]]
}


# 1 - type
# 2 - table name
# 3 - chain name
chainExists() {
    local ttype="${1:=$FW_DEFAULT_TABLE_TYPE}"
    local tname="${2:=$FW_FILTER_TABLE_NAME}"
    local cname="${3:="FORWARD"}"
    out=$(nft -j list tables 2>/dev/null | jq '.nftables[].chain | select(.!=null) | select(.table|test($tname)) | select (.name|test($cname)) | select(.family|test($ttype))' --arg ttype $ttype --arg tname $tname --arg cname $cname)
    [[ -n "$out" ]]
}


#create required tables with checks
#used for isolation init
#here is only V4 tables because isolation is not support V6
initTablesCheck() {
    #check filter table
    if ! tableExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME ; then
        echo "add table $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME"
    fi

    # check for input
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN  { type filter hook input priority 0 ;   policy $FW_DEFAULT_POLICY ; }"
    fi

    # check for forward
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_FORWARD_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_FORWARD_CHAIN   { type filter hook forward priority 0 ;   policy $FW_DEFAULT_POLICY ; }"
    fi

    # check for output
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_OUTPUT_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_OUTPUT_CHAIN   { type filter hook output priority 0 ;   policy $FW_DEFAULT_POLICY ; }"
    fi

    # check for forward_checks
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FORWARD_CHECKS_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FORWARD_CHECKS_CHAIN"
    fi

    # check for forward_lans
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FORWARD_LANS_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FORWARD_LANS_CHAIN "
    fi

    # check for forward_accounts
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FORWARD_ACCOUNTS_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FORWARD_ACCOUNTS_CHAIN"
    fi


    #----------------------------------------------------------------------------------------------------------------------------
    #check nat table
    if ! tableExists $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME ; then
        echo "add table $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME"
    fi

    # check for prerouting
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN  { type nat hook prerouting priority -100 ; }"
    fi

    # check for postrouting
    if ! chainExists $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN; then
        echo "add chain $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN  { type nat hook postrouting priority 100 ; }"
    fi

}

initTables() {
    #default firewall policy applied only for INPUT chains. FORWARD and OUTPUT always have "accept policy"

    ## IPv4 tables
    echo "add table $FW_V4_TABLE $FW_FILTER_TABLE_NAME"
    echo "add chain $FW_V4_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN  { type filter hook input priority 0 ;   policy $FW_DEFAULT_POLICY ; }"
    echo "add chain $FW_V4_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_FORWARD_CHAIN   { type filter hook forward priority 0 ;   policy accept ; }"
    echo "add chain $FW_V4_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_OUTPUT_CHAIN   { type filter hook output priority 0 ;   policy accept ; }"
    echo "add table $FW_V4_TABLE $FW_NAT_TABLE_NAME"
    echo "add chain $FW_V4_TABLE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN  { type nat hook prerouting priority -100 ; }"
    echo "add chain $FW_V4_TABLE $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN   { type nat hook postrouting priority 100 ; }"

    ## IPv6 tables
    echo "add table $FW_V6_TABLE $FW_FILTER_TABLE_NAME"
    echo "add chain $FW_V6_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN  { type filter hook input priority 0 ;   policy $FW_DEFAULT_POLICY ; }"
    echo "add chain $FW_V6_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_FORWARD_CHAIN   { type filter hook forward priority 0 ;   policy accept ; }"
    echo "add chain $FW_V6_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_OUTPUT_CHAIN   { type filter hook output priority 0 ;   policy accept ; }"
    echo "add table $FW_V6_TABLE $FW_NAT_TABLE_NAME"
    echo "add chain $FW_V6_TABLE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN  { type nat hook prerouting priority -100 ; }"
    echo "add chain $FW_V6_TABLE $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN   { type nat hook postrouting priority 100 ; }"
}

_doInitTables() {
    local rulesFile=$(mktemp)
    putShabang > $rulesFile
    initTablesCheck >> $rulesFile
    nft -f $rulesFile 2>> $JEM_CALLS_LOG
    if [ $? -eq 0 ] ; then
        rm $rulesFile
    fi
}

# $cmd $proto $ptype $dport $srchost $dsthost $chain $tcptype $raction
addRule() {
    protos="${2,,}"
    if [ "x$protos" == "xall" ] ; then
        protos=('tcp' 'udp')
    else
        protos=($protos)
    fi

    local filter_tt=$FW_DEFAULT_TABLE_TYPE

    chain="${7^^}"
    dport="${4,,}"
    raction="${9,,}"
    tcptype="${8^^}"

    if [ "x$tcptype" == "xALL" ] ; then
        ttype=('IPV4' 'IPV6')
    else
        ttype=($tcptype)
    fi

    for tcptype in ${ttype[@]} ; do
        src="${5^^}"
        src=${src/ALL/0.0.0.0\/0}
        dst="${6^^}"
        dst=${dst/ALL/0.0.0.0\/0}

        if [ "x$tcptype" == "xIPV4" ] ; then
            tcptype="ip"
        else
            tcptype="ip6"
            src=${src/0.0.0.0\/0/::\/0}
            dst=${dst/0.0.0.0\/0/::\/0}
            filter_tt="ip6"
        fi

        # command can be add, insert, delete. default add
        cmd="${1,,}"
        if [ "x$cmd" != "x" ] ; then
            case $cmd in
                i)
                    cmd="insert"
                    ;;
                a)
                    cmd="add"
                    ;;
                esac
        else
            cmd="add"
        fi

        #source address
        if [ "x$src" != "x" ] ; then
            if grep -q '!' <<< $src ; then
                src=${src//'!'/}
                src="$tcptype saddr != $src"
            else
                src="$tcptype saddr $src"
            fi
        fi

        #destination address
        if [ "x$dst" != "x" ] ; then
            if grep -q '!' <<< $dst ; then
                dst=${dst//'!'/}
                dst="$tcptype daddr != $dst"
            else
                dst="$tcptype daddr $dst"
            fi
        fi

        for proto in ${protos[@]} ; do
            p=""
            dp=""
            ptype="${3,,}"
            if [ "x$ptype" == "xrange" ] ; then
                #proto should be specified
                if [ "x$proto" == "x" ] ; then
                    return 2
                fi

                if [ "x$dport" != "x" ] ; then
                    #replace : to -
                    dp=${dport/:/-}
                    dp="$proto dport { $dp}"
                fi
            else
                if [ "x$dport" != "x" ] ; then
                    dp="$proto dport $dport"
                else
                    p="meta l4proto $proto"
                fi
            fi

            echo "$cmd rule $filter_tt $FW_FILTER_TABLE_NAME $chain $p $src $dst $dp counter $raction"
        done
    done
}

function netfilterStop() {
    systemctl disable firewalld &>> $JEM_CALLS_LOG
    systemctl enable nftables &>> $JEM_CALLS_LOG
    systemctl start nftables &>> $JEM_CALLS_LOG

    FW_DEFAULT_POLICY='accept'

    [[ -e "/etc/sysconfig/nftables.conf" ]] && sed -i -e 's|#include "/etc/nftables/main.nft"|include "/etc/nftables/main.nft"|' "/etc/sysconfig/nftables.conf"

    echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction flushing ruleset" >>$JEM_CALLS_LOG
    nft flush ruleset 2>>$JEM_CALLS_LOG

    echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction generating default ruleset" >>$JEM_CALLS_LOG
    local rulesFile=$(mktemp)
    putShabang > $rulesFile
    initTables >> $rulesFile
    echo "insert rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ct state related,established counter accept" >> $rulesFile
    nft -f $rulesFile 2>> $JEM_CALLS_LOG
    rm $rulesFile 2>>$JEM_CALLS_LOG

    echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction default ruleset applied" >>$JEM_CALLS_LOG

    if [ -f "$FW_CONTAINER_DEFINED_FILE" ] ; then
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction loading rules from $FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG
        nft -c -f "$FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
        if [[ $? -gt 0 ]] ; then
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction Static netfilter rules not loaded due to errors in $FW_CONTAINER_DEFINED_FILE. See logs for details" >>$JEM_CALLS_LOG
        else
            nft -f "$FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction rules loaded" >>$JEM_CALLS_LOG
        fi
    fi
}

function setDefaultPolicy() {
    #stub
    return 0
}

function netfilterRedirect() {
    __PORT=$1
    __SPORT=$2
    __PROTO=$3

    local rules_file=$(vzctPath $VZ_CTID_ROOT $FW_USER_DEFINED_FILE)

    local portExists=$(vzexecRunStdOut "nft -a list chain $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN | grep -E \"redirect to :$__PORT\" | grep -E \"dport $__SPORT\" | grep \"$__PROTO\"")

    if [ ! -z "$portExists" ] ; then
        writeJSONResponseErr "result=>4099" "message=>This pair SPORT:DPORT already exists"
        return 1
    fi

    rule="insert rule $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN ip protocol $__PROTO $__PROTO dport $__SPORT counter redirect to $__PORT"
    cmd="nft $rule"

    if vzexecRun "$cmd" ; then
        echo "$rule" >> "$rules_file"
        writeJSONResponseOut "result=>0" "message=>Port has been redirected" "sport=>$__SPORT" "dport=>$__PORT"
        return 0
    fi
}

function getUsedPorts() {
    echo $(nft -a list chain $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN 2>/dev/null | grep -E 'dnat' | cut -d' ' -f 3 | awk '!seen[$0]++')
}

function netfilterForward() {
    __IP=$1
    __PORT=$2
    __PROTO=$3
    __SPORT=$4

    rules_file=$FW_USER_DEFINED_FILE
    if [ ! -z $VZ_CTID_ROOT ] ; then
        rules_file=$(vzctPath $VZ_CTID_ROOT $FW_USER_DEFINED_FILE)
    fi


    if ! isValidIP $__IP ; then
        writeJSONResponseErr "result=>4083" "message=>Invalid IP specified"
        exit 1
    fi

    local port=$(nft -a list chain $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN 2>/dev/null | grep -E "$__PROTO dport $__SPORT dnat to $__IP:$__PORT")
    if [[ ! -z "$port" ]] ; then
        writeJSONResponseErr "result=>4092" "message=>This pair IP:PORT already bind to port $port" "port=>$port"
        exit 1
    fi

    local freeport=$(getFreePort $__SPORT)
    if [[ -z "$freeport" ]] ; then
        writeJSONResponseErr "result=>4084" "message=>Unable to bind IP. There is no free ports"
        exit 1
    fi

    if nft add rule $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN ip protocol $__PROTO $__PROTO dport $freeport dnat to $__IP:$__PORT 2>/dev/null ; then
        echo "add rule $FW_NAT_TABLE_NAME $FW_NAT_PREROUTING_CHAIN ip protocol $__PROTO $__PROTO dport $freeport dnat to $__IP:$__PORT" >> "$rules_file"
        writeJSONResponseOut "result=>0" "message=>Port has been added" "port=>$freeport"
        return 0
    fi
}

function netfilterSNAT() {
    __IP=$1
    __NETWORK=$2
    __PROTO=$3

    if ! isValidIP $__IP ; then
                writeJSONResponseErr "result=>4083" "message=>Invalid IP specified" ; exit 1;
    fi

    # Check if binding already exists {{{
    local port=$(nft -a list chain $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN 2>/dev/null | grep -E "ip protocol $__PROTO ip daddr $__NETWORK snat to $__IP")

    if [[ ! -z $port ]] ; then
                writeJSONResponseErr "result=>4092" "message=>This pair IP:NETWORK ($__IP:$__PROTO) already bind"
                exit 1
    fi
    # }}}


    if nft add rule $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN ip protocol $__PROTO ip daddr $__NETWORK snat to $__IP 2>/dev/null ; then
        echo "add rule $FW_NAT_TABLE_NAME $FW_NAT_POSTROUTING_CHAIN ip protocol $__PROTO ip daddr $__NETWORK snat to $__IP" >> "$rules_file"
        writeJSONResponseOut "result=>0" "message=>SNAT has been added"; return 0 ;
    fi
}

function netfilterUnbind() {
    __IP=$1
    __PORT=$2
    __PROTO=$3
}

#filename to save
function saveCurrentRules() {
    savefile="${1:-$FW_RULESET_FILE}"
    nft -s list ruleset > "$savefile" 2>> $JEM_CALLS_LOG
}

function netfilterStart() {
    echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction Starting firewall" >>$JEM_CALLS_LOG

    #making backup of current rules
    [ ! -d "/etc/jelastic" ] && mkdir -p "/etc/jelastic" 2>>$JEM_CALLS_LOG
    echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction backup current rules" >>$JEM_CALLS_LOG
    saveCurrentRules "/etc/jelastic/jelastic.nft"

    #test rule files

    if [ -f "$FW_PLATFORM_DEFINED_FILE" ] ; then
        nft -c -f "$FW_PLATFORM_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
        if [[ $? -gt 0 ]] ; then
            echo "Current iptables rules not modified due to errors in new rule-files" >>$JEM_CALLS_LOG
            return 2
        fi
    fi

    echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction flushing current rules" >>$JEM_CALLS_LOG
    nft flush ruleset 2>> $JEM_CALLS_LOG

    if [ -f "$FW_PLATFORM_DEFINED_FILE" ] ; then
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction loading rules from $FW_PLATFORM_DEFINED_FILE" >>$JEM_CALLS_LOG
        nft -f "$FW_PLATFORM_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
    fi

    #exports
    set -f
    NFSIPS=()
    [ -e '/etc/exports' ] && NFSIPS=($($SED -re 's/.+(\s|\t)+((\*)|([0-9]+\.){3}[0-9]+(\/[0-9]{0,2})?).*/\2/g' -e '/#/d' '/etc/exports' | sort | uniq | $SED -e '/^$/d' -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g'))
    local __allnetwork=0

    # for internal services:
    intips=($(getLANIPs))
    for ip in ${intips[@]} ; do
        for nfsip in ${NFSIPS[@]} ; do
            if [[ "${#NFSPort}" -gt 0 ]] ; then
                if [ "$nfsip" == "*" ] ; then
                    nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol tcp ip daddr $ip tcp dport { $NFSPort} counter accept 2>>$JEM_CALLS_LOG
                    nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol udp ip daddr $ip udp dport { $NFSPort} counter accept 2>>$JEM_CALLS_LOG
                    __allnetwork=1
                else
                    if [[ "$__allnetwork" -eq 0 ]] ; then
                        nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol tcp ip saddr $nfsip ip daddr $ip tcp dport { $NFSPort} counter accept 2>>$JEM_CALLS_LOG
                        nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol udp ip saddr $nfsip ip daddr $ip udp dport { $NFSPort} counter accept 2>>$JEM_CALLS_LOG
                    fi
                fi
            fi
            if [[ "${#RPCPort}" -gt 0 ]] ; then
                if [ "$nfsip" == "*" ] ; then
                    nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol tcp ip daddr $ip tcp dport { $RPCPort} counter accept 2>>$JEM_CALLS_LOG
                    nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol udp ip daddr $ip udp dport { $RPCPort} counter accept 2>>$JEM_CALLS_LOG
                    __allnetwork=1
                else
                    if [[ "$__allnetwork" -eq 0 ]] ; then
                        nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol tcp ip saddr $nfsip ip daddr $ip tcp dport { $RPCPort} counter accept 2>>$JEM_CALLS_LOG
                        nft add rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ip protocol udp ip saddr $nfsip ip daddr $ip udp dport { $RPCPort} counter accept 2>>$JEM_CALLS_LOG
                    fi
                fi
            fi
        done
    done;
    set +f

    #TODO: Remove it
    #tomcat has no this interface anymore
    #nft add rule $FW_DEFAULT_TABLE_TYPE $FW_TABLE_NAME input iifname "tap" accept

    redirPorts=($(isFunction _get$($SED -re 's/-/_/g' <<< ${COMPUTE_TYPE})RedirPorts && { _get$($SED -re 's/-/_/g' <<< ${COMPUTE_TYPE})RedirPorts; } || _defaultComputeNodeRedirPorts));

    # process input chain NAT
    for port in ${redirPorts[@]} ; do
        local ruleParams=($($SED 's/:/ /g' <<< ${port}));
        nft add rule $FW_DEFAULT_TABLE_TYPE $FW_NAT_TABLE_NAME ${ruleParams[0]^^} ip saddr ${ruleParams[1]} ip daddr ${ruleParams[2]} tcp dport ${ruleParams[3]} counter ${ruleParams[5],,} to ${ruleParams[4]} 2>>$JEM_CALLS_LOG
    done;

    if [ -f "$FW_CONTAINER_DEFINED_FILE" ] ; then
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction loading rules from $FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG
        nft -c -f "$FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
        if [[ $? -gt 0 ]] ; then
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction Static netfilter rules not loaded due to errors in $FW_CONTAINER_DEFINED_FILE. See logs for details" >>$JEM_CALLS_LOG
        else
            nft -f "$FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction rules loaded" >>$JEM_CALLS_LOG
        fi
    fi

    if [ -f "$FW_USER_DEFINED_FILE" ] ; then
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction loading rules from $FW_USER_DEFINED_FILE" >>$JEM_CALLS_LOG
        nft -c -f "$FW_USER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
        if [[ $? -gt 0 ]] ; then
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction Custom netfilter rules not loaded due to errors in $FW_USER_DEFINED_FILE. See logs for details" >>$JEM_CALLS_LOG
        else
            nft -f "$FW_USER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction rules loaded" >>$JEM_CALLS_LOG
        fi
    fi

    if [ -f /proc/net/if_inet6 ] ; then
        nft insert rule $FW_V6_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN meta l4proto ipv6-icmp accept 2>>$JEM_CALLS_LOG
        nft insert rule $FW_V6_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN iifname "lo" counter accept 2>>$JEM_CALLS_LOG
        nft insert rule $FW_V6_TABLE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ct state related,established  counter accept 2>>$JEM_CALLS_LOG
    fi

    nft insert rule $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN $FW_DEFAULT_TABLE_TYPE protocol icmp counter accept 2>>$JEM_CALLS_LOG
    nft insert rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN  iifname "lo" counter accept 2>>$JEM_CALLS_LOG
    nft insert rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ct state related,established  counter accept 2>>$JEM_CALLS_LOG

    #JE-67929
    nft insert rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN icmp type timestamp-request counter drop 2>>$JEM_CALLS_LOG


    nft -a list chain ip filter INPUT 2>>/dev/null | grep -q "reject with icmp host-prohibited" || nft add rule ip filter INPUT counter reject with icmp type host-prohibited 2>>$JEM_CALLS_LOG
}

function netfilterEnable() {
    systemctl disable firewalld &>> $JEM_CALLS_LOG
    systemctl enable nftables &>> $JEM_CALLS_LOG
    systemctl start nftables &>> $JEM_CALLS_LOG

    [[ -e "/etc/sysconfig/nftables.conf" ]] && sed -i -e 's|#include "/etc/nftables/main.nft"|include "/etc/nftables/main.nft"|' "/etc/sysconfig/nftables.conf"

    if [ -f "$FW_PLATFORM_DEFINED_FILE" ] ; then
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction loading rules from $FW_PLATFORM_DEFINED_FILE" >>$JEM_CALLS_LOG
        nft -f "$FW_PLATFORM_DEFINED_FILE" &>> $JEM_CALLS_LOG 2>&1
    else
        #init required tables
        FW_DEFAULT_POLICY='accept'

        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction $FW_PLATFORM_DEFINED_FILE not found" >>$JEM_CALLS_LOG
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction generating default ruleset" >>$JEM_CALLS_LOG
        local rulesFile=$(mktemp)
        putShabang > $rulesFile
        initTables >> $rulesFile
        echo "insert rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN ct state related,established counter accept" >> $rulesFile
        nft -f $rulesFile 2>> $JEM_CALLS_LOG
        rm $rulesFile 2>>$JEM_CALLS_LOG
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction default ruleset applied" >>$JEM_CALLS_LOG
    fi

    if [ -f "$FW_CONTAINER_DEFINED_FILE" ] ; then
        echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction loading rules from $FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG
        nft -c -f "$FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
        if [[ $? -gt 0 ]] ; then
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction Static netfilter rules not loaded due to errors in $FW_CONTAINER_DEFINED_FILE. See logs for details" >>$JEM_CALLS_LOG
        else
            nft -f "$FW_CONTAINER_DEFINED_FILE" >>$JEM_CALLS_LOG 2>&1
            echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction rules loaded" >>$JEM_CALLS_LOG
        fi
    fi

    nft -a list chain ip filter INPUT | \
        grep -q 'iifname "lo" counter' || \
        nft insert rule $FW_DEFAULT_TABLE_TYPE $FW_FILTER_TABLE_NAME $FW_FILTER_INPUT_CHAIN  iifname "lo" counter accept 2>>$JEM_CALLS_LOG
}

# global variable RULES required see firewall.module
firewallSet() {
    if [ ${#RULES[@]} -eq 0 ] ; then
        echo "rules are epmty"
        return 1
    fi
    putShabang > "$(vzctPath $VZ_CTID_ROOT $FW_PLATFORM_DEFINED_FILE)"
    initTables >> "$(vzctPath $VZ_CTID_ROOT $FW_PLATFORM_DEFINED_FILE)"
    for ruleraw in ${RULES[@]} ; do
        rulevars=$(awk -F, '{print "proto="$1";ptype="$2";dport="$3";srchost="$4";dsthost="$5";chain="$6";raction="$7";tcptype="$8";cmd="$9";"}' <<< $ruleraw )
        eval $rulevars
        addRule "$cmd" "$proto" "$ptype" "$dport" "$srchost" "$dsthost" "$chain" "$tcptype" "$raction" >> "$(vzctPath $VZ_CTID_ROOT $FW_PLATFORM_DEFINED_FILE)"
        res=$?
        if [ $res -gt 0 ] ; then
            echo "error adding rules"
            return 1
        fi
    done

}

function netfilterList() {
    nft list ruleset $@
}
