[PATCH 8/8] network: firewalld: use native NAT networks

Eric Garver eric at garver.life
Thu Nov 10 16:31:52 UTC 2022


Use the new "libvirt-nat" zone for native NAT networks.

The "libvirt" zone is still in use, but only to handle DHCP packets.
Those won't be dispatched to the "libvirt-zone" because said zone is
using sources (instead of interfaces). DHCP packets don't have a valid
source address.

The use of "libvirt" zone is necessary due to a Linux < 5.5 limitation
in which nftables iifname cannot be matched in postrouting hook (i.e.
masquerade). In the future, when we can assume Linux 5.5+, we can
further improve this by attaching interfaces to the "libvirt-nat" zone
instead of using sources. Thus making the "libvirt" zone unnecessary.

Signed-off-by: Eric Garver <eric at garver.life>
---
 src/network/bridge_driver_linux.c | 55 +++++++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 7 deletions(-)

diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c
index 42f098ff1f9b..d6c7d378f5f7 100644
--- a/src/network/bridge_driver_linux.c
+++ b/src/network/bridge_driver_linux.c
@@ -140,7 +140,10 @@ networkUseOnlyFirewallDRules(void)
         return false;
 
     if (virFirewallDPolicyExists("libvirt-routed-out") &&
-        virFirewallDZoneExists("libvirt-routed")) {
+        virFirewallDZoneExists("libvirt-routed") &&
+        virFirewallDPolicyExists("libvirt-nat-out") &&
+        virFirewallDZoneExists("libvirt-nat") &&
+        virFirewallDZoneExists("libvirt")) {
         return true;
     }
 
@@ -825,6 +828,48 @@ networkAddOnlyFirewallDRules(virNetworkDef *def)
     if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
         if (virFirewallDInterfaceSetZone(def->bridge, "libvirt-routed") < 0)
             return -1;
+    } else if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
+        virNetworkIPDef *ipdef;
+        size_t i;
+
+        /* The initial DHCP packets won't be dispatched to the
+         * libvirt-nat zone because they don't yet have an IP address.
+         * The libvirt-nat zone needs to use sources instead of
+         * interfaces because kernels < 5.5 do not support matching
+         * iifname in postrouting.
+         *
+         * As a workaround, add the interface to the libvirt zone. This
+         * will allow dhcp to function. Afterwards packets will go to
+         * the libvirt-nat zone.
+         */
+        if (virFirewallDInterfaceSetZone(def->bridge, "libvirt") < 0)
+            return -1;
+
+        for (i = 0;
+             (ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
+             i++) {
+            int prefix = virNetworkIPDefPrefix(ipdef);
+            g_autofree char *networkstr = NULL;
+
+            if (!(networkstr = virSocketAddrFormatWithPrefix(&ipdef->address, prefix, true)))
+                return -1;
+
+            if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
+                def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES) {
+                if (virFirewallDSourceSetZone(networkstr, "libvirt-nat") < 0)
+                    return -1;
+                if (def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES) {
+                    const char *rich_rules[] = {"rule family=ipv6 masquerade"};
+                    size_t rich_rules_count = sizeof(rich_rules) / sizeof(rich_rules[0]);
+
+                    if (virFirewallDApplyPolicyRichRules("libvirt-nat-out", rich_rules, rich_rules_count) < 0)
+                        return -1;
+                }
+            } else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) {
+                if (virFirewallDSourceSetZone(networkstr, "libvirt-routed") < 0)
+                    return -1;
+            }
+        }
     }
 
     return 0;
@@ -890,10 +935,8 @@ int networkAddFirewallRules(virNetworkDef *def)
     virNetworkIPDef *ipdef;
     g_autoptr(virFirewall) fw = virFirewallNew();
 
-    if (!def->bridgeZone && networkUseOnlyFirewallDRules() &&
-        def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
+    if (!def->bridgeZone && networkUseOnlyFirewallDRules())
         return networkAddOnlyFirewallDRules(def);
-    }
 
     if (virOnce(&createdOnce, networkSetupPrivateChains) < 0)
         return -1;
@@ -968,10 +1011,8 @@ void networkRemoveFirewallRules(virNetworkDef *def)
     virNetworkIPDef *ipdef;
     g_autoptr(virFirewall) fw = virFirewallNew();
 
-    if (!def->bridgeZone && networkUseOnlyFirewallDRules() &&
-        def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
+    if (!def->bridgeZone && networkUseOnlyFirewallDRules())
         return;
-    }
 
     virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
     networkRemoveChecksumFirewallRules(fw, def);
-- 
2.37.3



More information about the libvir-list mailing list