NFT_ALLOWED_HOSTS_CHAIN="allowed_hosts"
NFT_BLLIST_CHAIN="blacklist"
NFT_FPROXY_FILTER="fproxy_filter"
NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN="dnsmasq_timeout_update"
NFT_ACTION_CHAIN="action"
NFT_LOCAL_CLIENTS_CHAIN="local_clients"

if [ "$PROXY_MODE" = "2" ]; then
    MAIN_CHAIN_TYPE="type filter hook prerouting priority ${NFT_PRIO_ROUTE}; policy accept;"
    LOCAL_CLIENTS_CHAIN_TYPE="type route hook output priority ${NFT_PRIO_ROUTE_LOCAL}; policy accept;"
else
    MAIN_CHAIN_TYPE="type nat hook prerouting priority ${NFT_PRIO_NAT}; policy accept;"
    LOCAL_CLIENTS_CHAIN_TYPE="type nat hook output priority ${NFT_PRIO_NAT_LOCAL}; policy accept;"
fi

case "$ALLOWED_HOSTS_MODE" in
    "1")
        NFT_ALLOWED_HOSTS_EXPR="ip saddr @${NFTSET_ALLOWED_HOSTS} jump ${NFT_BLLIST_CHAIN}"
    ;;
    "2")
        NFT_ALLOWED_HOSTS_EXPR="ip saddr != @${NFTSET_ALLOWED_HOSTS} jump ${NFT_BLLIST_CHAIN}"
    ;;
    *)
        NFT_ALLOWED_HOSTS_EXPR="jump ${NFT_BLLIST_CHAIN}"
    ;;
esac

if [ "$NFTSET_DNSMASQ_TIMEOUT_UPDATE" = "1" ]; then
    NFT_DNSMASQ_RULE_TARGET="$NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN"
else
    NFT_DNSMASQ_RULE_TARGET="$NFT_ACTION_CHAIN"
fi

NftCmdWrapper() {
    local _i=0 _attempts=10 _return_code=1
    while [ $_i -lt $_attempts ]
    do
        if $*; then
            _return_code=$?
            break
        fi
        _i=$(($_i + 1))
    done
    return $_return_code
}

NftVpnRouteDelete() {
    $IP_CMD route flush table $VPN_ROUTE_TABLE_ID
    $IP_CMD rule del table $VPN_ROUTE_TABLE_ID
}

NftVpnRouteAdd() {
    local _vpn_ip
    if [ -n "$VPN_GW_IP" ]; then
        _vpn_ip="$VPN_GW_IP"
    else
        _vpn_ip=`$IP_CMD addr list dev $IF_VPN 2> /dev/null | $AWK_CMD '/inet/{f=($3 == "peer") ? 4 : 2; sub("/[0-9]{1,2}$", "", $f); print $f; exit}'`
    fi
    if [ -n "$_vpn_ip" ]; then
        echo 0 > /proc/sys/net/ipv4/conf/$IF_VPN/rp_filter
        NftVpnRouteDelete 2> /dev/null
        $IP_CMD rule add fwmark $VPN_PKTS_MARK table $VPN_ROUTE_TABLE_ID priority $VPN_RULE_PRIO
        $IP_CMD route add default via $_vpn_ip table $VPN_ROUTE_TABLE_ID
    fi
}

NftVpnRouteStatus() {
    [ -n "`$IP_CMD route show table $VPN_ROUTE_TABLE_ID 2> /dev/null`" ] && return 0
    return 1
}

NftMainAdd() {
    local _set
    $NFT_CMD add chain $NFT_TABLE "$NFT_LOCAL_CLIENTS_CHAIN" { $LOCAL_CLIENTS_CHAIN_TYPE }
    $NFT_CMD add chain $NFT_TABLE "$NFT_ACTION_CHAIN"
    $NFT_CMD add chain $NFT_TABLE "$NFT_FPROXY_FILTER"
    $NFT_CMD add chain $NFT_TABLE "$NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN"
    $NFT_CMD add chain $NFT_TABLE "$NFT_BLLIST_CHAIN"
    $NFT_CMD add chain $NFT_TABLE "$NFT_ALLOWED_HOSTS_CHAIN" { $MAIN_CHAIN_TYPE }
    NftCmdWrapper $NFT_CMD add rule $NFT_TABLE $NFT_FPROXY_FILTER ip daddr "@${NFTSET_FPROXY_PRIVATE}" return
    NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_FPROXY_FILTER" jump "$NFT_ACTION_CHAIN"
    NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN" ct state new set update ip daddr "@${NFTSET_DNSMASQ}"
    NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN" jump "$NFT_ACTION_CHAIN"
    NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_ALLOWED_HOSTS_CHAIN" $NFT_ALLOWED_HOSTS_EXPR
    if [ "$PROXY_MODE" = "2" ]; then
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_ACTION_CHAIN" mark set $VPN_PKTS_MARK
    elif [ "$PROXY_MODE" = "3" ]; then
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_ACTION_CHAIN" tcp dport { 0-65535 } redirect to $T_PROXY_PORT_TCP
        if [ "$T_PROXY_ALLOW_UDP" = "1" ]; then
            NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_ACTION_CHAIN" udp dport { 0-65535 } redirect to $T_PROXY_PORT_UDP
        fi
    else
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_ACTION_CHAIN" tcp dport { 0-65535 } redirect to $TOR_TRANS_PORT
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_BLLIST_CHAIN" ip daddr "@${NFTSET_ONION}" counter goto "$NFT_ACTION_CHAIN"
    fi
    if [ "$ENABLE_FPROXY" = "1" ]; then
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_BLLIST_CHAIN" ip saddr "@${NFTSET_FPROXY}" counter goto "$NFT_FPROXY_FILTER"
    fi
    if [ "$BYPASS_MODE" = "1" ]; then
        for _set in "$NFTSET_BYPASS_IP" "$NFTSET_BYPASS_FQDN"
        do
            NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_BLLIST_CHAIN" ip daddr "@${_set}" counter accept
        done
    fi
    for _set in "$NFTSET_CIDR" "$NFTSET_IP"
    do
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_BLLIST_CHAIN" ip daddr "@${_set}" counter goto "$NFT_ACTION_CHAIN"
    done
    NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_BLLIST_CHAIN" ip daddr "@${NFTSET_DNSMASQ}" counter goto "$NFT_DNSMASQ_RULE_TARGET"
    if [ "$PROXY_MODE" = "2" ]; then
        NftVpnRouteAdd
    fi
    if [ "$ENABLE_BLLIST_PROXY" = "1" ]; then
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_LOCAL_CLIENTS_CHAIN" ip daddr "@${NFTSET_BLLIST_PROXY}" counter goto "$NFT_ACTION_CHAIN"
    fi
    if [ "$PROXY_LOCAL_CLIENTS" = "1" ]; then
        NftCmdWrapper $NFT_CMD add rule $NFT_TABLE "$NFT_LOCAL_CLIENTS_CHAIN" jump "$NFT_BLLIST_CHAIN"
    fi
}

NftMainDelete() {
    $NFT_CMD flush chain $NFT_TABLE "$NFT_ALLOWED_HOSTS_CHAIN"
    $NFT_CMD delete chain $NFT_TABLE "$NFT_ALLOWED_HOSTS_CHAIN"
    $NFT_CMD flush chain $NFT_TABLE "$NFT_LOCAL_CLIENTS_CHAIN"
    $NFT_CMD delete chain $NFT_TABLE "$NFT_LOCAL_CLIENTS_CHAIN"
    $NFT_CMD flush chain $NFT_TABLE "$NFT_BLLIST_CHAIN"
    $NFT_CMD delete chain $NFT_TABLE "$NFT_BLLIST_CHAIN"
    $NFT_CMD flush chain $NFT_TABLE "$NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN"
    $NFT_CMD delete chain $NFT_TABLE "$NFT_DNSMASQ_TIMEOUT_UPDATE_CHAIN"
    $NFT_CMD flush chain $NFT_TABLE "$NFT_FPROXY_FILTER"
    $NFT_CMD delete chain $NFT_TABLE "$NFT_FPROXY_FILTER"
    $NFT_CMD flush chain $NFT_TABLE "$NFT_ACTION_CHAIN"
    $NFT_CMD delete chain $NFT_TABLE "$NFT_ACTION_CHAIN"
    NftVpnRouteDelete 2> /dev/null
}

NftListBllistChain() {
    $NFT_CMD -t list chain $NFT_TABLE "$NFT_BLLIST_CHAIN"
}

NftListBllistChainJson() {
    $NFT_CMD -t -j list chain $NFT_TABLE "$NFT_BLLIST_CHAIN"
}

NftReturnStatus() {
    $NFT_CMD -c add rule $NFT_TABLE "$NFT_BLLIST_CHAIN" continue &> /dev/null
    return $?
}
