Merge pull request #50049 from robmry/nftables_env_var_enable

nftables: enable using env var
diff --git a/hack/make/.integration-test-helpers b/hack/make/.integration-test-helpers
index 324f9ae..449b21b 100644
--- a/hack/make/.integration-test-helpers
+++ b/hack/make/.integration-test-helpers
@@ -201,6 +201,7 @@
 			DOCKER_REMAP_ROOT="$DOCKER_REMAP_ROOT" \
 			DOCKER_REMOTE_DAEMON="$DOCKER_REMOTE_DAEMON" \
 			DOCKER_ROOTLESS="$DOCKER_ROOTLESS" \
+			DOCKER_FIREWALL_BACKEND="$DOCKER_FIREWALL_BACKEND" \
 			GITHUB_ACTIONS="$GITHUB_ACTIONS" \
 			GO111MODULE="$GO111MODULE" \
 			GOCACHE="$GOCACHE" \
diff --git a/libnetwork/controller.go b/libnetwork/controller.go
index d516dae..14858c7 100644
--- a/libnetwork/controller.go
+++ b/libnetwork/controller.go
@@ -168,6 +168,7 @@
 		diagnosticServer: diagnostic.New(),
 	}
 
+	c.selectFirewallBackend()
 	c.drvRegistry.Notify = c
 
 	// External plugins don't need config passed through daemon. They can
diff --git a/libnetwork/controller_linux.go b/libnetwork/controller_linux.go
index 1cc174f..7b858db 100644
--- a/libnetwork/controller_linux.go
+++ b/libnetwork/controller_linux.go
@@ -8,6 +8,7 @@
 
 	"github.com/containerd/log"
 	"github.com/docker/docker/api/types/system"
+	"github.com/docker/docker/libnetwork/internal/nftables"
 	"github.com/docker/docker/libnetwork/iptables"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/options"
@@ -16,6 +17,9 @@
 
 // FirewallBackend returns the name of the firewall backend for "docker info".
 func (c *Controller) FirewallBackend() *system.FirewallInfo {
+	if nftables.Enabled() {
+		return &system.FirewallInfo{Driver: "nftables"}
+	}
 	usingFirewalld, err := iptables.UsingFirewalld()
 	if err != nil {
 		return nil
diff --git a/libnetwork/drivers/bridge/bridge_linux.go b/libnetwork/drivers/bridge/bridge_linux.go
index 84534a4..1dc331f 100644
--- a/libnetwork/drivers/bridge/bridge_linux.go
+++ b/libnetwork/drivers/bridge/bridge_linux.go
@@ -519,7 +519,6 @@
 	if err != nil {
 		return err
 	}
-	iptables.OnReloaded(d.handleFirewalldReload)
 
 	var pdc portDriverClient
 	if config.Rootless {
@@ -535,6 +534,12 @@
 	d.config = config
 	d.Unlock()
 
+	// Register for an event when firewalld is reloaded, but take the config lock so
+	// that events won't be processed until the initial load from Store is complete.
+	d.configNetwork.Lock()
+	defer d.configNetwork.Unlock()
+	iptables.OnReloaded(d.handleFirewalldReload)
+
 	return d.initStore()
 }
 
diff --git a/libnetwork/firewall_linux.go b/libnetwork/firewall_linux.go
index 119267a..bdde7a7 100644
--- a/libnetwork/firewall_linux.go
+++ b/libnetwork/firewall_linux.go
@@ -4,13 +4,23 @@
 	"context"
 	"errors"
 	"fmt"
+	"os"
 
 	"github.com/containerd/log"
+	"github.com/docker/docker/libnetwork/internal/nftables"
 	"github.com/docker/docker/libnetwork/iptables"
 )
 
 const userChain = "DOCKER-USER"
 
+func (c *Controller) selectFirewallBackend() {
+	// Only try to use nftables if explicitly enabled by env-var.
+	// TODO(robmry) - command line options?
+	if os.Getenv("DOCKER_FIREWALL_BACKEND") == "nftables" {
+		_ = nftables.Enable()
+	}
+}
+
 // Sets up the DOCKER-USER chain for each iptables version (IPv4, IPv6) that's
 // enabled in the controller's configuration.
 func (c *Controller) setupUserChains() {
diff --git a/libnetwork/firewall_others.go b/libnetwork/firewall_others.go
index 85aa825..fb5996f 100644
--- a/libnetwork/firewall_others.go
+++ b/libnetwork/firewall_others.go
@@ -2,4 +2,6 @@
 
 package libnetwork
 
+func (c *Controller) selectFirewallBackend() {}
+
 func (c *Controller) setupUserChains() {}
diff --git a/testutil/daemon/daemon.go b/testutil/daemon/daemon.go
index 7b797b0..dca8d97 100644
--- a/testutil/daemon/daemon.go
+++ b/testutil/daemon/daemon.go
@@ -234,6 +234,10 @@
 	}
 	ops = append(ops, WithOOMScoreAdjust(-500))
 
+	if val, ok := os.LookupEnv("DOCKER_FIREWALL_BACKEND"); ok {
+		ops = append(ops, WithEnvVars("DOCKER_FIREWALL_BACKEND="+val))
+	}
+
 	d, err := NewDaemon(dest, ops...)
 	assert.NilError(t, err, "could not create daemon at %q", dest)
 	if d.rootlessUser != nil && d.dockerdBinary != defaultDockerdBinary {