曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜. rahbord-ins.ir - GrazzMean-Shell
shell bypass 403

GrazzMean-Shell Shell

Uname: Linux server18.dn-server.com 3.10.0-962.3.2.lve1.5.88.el7.x86_64 #1 SMP Fri Sep 26 14:06:42 UTC 2025 x86_64
Software: LiteSpeed
PHP version: 7.4.33 [ PHP INFO ] PHP os: Linux
Server Ip: 185.126.202.122
Your Ip: 216.73.216.193
User: rahbordf (4876) | Group: rahbordf (4881)
Safe Mode: OFF
Disable Function:
show_source, system, shell_exec, passthru, exec, popen, proc_open

name : nftables.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 Red Hat, Inc.
#
# Authors:
# Eric Garver <e@erig.me>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import os.path
import copy

from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET
from firewall.core.prog import runProg
from firewall.core.logger import log
from firewall.functions import splitArgs, check_mac, portStr, \
                               check_single_address, check_address
from firewall import config
from firewall.errors import FirewallError, UNKNOWN_ERROR, INVALID_RULE, \
                            INVALID_ICMPTYPE, INVALID_TYPE, INVALID_ENTRY
from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark

TABLE_NAME = "firewalld"

# Map iptables (table, chain) to hooks and priorities.
# These are well defined by NF_IP_PRI_* defines in netfilter.
#
# This is analogous to ipXtables.BUILT_IN_CHAINS, but we omit the chains that
# are only used for direct rules.
#
# Note: All hooks use their standard position + NFT_HOOK_OFFSET. This means
# iptables will have DROP precedence. It also means that even if iptables
# ACCEPTs a packet it may still be dropped later by firewalld's rules.
#
NFT_HOOK_OFFSET = 10
IPTABLES_TO_NFT_HOOK = {
    #"security": {
    #    "INPUT": ("input", 50 + NFT_HOOK_OFFSET),
    #    "OUTPUT": ("output", 50 + NFT_HOOK_OFFSET),
    #    "FORWARD": ("forward", 50 + NFT_HOOK_OFFSET),
    #},
    "raw": {
        "PREROUTING": ("prerouting", -300 + NFT_HOOK_OFFSET),
    #    "OUTPUT": ("output", -300 + NFT_HOOK_OFFSET),
    },
    "mangle": {
        "PREROUTING": ("prerouting", -150 + NFT_HOOK_OFFSET),
    #    "POSTROUTING": ("postrouting", -150 + NFT_HOOK_OFFSET),
    #    "INPUT": ("input", -150 + NFT_HOOK_OFFSET),
    #    "OUTPUT": ("output", -150 + NFT_HOOK_OFFSET),
    #    "FORWARD": ("forward", -150 + NFT_HOOK_OFFSET),
    },
    "nat": {
        "PREROUTING": ("prerouting", -100 + NFT_HOOK_OFFSET),
        "POSTROUTING": ("postrouting", 100 + NFT_HOOK_OFFSET),
    #    "INPUT": ("input", 100 + NFT_HOOK_OFFSET),
    #    "OUTPUT": ("output", -100 + NFT_HOOK_OFFSET),
    },
    "filter": {
        "INPUT": ("input", 0 + NFT_HOOK_OFFSET),
        "FORWARD": ("forward", 0 + NFT_HOOK_OFFSET),
    #   "OUTPUT": ("output", 0 + NFT_HOOK_OFFSET),
    },
}

OUR_CHAINS = { # chains created by firewalld
    # family: { chains ...}
    "inet": {},
    "ip": {},
    "ip6": {},
}

# Most ICMP types are provided by nft, but for the codes we have to use numeric
# values.
#
ICMP_TYPES_FRAGMENT = {
    "ipv4" : {
        "communication-prohibited" :    ["icmp", "type", "destination-unreachable", "icmp", "code", "13"],
        "destination-unreachable" :     ["icmp", "type", "destination-unreachable"],
        "echo-reply" :                  ["icmp", "type", "echo-reply"],
        "echo-request" :                ["icmp", "type", "echo-request"],
        "fragmentation-needed" :        ["icmp", "type", "destination-unreachable", "icmp", "code", "4"],
        "host-precedence-violation" :   ["icmp", "type", "destination-unreachable", "icmp", "code", "14"],
        "host-prohibited" :             ["icmp", "type", "destination-unreachable", "icmp", "code", "10"],
        "host-redirect" :               ["icmp", "type", "redirect", "icmp", "code", "1"],
        "host-unknown" :                ["icmp", "type", "destination-unreachable", "icmp", "code", "7"],
        "host-unreachable" :            ["icmp", "type", "destination-unreachable", "icmp", "code", "1"],
        "ip-header-bad" :               ["icmp", "type", "parameter-problem", "icmp", "code", "1"],
        "network-prohibited" :          ["icmp", "type", "destination-unreachable", "icmp", "code", "8"],
        "network-redirect" :            ["icmp", "type", "redirect", "icmp", "code", "0"],
        "network-unknown" :             ["icmp", "type", "destination-unreachable", "icmp", "code", "6"],
        "network-unreachable" :         ["icmp", "type", "destination-unreachable", "icmp", "code", "0"],
        "parameter-problem" :           ["icmp", "type", "parameter-problem"],
        "port-unreachable" :            ["icmp", "type", "destination-unreachable", "icmp", "code", "3"],
        "precedence-cutoff" :           ["icmp", "type", "destination-unreachable", "icmp", "code", "15"],
        "protocol-unreachable" :        ["icmp", "type", "destination-unreachable", "icmp", "code", "2"],
        "redirect" :                    ["icmp", "type", "redirect"],
        "required-option-missing" :     ["icmp", "type", "parameter-problem", "icmp", "code", "1"],
        "router-advertisement" :        ["icmp", "type", "router-advertisement"],
        "router-solicitation" :         ["icmp", "type", "router-solicitation"],
        "source-quench" :               ["icmp", "type", "source-quench"],
        "source-route-failed" :         ["icmp", "type", "destination-unreachable", "icmp", "code", "5"],
        "time-exceeded" :               ["icmp", "type", "time-exceeded"],
        "timestamp-reply" :             ["icmp", "type", "timestamp-reply"],
        "timestamp-request" :           ["icmp", "type", "timestamp-request"],
        "tos-host-redirect" :           ["icmp", "type", "redirect", "icmp", "code", "3"],
        "tos-host-unreachable" :        ["icmp", "type", "destination-unreachable", "icmp", "code", "12"],
        "tos-network-redirect" :        ["icmp", "type", "redirect", "icmp", "code", "2"],
        "tos-network-unreachable" :     ["icmp", "type", "destination-unreachable", "icmp", "code", "11"],
        "ttl-zero-during-reassembly" :  ["icmp", "type", "time-exceeded", "icmp", "code", "1"],
        "ttl-zero-during-transit" :     ["icmp", "type", "time-exceeded", "icmp", "code", "0"],
    },

    "ipv6" : {
        "address-unreachable" :         ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "3"],
        "bad-header" :                  ["icmpv6", "type", "parameter-problem", "icmpv6", "code", "0"],
        "beyond-scope" :                ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "2"],
        "communication-prohibited" :    ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "1"],
        "destination-unreachable" :     ["icmpv6", "type", "destination-unreachable"],
        "echo-reply" :                  ["icmpv6", "type", "echo-reply"],
        "echo-request" :                ["icmpv6", "type", "echo-request"],
        "failed-policy" :               ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "5"],
        "neighbour-advertisement" :     ["icmpv6", "type", "nd-neighbor-advert"],
        "neighbour-solicitation" :      ["icmpv6", "type", "nd-neighbor-solicit"],
        "no-route" :                    ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "0"],
        "packet-too-big" :              ["icmpv6", "type", "packet-too-big"],
        "parameter-problem" :           ["icmpv6", "type", "parameter-problem"],
        "port-unreachable" :            ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "4"],
        "redirect" :                    ["icmpv6", "type", "nd-redirect"],
        "reject-route" :                ["icmpv6", "type", "destination-unreachable", "icmpv6", "code", "6"],
        "router-advertisement" :        ["icmpv6", "type", "nd-router-advert"],
        "router-solicitation" :         ["icmpv6", "type", "nd-router-solicit"],
        "time-exceeded" :               ["icmpv6", "type", "time-exceeded"],
        "ttl-zero-during-reassembly" :  ["icmpv6", "type", "time-exceeded", "icmpv6", "code", "1"],
        "ttl-zero-during-transit" :     ["icmpv6", "type", "time-exceeded", "icmpv6", "code", "0"],
        "unknown-header-type" :         ["icmpv6", "type", "parameter-problem", "icmpv6", "code", "1"],
        "unknown-option" :              ["icmpv6", "type", "parameter-problem", "icmpv6", "code", "2"],
    }
}

class nftables(object):
    name = "nftables"
    zones_supported = True

    def __init__(self, fw):
        self._fw = fw
        self._command = config.COMMANDS["nft"]
        self.fill_exists()
        self.available_tables = []
        self.rule_to_handle = {}
        self.rule_ref_count = {}
        self.zone_source_index_cache = {}

    def fill_exists(self):
        self.command_exists = os.path.exists(self._command)
        self.restore_command_exists = False

    def _run_replace_zone_source(self, rule_add, rule, zone_source_index_cache):
        try:
            i = rule.index("%%ZONE_SOURCE%%")
            rule.pop(i)
            zone = rule.pop(i)
            zone_source = (zone, rule[7]) # (zone, address)
        except ValueError:
            try:
                i = rule.index("%%ZONE_INTERFACE%%")
                rule.pop(i)
                zone_source = None
            except ValueError:
                return

        family = rule[2]

        if zone_source and not rule_add:
            if family in zone_source_index_cache and \
               zone_source in zone_source_index_cache[family]:
                zone_source_index_cache[family].remove(zone_source)
        elif rule_add:
            if family not in zone_source_index_cache:
                zone_source_index_cache[family] = []

            if zone_source:
                # order source based dispatch by zone name
                if zone_source not in zone_source_index_cache[family]:
                    zone_source_index_cache[family].append(zone_source)
                    zone_source_index_cache[family].sort(key=lambda x: x[0])

                index = zone_source_index_cache[family].index(zone_source)
            else:
                if self._fw._allow_zone_drifting:
                    index = 0
                else:
                    index = len(zone_source_index_cache[family])

            if index == 0:
                rule[0] = "insert"
            else:
                index -= 1 # point to the rule before insertion point
                rule[0] = "add"
                rule.insert(i, "index")
                rule.insert(i+1, "%d" % index)

    def __run(self, args):
        nft_opts = ["--echo", "--handle"]
        _args = args[:]

        # If we're deleting a table (i.e. build_flush_rules())
        # then check if its exist first to avoid nft throwing an error
        if _args[0] == "delete" and _args[1] == "table":
            _args_test = _args[:]
            _args_test[0] = "list"
            (status, output) = runProg(self._command, nft_opts + _args_test)
            if status != 0:
                return ""

        rule_key = None
        if _args[0] in ["add", "insert"] and _args[1] == "rule":
            rule_add = True
            rule_key = _args[2:]
            if rule_key[3] == "position":
                # strip "position #"
                # "insert rule family table chain position <num>"
                #              ^^ rule_key starts here
                try:
                    int(rule_key[4])
                except Exception:
                    raise FirewallError(INVALID_RULE, "position without a number")
                else:
                    rule_key.pop(3)
                    rule_key.pop(3)
            rule_key = " ".join(rule_key)
        elif _args[0] in ["delete"] and _args[1] == "rule":
            rule_add = False
            rule_key = _args[2:]
            rule_key = " ".join(rule_key)

        # rule deduplication
        if rule_key in self.rule_ref_count:
            if rule_add:
                self.rule_ref_count[rule_key] += 1
                return ""
            if not rule_add and self.rule_ref_count[rule_key] > 1:
                self.rule_ref_count[rule_key] -= 1
                return ""
            elif self.rule_ref_count[rule_key] == 1:
                self.rule_ref_count[rule_key] -= 1
            else:
                raise FirewallError(UNKNOWN_ERROR, "rule ref count bug: rule_key '%s', cnt %d"
                                                   % (rule_key, self.rule_ref_count[rule_key]))
            log.debug2("%s: rule ref cnt %d, %s %s", self.__class__,
                       self.rule_ref_count[rule_key], self._command, " ".join(_args))

        if rule_key:
            zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
            self._run_replace_zone_source(rule_add, _args, zone_source_index_cache)

        if not rule_key or (not rule_add and self.rule_ref_count[rule_key] == 0) \
                        or (    rule_add and rule_key not in self.rule_ref_count):
            # delete using rule handle
            if rule_key and not rule_add:
                _args = ["delete", "rule"] + _args[2:5] + \
                        ["handle", self.rule_to_handle[rule_key]]
            _args_str = " ".join(_args)
            log.debug2("%s: %s %s", self.__class__, self._command, _args_str)
            (status, output) = runProg(self._command, nft_opts + _args)
            if status != 0:
                raise ValueError("'%s %s' failed: %s" % (self._command,
                                                         _args_str, output))

            if rule_key:
                self.zone_source_index_cache = zone_source_index_cache

            # nft requires deleting rules by handle. So we must cache the rule
            # handle when adding/inserting rules.
            #
            if rule_key:
                if rule_add:
                    str = "# handle "
                    offset = output.index(str) + len(str)
                    self.rule_to_handle[rule_key] = output[offset:].strip()
                    self.rule_ref_count[rule_key] = 1
                else:
                    del self.rule_to_handle[rule_key]
                    del self.rule_ref_count[rule_key]

        return output

    def _rule_replace(self, rule, pattern, replacement):
        try:
            i = rule.index(pattern)
        except ValueError:
            return False
        else:
            rule[i:i+1] = replacement
            return True

    def reverse_rule(self, args):
        ret_args = args[:]
        ret_args[0] = "delete"
        return ret_args

    def set_rules(self, rules, log_denied):
        # We can't support using "nft -f" because we need to retrieve the
        # handles for each rules so we can delete them later on.
        # See also: self.restore_command_exists
        #
        # We can implement this once libnftables in ready.
        #
        raise FirewallError(UNKNOWN_ERROR, "not implemented")

    def set_rule(self, rule, log_denied):
        # replace %%REJECT%%
        #
        # HACK: work around nft bug in which icmpx does not work if the rule
        # has qualified the ip family.
        icmp_keyword = "icmpx"
        if "ipv4" in rule or "ip" in rule or "icmp" in rule:
            icmp_keyword = "icmp"
        elif "ipv6" in rule or "ip6" in rule or "icmpv6" in rule:
            icmp_keyword = "icmpv6"
        self._rule_replace(rule, "%%REJECT%%",
                           ["reject", "with", icmp_keyword, "type", "admin-prohibited"])

        # replace %%ICMP%%
        self._rule_replace(rule, "%%ICMP%%", ["meta", "l4proto", "{icmp, icmpv6}"])

        # replace %%LOGTYPE%%
        try:
            i = rule.index("%%LOGTYPE%%")
        except ValueError:
            pass
        else:
            if log_denied == "off":
                return ""
            if log_denied in ["unicast", "broadcast", "multicast"]:
                rule[i:i+1] = ["pkttype", log_denied]
            else:
                rule.pop(i)

        return self.__run(rule)

    def get_available_tables(self, table=None):
        # Tables always exist in nftables
        return [table] if table else IPTABLES_TO_NFT_HOOK.keys()

    def build_flush_rules(self):
        self.rule_to_handle = {}
        self.rule_ref_count = {}
        self.zone_source_index_cache = {}

        rules = []
        for family in OUR_CHAINS.keys():
            rules.append(["delete", "table", family, "%s" % TABLE_NAME])
        return rules

    def build_set_policy_rules(self, policy):
        # Policy is not exposed to the user. It's only to make sure we DROP
        # packets while initially starting and for panic mode. As such, using
        # hooks with a higher priority than our base chains is sufficient.
        #
        table_name = TABLE_NAME + "_" + "policy_drop"

        rules = []
        if policy == "DROP":
            rules.append(["add", "table", "inet", table_name])

            # To drop everything we need to use the "raw" priority. These occur
            # before conntrack, mangle, nat, etc
            for hook in ["prerouting", "output"]:
                _add_chain = "add chain inet %s %s_%s '{ type filter hook %s priority %d ; policy drop ; }'" % \
                             (table_name, "raw", hook, hook, -300 + NFT_HOOK_OFFSET - 1)
                rules.append(splitArgs(_add_chain))
        elif policy == "ACCEPT":
            rules.append(["delete", "table", "inet", table_name])
        else:
            FirewallError(UNKNOWN_ERROR, "not implemented")

        return rules

    def supported_icmp_types(self):
        # nftables supports any icmp_type via arbitrary type/code matching.
        # We just need a translation for it in ICMP_TYPES_FRAGMENT.
        supported = set()

        for ipv in ICMP_TYPES_FRAGMENT.keys():
            supported.update(ICMP_TYPES_FRAGMENT[ipv].keys())

        return list(supported)

    def build_default_tables(self):
        default_tables = []
        for family in OUR_CHAINS.keys():
            default_tables.append("add table %s %s" % (family, TABLE_NAME))
        return map(splitArgs, default_tables)

    def build_default_rules(self, log_denied="off"):
        default_rules = []
        OUR_CHAINS["inet"]["raw"] = set()
        for chain in IPTABLES_TO_NFT_HOOK["raw"].keys():
            default_rules.append("add chain inet %s raw_%s '{ type filter hook %s priority %d ; }'" %
                                 (TABLE_NAME, chain,
                                  IPTABLES_TO_NFT_HOOK["raw"][chain][0],
                                  IPTABLES_TO_NFT_HOOK["raw"][chain][1]))

            for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]:
                default_rules.append("add chain inet %s raw_%s_%s" % (TABLE_NAME, chain, dispatch_suffix))
                default_rules.append("add rule inet %s raw_%s jump raw_%s_%s" % (TABLE_NAME, chain, chain, dispatch_suffix))
                OUR_CHAINS["inet"]["raw"].update(set(["%s_%s" % (chain, dispatch_suffix)]))

        OUR_CHAINS["inet"]["mangle"] = set()
        for chain in IPTABLES_TO_NFT_HOOK["mangle"].keys():
            default_rules.append("add chain inet %s mangle_%s '{ type filter hook %s priority %d ; }'" %
                                 (TABLE_NAME, chain,
                                  IPTABLES_TO_NFT_HOOK["mangle"][chain][0],
                                  IPTABLES_TO_NFT_HOOK["mangle"][chain][1]))

            for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]:
                default_rules.append("add chain inet %s mangle_%s_%s" % (TABLE_NAME, chain, dispatch_suffix))
                default_rules.append("add rule inet %s mangle_%s jump mangle_%s_%s" % (TABLE_NAME, chain, chain, dispatch_suffix))
                OUR_CHAINS["inet"]["mangle"].update(set(["%s_%s" % (chain, dispatch_suffix)]))

        OUR_CHAINS["ip"]["nat"] = set()
        OUR_CHAINS["ip6"]["nat"] = set()
        for family in ["ip", "ip6"]:
            for chain in IPTABLES_TO_NFT_HOOK["nat"].keys():
                default_rules.append("add chain %s %s nat_%s '{ type nat hook %s priority %d ; }'" %
                                     (family, TABLE_NAME, chain,
                                      IPTABLES_TO_NFT_HOOK["nat"][chain][0],
                                      IPTABLES_TO_NFT_HOOK["nat"][chain][1]))

                for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]:
                    default_rules.append("add chain %s %s nat_%s_%s" % (family, TABLE_NAME, chain, dispatch_suffix))
                    default_rules.append("add rule %s %s nat_%s jump nat_%s_%s" % (family, TABLE_NAME, chain, chain, dispatch_suffix))
                    OUR_CHAINS[family]["nat"].update(set(["%s_%s" % (chain, dispatch_suffix)]))

        OUR_CHAINS["inet"]["filter"] = set()
        for chain in IPTABLES_TO_NFT_HOOK["filter"].keys():
            default_rules.append("add chain inet %s filter_%s '{ type filter hook %s priority %d ; }'" %
                                 (TABLE_NAME, chain,
                                  IPTABLES_TO_NFT_HOOK["filter"][chain][0],
                                  IPTABLES_TO_NFT_HOOK["filter"][chain][1]))

        # filter, INPUT
        default_rules.append("add rule inet %s filter_%s ct state established,related accept" % (TABLE_NAME, "INPUT"))
        default_rules.append("add rule inet %s filter_%s iifname lo accept" % (TABLE_NAME, "INPUT"))
        for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]:
            default_rules.append("add chain inet %s filter_%s_%s" % (TABLE_NAME, "INPUT", dispatch_suffix))
            default_rules.append("add rule inet %s filter_%s jump filter_%s_%s" % (TABLE_NAME, "INPUT", "INPUT", dispatch_suffix))
        if log_denied != "off":
            default_rules.append("add rule inet %s filter_%s ct state invalid %%%%LOGTYPE%%%% log prefix '\"STATE_INVALID_DROP: \"'" % (TABLE_NAME, "INPUT"))
        default_rules.append("add rule inet %s filter_%s ct state invalid drop" % (TABLE_NAME, "INPUT"))
        if log_denied != "off":
            default_rules.append("add rule inet %s filter_%s %%%%LOGTYPE%%%% log prefix '\"FINAL_REJECT: \"'" % (TABLE_NAME, "INPUT"))
        default_rules.append("add rule inet %s filter_%s reject with icmpx type admin-prohibited" % (TABLE_NAME, "INPUT"))

        # filter, FORWARD
        default_rules.append("add chain inet %s filter_%s_IN_ZONES" % (TABLE_NAME, "FORWARD"))
        default_rules.append("add rule inet %s filter_%s ct state established,related accept" % (TABLE_NAME, "FORWARD"))
        default_rules.append("add rule inet %s filter_%s iifname lo accept" % (TABLE_NAME, "FORWARD"))
        for direction in ["IN", "OUT"]:
            for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]:
                default_rules.append("add chain inet %s filter_%s_%s_%s" % (TABLE_NAME, "FORWARD", direction, dispatch_suffix))
                default_rules.append("add rule inet %s filter_%s jump filter_%s_%s_%s" % (TABLE_NAME, "FORWARD", "FORWARD", direction, dispatch_suffix))
        if log_denied != "off":
            default_rules.append("add rule inet %s filter_%s ct state invalid %%%%LOGTYPE%%%% log prefix '\"STATE_INVALID_DROP: \"'" % (TABLE_NAME, "FORWARD"))
        default_rules.append("add rule inet %s filter_%s ct state invalid drop" % (TABLE_NAME, "FORWARD"))
        if log_denied != "off":
            default_rules.append("add rule inet %s filter_%s %%%%LOGTYPE%%%% log prefix '\"FINAL_REJECT: \"'" % (TABLE_NAME, "FORWARD"))
        default_rules.append("add rule inet %s filter_%s reject with icmpx type admin-prohibited" % (TABLE_NAME, "FORWARD"))

        OUR_CHAINS["inet"]["filter"] = set(["INPUT_ZONES_SOURCE",
                                            "INPUT_ZONES",
                                            "FORWARD_IN_ZONES_SOURCE",
                                            "FORWARD_IN_ZONES",
                                            "FORWARD_OUT_ZONES_SOURCE",
                                            "FORWARD_OUT_ZONES"])

        return map(splitArgs, default_rules)

    def get_zone_table_chains(self, table):
        if table == "filter":
            return ["INPUT", "FORWARD_IN", "FORWARD_OUT"]
        if table == "mangle":
            return ["PREROUTING"]
        if table == "nat":
            return ["PREROUTING", "POSTROUTING"]
        if table == "raw":
            return ["PREROUTING"]

        return {}

    def build_zone_source_interface_rules(self, enable, zone, interface,
                                          table, chain, append=False,
                                          family="inet"):
        # nat tables needs to use ip/ip6 family
        if table == "nat" and family == "inet":
            rules = []
            rules.extend(self.build_zone_source_interface_rules(enable, zone,
                            interface, table, chain, append, "ip"))
            rules.extend(self.build_zone_source_interface_rules(enable, zone,
                            interface, table, chain, append, "ip6"))
            return rules

        # handle all zones in the same way here, now
        # trust and block zone targets are handled now in __chain
        opt = {
            "PREROUTING": "iifname",
            "POSTROUTING": "oifname",
            "INPUT": "iifname",
            "FORWARD_IN": "iifname",
            "FORWARD_OUT": "oifname",
            "OUTPUT": "oifname",
        }[chain]

        if interface[len(interface)-1] == "+":
            interface = interface[:len(interface)-1] + "*"

        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
        action = "goto"

        if enable and not append:
            rule = ["insert", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain),
                    "%%ZONE_INTERFACE%%"]
        elif enable:
            rule = ["add", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain)]
        else:
            rule = ["delete", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain)]
            if not append:
                rule += ["%%ZONE_INTERFACE%%"]
        if interface == "*":
            rule += [action, "%s_%s" % (table, target)]
        else:
            rule += [opt, "\"" + interface + "\"", action, "%s_%s" % (table, target)]
        return [rule]

    def build_zone_source_address_rules(self, enable, zone,
                                        address, table, chain, family="inet"):
        # nat tables needs to use ip/ip6 family
        if table == "nat" and family == "inet":
            rules = []
            if address.startswith("ipset:"):
                ipset_family = self._set_get_family(address[len("ipset:"):])
            else:
                ipset_family = None

            if check_address("ipv4", address) or check_mac(address) or ipset_family == "ip":
                rules.extend(self.build_zone_source_address_rules(enable, zone,
                                    address, table, chain, "ip"))
            if check_address("ipv6", address) or check_mac(address) or ipset_family == "ip6":
                rules.extend(self.build_zone_source_address_rules(enable, zone,
                                    address, table, chain, "ip6"))
            return rules

        add_del = { True: "insert", False: "delete" }[enable]

        opt = {
            "PREROUTING": "saddr",
            "POSTROUTING": "daddr",
            "INPUT": "saddr",
            "FORWARD_IN": "saddr",
            "FORWARD_OUT": "daddr",
            "OUTPUT": "daddr",
        }[chain]

        if self._fw._allow_zone_drifting:
            zone_dispatch_chain = "%s_%s_ZONES_SOURCE" % (table, chain)
        else:
            zone_dispatch_chain = "%s_%s_ZONES" % (table, chain)

        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
        action = "goto"

        if address.startswith("ipset:"):
            ipset = address[len("ipset:"):]
            rule_family = self._set_get_family(ipset)
            address = "@" + ipset
        else:
            if check_mac(address):
                # outgoing can not be set
                if opt == "daddr":
                    return ""
                rule_family = "ether"
            elif check_address("ipv4", address):
                rule_family = "ip"
            else:
                rule_family = "ip6"

        rule = [add_del, "rule", family, "%s" % TABLE_NAME,
                zone_dispatch_chain,
                "%%ZONE_SOURCE%%", zone,
                rule_family, opt, address, action, "%s_%s" % (table, target)]
        return [rule]

    def build_zone_chain_rules(self, zone, table, chain, family="inet"):
        # nat tables needs to use ip/ip6 family
        if table == "nat" and family == "inet":
            rules = []
            rules.extend(self.build_zone_chain_rules(zone, table, chain, "ip"))
            rules.extend(self.build_zone_chain_rules(zone, table, chain, "ip6"))
            return rules

        _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)

        OUR_CHAINS[family][table].update(set([_zone,
                                         "%s_log" % _zone,
                                         "%s_deny" % _zone,
                                         "%s_allow" % _zone]))

        rules = []
        rules.append(["add", "chain", family, "%s" % TABLE_NAME,
                      "%s_%s" % (table, _zone)])
        rules.append(["add", "chain", family, "%s" % TABLE_NAME,
                      "%s_%s_log" % (table, _zone)])
        rules.append(["add", "chain", family, "%s" % TABLE_NAME,
                      "%s_%s_deny" % (table, _zone)])
        rules.append(["add", "chain", family, "%s" % TABLE_NAME,
                      "%s_%s_allow" % (table, _zone)])

        rules.append(["add", "rule", family, "%s" % TABLE_NAME,
                      "%s_%s" % (table, _zone),
                      "jump", "%s_%s_log" % (table, _zone)])
        rules.append(["add", "rule", family, "%s" % TABLE_NAME,
                      "%s_%s" % (table, _zone),
                      "jump", "%s_%s_deny" % (table, _zone)])
        rules.append(["add", "rule", family, "%s" % TABLE_NAME,
                      "%s_%s" % (table, _zone),
                      "jump", "%s_%s_allow" % (table, _zone)])

        target = self._fw.zone._zones[zone].target

        if self._fw.get_log_denied() != "off":
            if table == "filter" and \
               chain in ["INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT"]:
                if target in ["REJECT", "%%REJECT%%", "DROP"]:
                    log_suffix = target
                    if target == "%%REJECT%%":
                        log_suffix = "REJECT"
                    rules.append(["add", "rule", family, "%s" % TABLE_NAME,
                                  "%s_%s" % (table, _zone), "%%LOGTYPE%%",
                                  "log", "prefix",
                                  "\"filter_%s_%s: \"" % (_zone, log_suffix)])

        # Handle trust, block and drop zones:
        # Add an additional rule with the zone target (accept, reject
        # or drop) to the base zone only in the filter table.
        # Otherwise it is not be possible to have a zone with drop
        # target, that is allowing traffic that is locally initiated
        # or that adds additional rules. (RHBZ#1055190)
        if table == "filter" and \
           target in ["ACCEPT", "REJECT", "%%REJECT%%", "DROP"] and \
           chain in ["INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT"]:
            rules.append(["add", "rule", family, "%s" % TABLE_NAME,
                          "%s_%s" % (table, _zone),
                          target.lower() if target != "%%REJECT%%" else "%%REJECT%%"])

        return rules

    def _reject_types_fragment(self, reject_type):
        frags = {
            # REJECT_TYPES              : <nft reject rule fragment>
            "icmp-host-prohibited"      : ["with", "icmp",   "type", "host-prohibited"],
            "host-prohib"               : ["with", "icmp",   "type", "host-prohibited"],
            "icmp-net-prohibited"       : ["with", "icmp",   "type", "net-prohibited"],
            "net-prohib"                : ["with", "icmp",   "type", "net-prohibited"],
            "icmp-admin-prohibited"     : ["with", "icmp",   "type", "admin-prohibited"],
            "admin-prohib"              : ["with", "icmp",   "type", "admin-prohibited"],
            "icmp6-adm-prohibited"      : ["with", "icmpv6", "type", "admin-prohibited"],
            "adm-prohibited"            : ["with", "icmpv6", "type", "admin-prohibited"],

            "icmp-net-unreachable"      : ["with", "icmp",   "type", "net-unreachable"],
            "net-unreach"               : ["with", "icmp",   "type", "net-unreachable"],
            "icmp-host-unreachable"     : ["with", "icmp",   "type", "host-unreachable"],
            "host-unreach"              : ["with", "icmp",   "type", "host-unreachable"],
            "icmp-port-unreachable"     : ["with", "icmp",   "type", "port-unreachable"],
            "icmp6-port-unreachable"    : ["with", "icmpv6", "type", "port-unreachable"],
            "port-unreach"              : ["with", "icmpx",  "type", "port-unreachable"],
            "icmp-proto-unreachable"    : ["with", "icmp",   "type", "prot-unreachable"],
            "proto-unreach"             : ["with", "icmp",   "type", "prot-unreachable"],
            "icmp6-addr-unreachable"    : ["with", "icmpv6", "type", "addr-unreachable"],
            "addr-unreach"              : ["with", "icmpv6", "type", "addr-unreachable"],

            "icmp6-no-route"            : ["with", "icmpv6", "type", "no-route"],
            "no-route"                  : ["with", "icmpv6", "type", "no-route"],

            "tcp-reset"                 : ["with", "tcp",    "reset"],
            "tcp-rst"                   : ["with", "tcp",    "reset"],
        }
        return frags[reject_type]

    def _rich_rule_limit_fragment(self, limit):
        if not limit:
            return []

        rich_to_nft = {
            "s" : "second",
            "m" : "minute",
            "h" : "hour",
            "d" : "day",
        }

        try:
            i = limit.value.index("/")
        except ValueError:
            raise FirewallError(INVALID_RULE, "Expected '/' in limit")

        return ["limit", "rate", limit.value[0:i], "/",
                rich_to_nft[limit.value[i+1]]]

    def _rich_rule_log(self, rich_rule, enable, table, target, rule_fragment):
        if not rich_rule.log:
            return []

        add_del = { True: "add", False: "delete" }[enable]

        rule = [add_del, "rule", "inet", "%s" % TABLE_NAME,
                "%s_%s_log" % (table, target)]
        rule += rule_fragment + ["log"]
        if rich_rule.log.prefix:
            rule += ["prefix", "\"%s\"" % rich_rule.log.prefix]
        if rich_rule.log.level:
            rule += ["level", '"%s"' % rich_rule.log.level]
        rule += self._rich_rule_limit_fragment(rich_rule.log.limit)

        return rule

    def _rich_rule_audit(self, rich_rule, enable, table, target, rule_fragment):
        if not rich_rule.audit:
            return []

        add_del = { True: "add", False: "delete" }[enable]

        rule = [add_del, "rule", "inet", "%s" % TABLE_NAME,
                "%s_%s_log" % (table, target)]
        rule += rule_fragment + ["log", "level", "audit"]
        rule += self._rich_rule_limit_fragment(rich_rule.audit.limit)

        return rule

    def _rich_rule_action(self, zone, rich_rule, enable, table, target, rule_fragment):
        if not rich_rule.action:
            return []

        add_del = { True: "add", False: "delete" }[enable]

        if type(rich_rule.action) == Rich_Accept:
            chain = "%s_%s_allow" % (table, target)
            rule_action = ["accept"]
        elif type(rich_rule.action) == Rich_Reject:
            chain = "%s_%s_deny" % (table, target)
            rule_action = ["reject"]
            if rich_rule.action.type:
                rule_action += self._reject_types_fragment(rich_rule.action.type)
        elif type(rich_rule.action) ==  Rich_Drop:
            chain = "%s_%s_deny" % (table, target)
            rule_action = ["drop"]
        elif type(rich_rule.action) == Rich_Mark:
            target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
                                                zone=zone)
            table = "mangle"
            chain = "%s_%s_allow" % (table, target)
            rule_action = ["meta", "mark", "set", rich_rule.action.set]
        else:
            raise FirewallError(INVALID_RULE,
                                "Unknown action %s" % type(rich_rule.action))

        rule = [add_del, "rule", "inet", "%s" % TABLE_NAME, chain]
        rule += rule_fragment
        rule += self._rich_rule_limit_fragment(rich_rule.action.limit)
        rule += rule_action

        return rule

    def _rich_rule_family_fragment(self, rich_family):
        if not rich_family:
            return []
        if rich_family == "ipv4":
            return ["meta", "nfproto", "ipv4"]
        if rich_family == "ipv6":
            return ["meta", "nfproto", "ipv6"]
        raise FirewallError(INVALID_RULE,
                            "Invalid family" % rich_family)

    def _rich_rule_destination_fragment(self, rich_dest):
        if not rich_dest:
            return []

        rule_fragment = []
        if check_address("ipv4", rich_dest.addr):
            rule_fragment += ["ip"]
        else:
            rule_fragment += ["ip6"]

        if rich_dest.invert:
            rule_fragment += ["daddr", "!=", rich_dest.addr]
        else:
            rule_fragment += ["daddr", rich_dest.addr]

        return rule_fragment

    def _rich_rule_source_fragment(self, rich_source):
        if not rich_source:
            return []

        rule_fragment = []
        if rich_source.addr:
            if check_address("ipv4", rich_source.addr):
                rule_fragment += ["ip"]
            else:
                rule_fragment += ["ip6"]

            if rich_source.invert:
                rule_fragment += ["saddr", "!=", rich_source.addr]
            else:
                rule_fragment += ["saddr", rich_source.addr]
        elif hasattr(rich_source, "mac") and rich_source.mac:
            if rich_source.invert:
                rule_fragment += ["ether", "saddr", "!=", rich_source.mac]
            else:
                rule_fragment += ["ether", "saddr", rich_source.mac]
        elif hasattr(rich_source, "ipset") and rich_source.ipset:
            family = self._set_get_family(rich_source.ipset)
            if rich_source.invert:
                rule_fragment += [family, "saddr", "!=", "@" + rich_source.ipset]
            else:
                rule_fragment += [family, "saddr", "@" + rich_source.ipset]

        return rule_fragment

    def build_zone_ports_rules(self, enable, zone, proto, port, destination=None, rich_rule=None):
        add_del = { True: "add", False: "delete" }[enable]
        table = "filter"
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)

        rule_fragment = []
        if rich_rule:
            rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
        if destination:
            if check_address("ipv4", destination):
                rule_fragment += ["ip"]
            else:
                rule_fragment += ["ip6"]
            rule_fragment += ["daddr", destination]
        if rich_rule:
            rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
            rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
        rule_fragment += [proto, "dport", "%s" % portStr(port, "-")]
        if not rich_rule or type(rich_rule.action) != Rich_Mark:
            rule_fragment += ["ct", "state", "new,untracked"]

        rules = []
        if rich_rule:
            rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
            rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
            rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
        else:
            rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                          "%s_%s_allow" % (table, target)] +
                          rule_fragment + ["accept"])

        return rules

    def build_zone_protocol_rules(self, enable, zone, protocol, destination=None, rich_rule=None):
        add_del = { True: "add", False: "delete" }[enable]
        table = "filter"
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)

        rule_fragment = []
        if rich_rule:
            rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
        if destination:
            if check_address("ipv4", destination):
                rule_fragment += ["ip"]
            else:
                rule_fragment += ["ip6"]
            rule_fragment += ["daddr", destination]
        if rich_rule:
            rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
            rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
            rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
        rule_fragment = ["meta", "l4proto", protocol]
        if not rich_rule or type(rich_rule.action) != Rich_Mark:
            rule_fragment += ["ct", "state", "new,untracked"]

        rules = []
        if rich_rule:
            rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
            rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
            rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
        else:
            rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                          "filter_%s_allow" % (target)] +
                          rule_fragment + ["accept"])

        return rules

    def build_zone_source_ports_rules(self, enable, zone, proto, port,
                                      destination=None, rich_rule=None):
        add_del = { True: "add", False: "delete" }[enable]
        table = "filter"
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)

        rule_fragment = []
        if rich_rule:
            rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
        if destination:
            if check_address("ipv4", destination):
                rule_fragment += ["ip"]
            else:
                rule_fragment += ["ip6"]
            rule_fragment += ["daddr", destination]
        if rich_rule:
            rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
            rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
        rule_fragment += [proto, "sport", "%s" % portStr(port, "-")]
        if not rich_rule or type(rich_rule.action) != Rich_Mark:
            rule_fragment += ["ct", "state", "new,untracked"]

        rules = []
        if rich_rule:
            rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
            rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
            rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
        else:
            rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                          "%s_%s_allow" % (table, target)] +
                          rule_fragment + ["accept"])

        return rules

    def build_zone_helper_ports_rules(self, enable, zone, proto, port,
                                      destination, helper_name, module_short_name):
        add_del = { True: "add", False: "delete" }[enable]
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"],
                                            zone=zone)
        rule = [add_del, "rule", "inet", "%s" % TABLE_NAME,
                "filter_%s_allow" % (target)]
        if destination:
            if check_address("ipv4", destination):
                rule += ["ip"]
            else:
                rule += ["ip6"]
            rule += ["daddr", destination]
        rule += [proto, "dport", "%s" % portStr(port, "-")]
        rule += ["ct", "helper", "set", "\"helper-%s-%s\"" % (helper_name, proto)]

        helper_object = ["ct", "helper", "inet", TABLE_NAME,
                         "helper-%s-%s" % (helper_name, proto),
                         "{", "type", "\"%s\"" % (module_short_name), "protocol",
                         proto, ";", "}"]

        return [helper_object, rule]

    def _build_zone_masquerade_nat_rules(self, enable, zone, family, rich_rule=None):
        add_del = { True: "add", False: "delete" }[enable]
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["POSTROUTING"],
                                            zone=zone)

        rule_fragment = []
        if rich_rule:
            rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
            rule_fragment += self._rich_rule_source_fragment(rich_rule.source)

        return [[add_del, "rule", family, "%s" % TABLE_NAME,
                "nat_%s_allow" % (target)]
                + rule_fragment + ["oifname", "!=", "lo", "masquerade"]]

    def build_zone_masquerade_rules(self, enable, zone, rich_rule=None):
        # nat tables needs to use ip/ip6 family
        rules = []
        if rich_rule and (rich_rule.family and rich_rule.family == "ipv6"
           or rich_rule.source and check_address("ipv6", rich_rule.source.addr)):
            rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip6", rich_rule))
        elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4"
           or rich_rule.source and check_address("ipv4", rich_rule.source.addr)):
            rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip", rich_rule))
        else:
            rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip", rich_rule))

        add_del = { True: "add", False: "delete" }[enable]
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["FORWARD_OUT"],
                                            zone=zone)

        rule_fragment = []
        if rich_rule:
            rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
            rule_fragment += self._rich_rule_source_fragment(rich_rule.source)

        rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                      "filter_%s_allow" % (target)]
                      + rule_fragment + ["ct", "state", "new,untracked", "accept"])

        return rules

    def _build_zone_forward_port_nat_rules(self, enable, zone, protocol,
                                           mark_fragment, toaddr, toport,
                                           family):
        add_del = { True: "add", False: "delete" }[enable]
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
                                            zone=zone)

        dnat_fragment = []
        if toaddr:
            dnat_fragment += ["dnat", "to", toaddr]
        else:
            dnat_fragment += ["redirect", "to"]

        if toport and toport != "":
            dnat_fragment += [":%s" % portStr(toport, "-")]

        return [[add_del, "rule", family, "%s" % TABLE_NAME,
                "nat_%s_allow" % (target), "meta", "l4proto", protocol]
                + mark_fragment + dnat_fragment]

    def build_zone_forward_port_rules(self, enable, zone, filter_chain, port,
                                      protocol, toport, toaddr, mark_id, rich_rule=None):
        add_del = { True: "add", False: "delete" }[enable]

        mark_str = "0x%x" % mark_id
        mark_fragment = ["meta", "mark", mark_str]

        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
                                            zone=zone)
        rule_fragment = []
        if rich_rule:
            rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
            rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
            rule_fragment += self._rich_rule_source_fragment(rich_rule.source)

        rules = []
        rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                      "mangle_%s_allow" % (target)]
                      + rule_fragment +
                      [protocol, "dport", port, "meta", "mark", "set", mark_str])

        if rich_rule and (rich_rule.family and rich_rule.family == "ipv6"
           or toaddr and check_single_address("ipv6", toaddr)):
            rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
                                protocol, mark_fragment, toaddr, toport, "ip6"))
        elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4"
           or toaddr and check_single_address("ipv4", toaddr)):
            rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
                                protocol, mark_fragment, toaddr, toport, "ip"))
        else:
            if toaddr and check_single_address("ipv6", toaddr):
                rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
                                    protocol, mark_fragment, toaddr, toport, "ip6"))
            else:
                rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
                                    protocol, mark_fragment, toaddr, toport, "ip"))

        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[filter_chain],
                                            zone=zone)
        rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                      "filter_%s_allow" % (target), "ct", "state", "new,untracked"]
                      + mark_fragment + ["accept"])

        return rules

    def _icmp_types_to_nft_fragment(self, ipv, icmp_type):
        if icmp_type in ICMP_TYPES_FRAGMENT[ipv]:
            return ICMP_TYPES_FRAGMENT[ipv][icmp_type]
        else:
            raise FirewallError(INVALID_ICMPTYPE,
                                "ICMP type '%s' not supported by %s" % (icmp_type, self.name))

    def build_zone_icmp_block_rules(self, enable, zone, ict, rich_rule=None):
        table = "filter"
        add_del = { True: "add", False: "delete" }[enable]

        if rich_rule and rich_rule.ipvs:
            ipvs = rich_rule.ipvs
        elif ict.destination:
            ipvs = []
            if "ipv4" in ict.destination:
                ipvs.append("ipv4")
            if "ipv6" in ict.destination:
                ipvs.append("ipv6")
        else:
            ipvs = ["ipv4", "ipv6"]

        rules = []
        for ipv in ipvs:
            for chain in ["INPUT", "FORWARD_IN"]:
                target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain],
                                                    zone=zone)
                if self._fw.zone.query_icmp_block_inversion(zone):
                    final_chain = "%s_%s_allow" % (table, target)
                    final_target = "accept"
                else:
                    final_chain = "%s_%s_deny" % (table, target)
                    final_target = "%%REJECT%%"

                rule_fragment = []
                if rich_rule:
                    rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
                    rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
                    rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
                rule_fragment += self._icmp_types_to_nft_fragment(ipv, ict.name)

                if rich_rule:
                    rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
                    rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
                    if rich_rule.action:
                        rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
                    else:
                        rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                                      "%s_%s_deny" % (table, target)]
                                      + rule_fragment + ["%%REJECT%%"])
                else:
                    if self._fw.get_log_denied() != "off" and final_target != "accept":
                        rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                                      final_chain] + rule_fragment +
                                     ["%%LOGTYPE%%", "log", "prefix",
                                      "\"%s_%s_ICMP_BLOCK: \"" % (table, zone)])
                    rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
                                  final_chain] + rule_fragment + [final_target])

        return rules

    def build_zone_icmp_block_inversion_rules(self, enable, zone):
        table = "filter"
        rules = []
        for chain in ["INPUT", "FORWARD_IN"]:
            _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain],
                                               zone=zone)
            # HACK: nft position is actually a handle, so we need to lookup the
            # handle of the rule we want to insert this after.
            #
            # This must be kept in sync with build_zone_chain_rules()
            #
            # WARN: This does not work if we haven't executed the transaction
            # yet, because we don't have a handle for our rule_key!! As such,
            # we execute transactions before calling this function.
            #
            rule_key = " ".join(["inet", "%s" % TABLE_NAME,
                                 "%s_%s" % (table, _zone),
                                 "jump", "%s_%s_allow" % (table, _zone)])
            rule_handle = self.rule_to_handle[rule_key]

            if self._fw.zone.query_icmp_block_inversion(zone):
                ibi_target = "%%REJECT%%"
            else:
                ibi_target = "accept"

            if enable:
                # FIXME: can we get rid of position ?
                rule = ["add", "rule", "inet", "%s" % TABLE_NAME,
                        "%s_%s" % (table, _zone), "position", rule_handle]
            else:
                rule = ["delete", "rule", "inet", "%s" % TABLE_NAME,
                        "%s_%s" % (table, _zone)]
            rule += ["%%ICMP%%", ibi_target]
            rules.append(rule)

            if self._fw.zone.query_icmp_block_inversion(zone):
                if self._fw.get_log_denied() != "off":
                    if enable:
                        # FIXME: can we get rid of position ?
                        rule = ["add", "rule", "inet", "%s" % TABLE_NAME,
                                "%s_%s" % (table, _zone), "position", rule_handle]
                    else:
                        rule = ["delete", "rule", "inet", "%s" % TABLE_NAME,
                                "%s_%s" % (table, _zone)]
                    rule += ["%%ICMP%%", "%%LOGTYPE%%", "log", "prefix",
                             "\"%s_%s_ICMP_BLOCK: \"" % (table, _zone)]
                    rules.append(rule)

        return rules

    def build_rpfilter_rules(self, log_denied=False):
        rules = []
        rules.append(["insert", "rule", "inet", "%s" % TABLE_NAME,
                      "raw_%s" % "PREROUTING",
                      "meta", "nfproto", "ipv6", "fib", "saddr", ".", "iif",
                      "oif", "missing", "drop"])
        if log_denied != "off":
            rules.append(["insert", "rule", "inet", "%s" % TABLE_NAME,
                          "raw_%s" % "PREROUTING",
                          "meta", "nfproto", "ipv6", "fib", "saddr", ".", "iif",
                          "oif", "missing", "log", "prefix", "\"rpfilter_DROP: \""])
        rules.append(["insert", "rule", "inet", "%s" % TABLE_NAME,
                      "raw_%s" % "PREROUTING",
                      "icmpv6", "type", "{ nd-router-advert, nd-neighbor-solicit }",
                      "accept"]) # RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17)
        return rules

    def build_zone_rich_source_destination_rules(self, enable, zone, rich_rule):
        table = "filter"
        target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"],
                                            zone=zone)

        rule_fragment = []
        rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
        rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
        rule_fragment += self._rich_rule_source_fragment(rich_rule.source)

        rules = []
        rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
        rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
        rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))

        return rules

    def is_ipv_supported(self, ipv):
        if ipv in ["ipv4", "ipv6", "eb"]:
            return True
        return False

    def _set_type_fragment(self, ipv, type):
        ipv_addr = {
            "ipv4" : "ipv4_addr",
            "ipv6" : "ipv6_addr",
        }
        types = {
            "hash:ip" : [ipv_addr[ipv]],
            "hash:ip,port" : [ipv_addr[ipv], ". inet_proto", ". inet_service"],
            "hash:ip,port,ip" : [ipv_addr[ipv], ". inet_proto", ". inet_service .", ipv_addr[ipv]],
            "hash:ip,port,net" : [ipv_addr[ipv], ". inet_proto", ". inet_service .", ipv_addr[ipv]],
            "hash:ip,mark" : [ipv_addr[ipv], ". mark"],

            "hash:net" : [ipv_addr[ipv]],
            "hash:net,port" : [ipv_addr[ipv], ". inet_proto", ". inet_service"],
            "hash:net,port,ip" : [ipv_addr[ipv], ". inet_proto", ". inet_service .", ipv_addr[ipv]],
            "hash:net,port,net" : [ipv_addr[ipv], ". inet_proto", ". inet_service .", ipv_addr[ipv]],
            "hash:net,iface" : [ipv_addr[ipv], ". ifname"],

            "hash:mac" : ["ether_addr"],
        }
        try:
            return ["type"] + types[type] + [";"]
        except KeyError:
            raise FirewallError(INVALID_TYPE,
                                "ipset type name '%s' is not valid" % type)

    def set_create(self, name, type, options=None):
        if options and "family" in options and options["family"] == "inet6":
            ipv = "ipv6"
        else:
            ipv = "ipv4"

        cmd = [name, "{"]
        cmd += self._set_type_fragment(ipv, type)
        if options:
            if "timeout" in options:
                cmd += ["timeout", options["timeout"]+ "s", ";"]
            if "maxelem" in options:
                cmd += ["size", options["maxelem"], ";"]
        # flag "interval" currently does not work with timeouts or
        # concatenations. See rhbz 1576426, 1576430.
        if (not options or "timeout" not in options) \
           and "," not in type: # e.g. hash:net,port
            cmd += ["flags", "interval", ";"]
        cmd += ["}"]

        for family in ["inet", "ip", "ip6"]:
            self.__run(["add", "set", family, TABLE_NAME] + cmd)

    def set_destroy(self, name):
        for family in ["inet", "ip", "ip6"]:
            self.__run(["delete", "set", family, TABLE_NAME, name])

    def _set_entry_fragment(self, name, entry):
        # convert something like
        #    1.2.3.4,sctp:8080 (type hash:ip,port)
        # to
        #    1.2.3.4 . sctp . 8080
        type_format = self._fw.ipset.get_type(name).split(":")[1].split(",")
        entry_tokens = entry.split(",")
        if len(type_format) != len(entry_tokens):
            raise FirewallError(INVALID_ENTRY,
                                "Number of values does not match ipset type.")
        fragment = []
        for i in range(len(type_format)):
            if type_format[i] == "port":
                try:
                    index = entry_tokens[i].index(":")
                except ValueError:
                    # no protocol means default tcp
                    fragment += ["tcp", ".", entry_tokens[i]]
                else:
                    fragment += [entry_tokens[i][:index], ".", entry_tokens[i][index+1:]]
            else:
                fragment.append(entry_tokens[i])
            fragment.append(".")
        return fragment[:-1] # snip last concat operator

    def set_add(self, name, entry):
        for family in ["inet", "ip", "ip6"]:
            self.__run(["add", "element", family, TABLE_NAME, name, "{"]
                       + self._set_entry_fragment(name, entry) + ["}"])

    def set_delete(self, name, entry):
        for family in ["inet", "ip", "ip6"]:
            self.__run(["delete", "element", family, TABLE_NAME, name, "{"]
                       + self._set_entry_fragment(name, entry) + ["}"])

    def set_flush(self, name):
        for family in ["inet", "ip", "ip6"]:
            self.__run(["flush", "set", family, TABLE_NAME, name])

    def _set_get_family(self, name):
        ipset = self._fw.ipset.get_ipset(name)

        if ipset.type == "hash:mac":
            family = "ether"
        elif ipset.options and "family" in ipset.options \
             and ipset.options["family"] == "inet6":
            family = "ip6"
        else:
            family = "ip"

        return family
© 2026 GrazzMean-Shell