Blob Blame History Raw
From ca0695dd53ad321d89906d190b93a5898cb40220 Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Thu, 10 Jan 2019 10:10:12 -0500
Subject: [PATCH 3/3] RFC3964_IPv4: Use filter table instead of raw

This allows us to use the reject statement, which is more useful to
provide feedback to senders.

Fixes: 5afa02271418 ("nftables: support RFC3964_IPv4 filtering")
Fixes: b86206ed1590 ("ipXtables: support RFC3964_IPv4 filtering")
(cherry picked from commit 44200d0f508a990c5dfff9f480a6206ec507e229)
---
 src/firewall/core/fw.py            |  12 ++-
 src/firewall/core/ipXtables.py     |  24 +++--
 src/firewall/core/nftables.py      |  20 ++--
 src/tests/features/rfc3964_ipv4.at | 159 +++++++++++++++--------------
 4 files changed, 122 insertions(+), 93 deletions(-)

diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index a3089ce70eb8..66f4d9508afa 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -822,9 +822,15 @@ class Firewall(object):
                 rules = ipv6_backend.build_rpfilter_rules(self._log_denied)
                 transaction.add_rules(ipv6_backend, rules)
 
-            if self._rfc3964_ipv4:
-                rules = ipv6_backend.build_rfc3964_ipv4_rules()
-                transaction.add_rules(ipv6_backend, rules)
+        if self._rfc3964_ipv4:
+            # Flush due to iptables-restore (nftables) bug tiggered when
+            # specifying same index multiple times in same batch
+            # rhbz 1647925
+            transaction.execute(True)
+            transaction.clear()
+
+            rules = ipv6_backend.build_rfc3964_ipv4_rules()
+            transaction.add_rules(ipv6_backend, rules)
 
         else:
             if use_transaction is None:
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
index c5b17aa3a846..1355a473f792 100644
--- a/src/firewall/core/ipXtables.py
+++ b/src/firewall/core/ipXtables.py
@@ -1320,13 +1320,23 @@ class ip6tables(ip4tables):
                      "2002:e000::/19", # 224.0.0.0/4 (multicast), 240.0.0.0/4 (reserved and broadcast)
                      ]
 
+        chain_name = "RFC3964_IPv4"
+        self.our_chains["filter"].add(chain_name)
+
         rules = []
+        rules.append(["-t", "filter", "-N", chain_name])
         for daddr in daddr_list:
-            for chain in ["PREROUTING", "OUTPUT"]:
-                rules.append(["-t", "raw", "-I", chain,
-                              "-d", daddr, "-j", "DROP"])
-                if self._fw._log_denied in ["unicast", "all"]:
-                    rules.append(["-t", "raw", "-I", chain,
-                                  "-d", daddr, "-j", "LOG",
-                                  "--log-prefix", "\"RFC3964_IPv4_DROP: \""])
+            rules.append(["-t", "filter", "-I", chain_name,
+                          "-d", daddr, "-j", "REJECT", "--reject-with",
+                          "addr-unreach"])
+            if self._fw._log_denied in ["unicast", "all"]:
+                rules.append(["-t", "filter", "-I", chain_name,
+                              "-d", daddr, "-j", "LOG",
+                              "--log-prefix", "\"RFC3964_IPv4_REJECT: \""])
+
+        # Inject into FORWARD and OUTPUT chains
+        rules.append(["-t", "filter", "-I", "OUTPUT", "3",
+                      "-j", chain_name])
+        rules.append(["-t", "filter", "-I", "FORWARD", "4",
+                      "-j", chain_name])
         return rules
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
index 1eb9c3fb94c2..94d8c2b155dc 100644
--- a/src/firewall/core/nftables.py
+++ b/src/firewall/core/nftables.py
@@ -54,7 +54,7 @@ IPTABLES_TO_NFT_HOOK = {
     #},
     "raw": {
         "PREROUTING": ("prerouting", -300 + NFT_HOOK_OFFSET),
-        "OUTPUT": ("output", -300 + NFT_HOOK_OFFSET),
+    #   "OUTPUT": ("output", -300 + NFT_HOOK_OFFSET),
     },
     "mangle": {
         "PREROUTING": ("prerouting", -150 + NFT_HOOK_OFFSET),
@@ -72,7 +72,7 @@ IPTABLES_TO_NFT_HOOK = {
     "filter": {
         "INPUT": ("input", 0 + NFT_HOOK_OFFSET),
         "FORWARD": ("forward", 0 + NFT_HOOK_OFFSET),
-    #   "OUTPUT": ("output", 0 + NFT_HOOK_OFFSET),
+        "OUTPUT": ("output", 0 + NFT_HOOK_OFFSET),
     },
 }
 
@@ -485,6 +485,9 @@ class nftables(object):
             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"))
 
+        # filter, OUTPUT
+        default_rules.append("add rule inet %s filter_%s oifname lo accept" % (TABLE_NAME, "OUTPUT"))
+
         self.our_chains["inet"]["filter"] = set(["INPUT_ZONES_SOURCE",
                                                  "INPUT_ZONES",
                                                  "FORWARD_IN_ZONES_SOURCE",
@@ -1260,13 +1263,16 @@ class nftables(object):
 
         rule_fragment = ["ip6", "daddr"] + daddr_set
         if self._fw._log_denied in ["unicast", "all"]:
-            rule_fragment += ["log", "prefix", "\"RFC3964_IPv4_DROP: \""]
-        rule_fragment += ["drop"]
+            rule_fragment += ["log", "prefix", "\"RFC3964_IPv4_REJECT: \""]
+        rule_fragment += ["reject"]
+        rule_fragment += self._reject_types_fragment("addr-unreach")
 
         rules = []
-        for chain in ["PREROUTING", "OUTPUT"]:
-            rules.append(["insert", "rule", "inet", "%s" % TABLE_NAME,
-                          "raw_%s" % chain] + rule_fragment)
+        # WARN: index must be kept in sync with build_default_rules()
+        rules.append(["add", "rule", "inet", "%s" % TABLE_NAME,
+                      "filter_OUTPUT", "index", "0"] + rule_fragment)
+        rules.append(["add", "rule", "inet", "%s" % TABLE_NAME,
+                      "filter_FORWARD", "index", "1"] + rule_fragment)
         return rules
 
     def build_zone_rich_source_destination_rules(self, enable, zone, rich_rule):
diff --git a/src/tests/features/rfc3964_ipv4.at b/src/tests/features/rfc3964_ipv4.at
index ea8dd40bb5c3..a93aba192c2c 100644
--- a/src/tests/features/rfc3964_ipv4.at
+++ b/src/tests/features/rfc3964_ipv4.at
@@ -5,74 +5,70 @@ AT_CHECK([sed -i 's/^RFC3964_IPv4.*/RFC3964_IPv4=yes/' ./firewalld.conf])
 FWD_RELOAD
 
 m4_if(nftables, FIREWALL_BACKEND, [
-    NFT_LIST_RULES([inet], [raw_PREROUTING], 0, [dnl
+    NFT_LIST_RULES([inet], [filter_FORWARD], 0, [dnl
         table inet firewalld {
-        chain raw_PREROUTING {
-        ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } log prefix "RFC3964_IPv4_DROP: " drop
-        m4_if(yes, HOST_SUPPORTS_NFT_FIB, [dnl
-            icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
-            meta nfproto ipv6 fib saddr . iif oif missing log prefix "rpfilter_DROP: " drop
-        ])dnl
-        jump raw_PREROUTING_ZONES_SOURCE
-        jump raw_PREROUTING_ZONES
+        chain filter_FORWARD {
+        ct state established,related accept
+        iifname "lo" accept
+        ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } log prefix "RFC3964_IPv4_REJECT: " reject with icmpv6 type addr-unreachable
+        jump filter_FORWARD_IN_ZONES_SOURCE
+        jump filter_FORWARD_IN_ZONES
+        jump filter_FORWARD_OUT_ZONES_SOURCE
+        jump filter_FORWARD_OUT_ZONES
+        ct state invalid log prefix "STATE_INVALID_DROP: "
+        ct state invalid drop
+        log prefix "FINAL_REJECT: "
+        reject with icmpx type admin-prohibited
         }
         }
     ])
-    NFT_LIST_RULES([inet], [raw_OUTPUT], 0, [dnl
+    NFT_LIST_RULES([inet], [filter_OUTPUT], 0, [dnl
         table inet firewalld {
-        chain raw_OUTPUT {
-        ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } log prefix "RFC3964_IPv4_DROP: " drop
+        chain filter_OUTPUT {
+        oifname "lo" accept
+        ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } log prefix "RFC3964_IPv4_REJECT: " reject with icmpv6 type addr-unreachable
         }
         }
     ])
 ], [
-    IP6TABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
-        LOG all ::/0 2002:e000::/19 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:e000::/19
-        LOG all ::/0 2002:a9fe::/32 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:a9fe::/32
-        LOG all ::/0 2002:c0a8::/32 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:c0a8::/32
-        LOG all ::/0 2002:ac10::/28 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:ac10::/28
-        LOG all ::/0 2002:7f00::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:7f00::/24
-        LOG all ::/0 2002:a00::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:a00::/24
-        LOG all ::/0 2002::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002::/24
-        LOG all ::/0 ::ffff:0.0.0.0/96 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 ::ffff:0.0.0.0/96
-        LOG all ::/0 ::/96 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 ::/96
-        ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 134
-        ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 135
-        LOG all ::/0 ::/0 rpfilter invert LOG flags 0 level 4 prefix "rpfilter_DROP: "
-        DROP all ::/0 ::/0 rpfilter invert
-        PREROUTING_direct all ::/0 ::/0
-        PREROUTING_ZONES_SOURCE all ::/0 ::/0
-        PREROUTING_ZONES all ::/0 ::/0
+    IP6TABLES_LIST_RULES([filter], [RFC3964_IPv4], 0, [dnl
+        LOG all ::/0 2002:e000::/19 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002:e000::/19 reject-with icmp6-addr-unreachable
+        LOG all ::/0 2002:a9fe::/32 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002:a9fe::/32 reject-with icmp6-addr-unreachable
+        LOG all ::/0 2002:c0a8::/32 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002:c0a8::/32 reject-with icmp6-addr-unreachable
+        LOG all ::/0 2002:ac10::/28 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002:ac10::/28 reject-with icmp6-addr-unreachable
+        LOG all ::/0 2002:7f00::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002:7f00::/24 reject-with icmp6-addr-unreachable
+        LOG all ::/0 2002:a00::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002:a00::/24 reject-with icmp6-addr-unreachable
+        LOG all ::/0 2002::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 2002::/24 reject-with icmp6-addr-unreachable
+        LOG all ::/0 ::ffff:0.0.0.0/96 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 ::ffff:0.0.0.0/96 reject-with icmp6-addr-unreachable
+        LOG all ::/0 ::/96 LOG flags 0 level 4 prefix "RFC3964_IPv4_REJECT: "
+        REJECT all ::/0 ::/96 reject-with icmp6-addr-unreachable
     ])
-    IP6TABLES_LIST_RULES([raw], [OUTPUT], 0, [dnl
-        LOG all ::/0 2002:e000::/19 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:e000::/19
-        LOG all ::/0 2002:a9fe::/32 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:a9fe::/32
-        LOG all ::/0 2002:c0a8::/32 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:c0a8::/32
-        LOG all ::/0 2002:ac10::/28 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:ac10::/28
-        LOG all ::/0 2002:7f00::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:7f00::/24
-        LOG all ::/0 2002:a00::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002:a00::/24
-        LOG all ::/0 2002::/24 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 2002::/24
-        LOG all ::/0 ::ffff:0.0.0.0/96 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 ::ffff:0.0.0.0/96
-        LOG all ::/0 ::/96 LOG flags 0 level 4 prefix "RFC3964_IPv4_DROP: "
-        DROP all ::/0 ::/96
+    IP6TABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
+        ACCEPT all ::/0 ::/0 ctstate RELATED,ESTABLISHED
+        ACCEPT all ::/0 ::/0
+        FORWARD_direct all ::/0 ::/0
+        RFC3964_IPv4 all ::/0 ::/0
+        FORWARD_IN_ZONES_SOURCE all ::/0 ::/0
+        FORWARD_IN_ZONES all ::/0 ::/0
+        FORWARD_OUT_ZONES_SOURCE all ::/0 ::/0
+        FORWARD_OUT_ZONES all ::/0 ::/0
+        LOG all ::/0 ::/0 ctstate INVALID LOG flags 0 level 4 prefix "STATE_INVALID_DROP: "
+        DROP all ::/0 ::/0 ctstate INVALID
+        LOG all ::/0 ::/0 LOG flags 0 level 4 prefix "FINAL_REJECT: "
+        REJECT all ::/0 ::/0 reject-with icmp6-adm-prohibited
+    ])
+    IP6TABLES_LIST_RULES([filter], [OUTPUT], 0, [dnl
+        ACCEPT all ::/0 ::/0
         OUTPUT_direct all ::/0 ::/0
+        RFC3964_IPv4 all ::/0 ::/0
     ])
 ])
 
@@ -80,35 +76,46 @@ AT_CHECK([sed -i 's/^RFC3964_IPv4.*/RFC3964_IPv4=no/' ./firewalld.conf])
 FWD_RELOAD
 
 m4_if(nftables, FIREWALL_BACKEND, [
-    NFT_LIST_RULES([inet], [raw_PREROUTING], 0, [dnl
+    NFT_LIST_RULES([inet], [filter_FORWARD], 0, [dnl
         table inet firewalld {
-        chain raw_PREROUTING {
-        m4_if(yes, HOST_SUPPORTS_NFT_FIB, [dnl
-            icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
-            meta nfproto ipv6 fib saddr . iif oif missing log prefix "rpfilter_DROP: " drop
-        ])dnl
-        jump raw_PREROUTING_ZONES_SOURCE
-        jump raw_PREROUTING_ZONES
+        chain filter_FORWARD {
+        ct state established,related accept
+        iifname "lo" accept
+        jump filter_FORWARD_IN_ZONES_SOURCE
+        jump filter_FORWARD_IN_ZONES
+        jump filter_FORWARD_OUT_ZONES_SOURCE
+        jump filter_FORWARD_OUT_ZONES
+        ct state invalid log prefix "STATE_INVALID_DROP: "
+        ct state invalid drop
+        log prefix "FINAL_REJECT: "
+        reject with icmpx type admin-prohibited
         }
         }
     ])
-    NFT_LIST_RULES([inet], [raw_OUTPUT], 0, [dnl
+    NFT_LIST_RULES([inet], [filter_OUTPUT], 0, [dnl
         table inet firewalld {
-        chain raw_OUTPUT {
+        chain filter_OUTPUT {
+        oifname "lo" accept
         }
         }
     ])
 ], [
-    IP6TABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
-        ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 134
-        ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 135
-        LOG all ::/0 ::/0 rpfilter invert LOG flags 0 level 4 prefix "rpfilter_DROP: "
-        DROP all ::/0 ::/0 rpfilter invert
-        PREROUTING_direct all ::/0 ::/0
-        PREROUTING_ZONES_SOURCE all ::/0 ::/0
-        PREROUTING_ZONES all ::/0 ::/0
+    NS_CHECK([ip6tables -w -n -t filter -L RFC3964_IPv4], 1, [ignore], [ignore])
+    IP6TABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
+        ACCEPT all ::/0 ::/0 ctstate RELATED,ESTABLISHED
+        ACCEPT all ::/0 ::/0
+        FORWARD_direct all ::/0 ::/0
+        FORWARD_IN_ZONES_SOURCE all ::/0 ::/0
+        FORWARD_IN_ZONES all ::/0 ::/0
+        FORWARD_OUT_ZONES_SOURCE all ::/0 ::/0
+        FORWARD_OUT_ZONES all ::/0 ::/0
+        LOG all ::/0 ::/0 ctstate INVALID LOG flags 0 level 4 prefix "STATE_INVALID_DROP: "
+        DROP all ::/0 ::/0 ctstate INVALID
+        LOG all ::/0 ::/0 LOG flags 0 level 4 prefix "FINAL_REJECT: "
+        REJECT all ::/0 ::/0 reject-with icmp6-adm-prohibited
     ])
-    IP6TABLES_LIST_RULES([raw], [OUTPUT], 0, [dnl
+    IP6TABLES_LIST_RULES([filter], [OUTPUT], 0, [dnl
+        ACCEPT all ::/0 ::/0
         OUTPUT_direct all ::/0 ::/0
     ])
 ])
-- 
2.18.0