Repository: antrea-io/antrea
Pull request #3236 (2022-01-24 10:48:40)

Add github labels to indicate actions required to take
action/backport: Indicates a PR that requires backports.
action/release-note: Indicates a PR that should be included in release notes.

Signed-off-by: Quan Tian <qtian@vmware.com>

tnqn: I haven't created the labels, want to hear opinions about the labels first. Typically a PR's author can add the labels if they are aware of the impact of their PRs. If they don't, maintainers who review/merge the PRs should consider adding them to facilitate backport and release process.
codecov-commenter: # [Codecov](https://codecov.io/gh/antrea-io/antrea/pull/3236?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#3236](https://codecov.io/gh/antrea-io/antrea/pull/3236?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (b2bf1b9) into [main](https://codecov.io/gh/antrea-io/antrea/commit/df6094bd0870a763d0d63b4fc69738a5370aa0de?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (df6094b) will **decrease** coverage by `17.40%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/antrea-io/antrea/pull/3236/graphs/tree.svg?width=650&height=150&src=pr&token=TjWaLL2GoC&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)](https://codecov.io/gh/antrea-io/antrea/pull/3236?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

```diff
@@             Coverage Diff             @@
##             main    #3236       +/-   ##
===========================================
- Coverage   58.76%   41.36%   -17.41%     
===========================================
  Files         331      199      -132     
  Lines       28437    24097     -4340     
===========================================
- Hits        16711     9967     -6744     
- Misses       9894    13102     +3208     
+ Partials     1832     1028      -804     
```

| Flag | Coverage Δ | |
|---|---|---|
| kind-e2e-tests | `?` | |
| unit-tests | `41.36% <ø> (-0.01%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/antrea-io/antrea/pull/3236?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [pkg/ovs/openflow/default.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL292cy9vcGVuZmxvdy9kZWZhdWx0Lmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/util/runtime/runtime.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL3V0aWwvcnVudGltZS9ydW50aW1lLmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/ovs/openflow/logs.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL292cy9vcGVuZmxvdy9sb2dzLmdv) | `9.52% <0.00%> (-90.48%)` | :arrow_down: |
| [pkg/apis/controlplane/register.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FwaXMvY29udHJvbHBsYW5lL3JlZ2lzdGVyLmdv) | `0.00% <0.00%> (-90.00%)` | :arrow_down: |
| [pkg/agent/nodeportlocal/k8s/annotations.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L25vZGVwb3J0bG9jYWwvazhzL2Fubm90YXRpb25zLmdv) | `0.00% <0.00%> (-83.88%)` | :arrow_down: |
| [pkg/agent/controller/networkpolicy/reject.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2NvbnRyb2xsZXIvbmV0d29ya3BvbGljeS9yZWplY3QuZ28=) | `0.00% <0.00%> (-78.23%)` | :arrow_down: |
| [pkg/agent/client.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2NsaWVudC5nbw==) | `0.00% <0.00%> (-77.42%)` | :arrow_down: |
| [pkg/ovs/ovsconfig/ovs\_client\_linux.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL292cy9vdnNjb25maWcvb3ZzX2NsaWVudF9saW51eC5nbw==) | `0.00% <0.00%> (-76.93%)` | :arrow_down: |
| [pkg/flowaggregator/certificate.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2Zsb3dhZ2dyZWdhdG9yL2NlcnRpZmljYXRlLmdv) | `0.00% <0.00%> (-76.58%)` | :arrow_down: |
| [...agent/controller/traceflow/traceflow\_controller.go](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2NvbnRyb2xsZXIvdHJhY2VmbG93L3RyYWNlZmxvd19jb250cm9sbGVyLmdv) | `0.00% <0.00%> (-69.86%)` | :arrow_down: |
| ... and [243 more](https://codecov.io/gh/antrea-io/antrea/pull/3236/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | |

tnqn: /skip-all

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 156b3a7e4..60ce7aca9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -227,7 +227,11 @@ do:
 
 The second step will compile the Antrea code in a `golang` container, and build
 a `Ubuntu 20.04` Docker image that includes all the generated binaries. [`Docker`](https://docs.docker.com/install)
-must be installed on your local machine in advance.
+must be installed on your local machine in advance. If you are a macOS user and
+cannot use [Docker Desktop](https://www.docker.com/products/docker-desktop) to
+contribute to Antrea for licensing reasons, check out this
+[document](docs/contributors/docker-desktop-alternatives.md) for possible
+alternatives.
 
 Alternatively, you can build the Antrea code in your local Go environment. The
 Antrea project uses the [Go modules support](https://github.com/golang/go/wiki/Modules) which was introduced in Go 1.11. It
diff --git a/cmd/antrea-agent/agent.go b/cmd/antrea-agent/agent.go
index ed2d18d02..c80035c4d 100644
--- a/cmd/antrea-agent/agent.go
+++ b/cmd/antrea-agent/agent.go
@@ -238,8 +238,8 @@ func run(o *Options) error {
 	v4GroupCounter := proxytypes.NewGroupCounter(false, groupIDUpdates)
 	v6GroupCounter := proxytypes.NewGroupCounter(true, groupIDUpdates)
 
-	v4Enabled := config.IsIPv4Enabled(nodeConfig, networkConfig.TrafficEncapMode)
-	v6Enabled := config.IsIPv6Enabled(nodeConfig, networkConfig.TrafficEncapMode)
+	v4Enabled := networkConfig.IPv4Enabled
+	v6Enabled := networkConfig.IPv6Enabled
 	var proxier proxy.Proxier
 	if features.DefaultFeatureGate.Enabled(features.AntreaProxy) {
 		proxyAll := o.config.AntreaProxy.ProxyAll
@@ -290,7 +290,9 @@ func run(o *Options) error {
 		statusManagerEnabled,
 		loggingEnabled,
 		asyncRuleDeleteInterval,
-		o.config.DNSServerOverride)
+		o.config.DNSServerOverride,
+		v4Enabled,
+		v6Enabled)
 	if err != nil {
 		return fmt.Errorf("error creating new NetworkPolicy controller: %v", err)
 	}
@@ -612,7 +614,9 @@ func run(o *Options) error {
 		*o.config.EnablePrometheusMetrics,
 		o.config.ClientConnection.Kubeconfig,
 		cipherSuites,
-		cipher.TLSVersionMap[o.config.TLSMinVersion])
+		cipher.TLSVersionMap[o.config.TLSMinVersion],
+		v4Enabled,
+		v6Enabled)
 	if err != nil {
 		return fmt.Errorf("error when creating agent API server: %v", err)
 	}
diff --git a/docs/contributors/cherry-picks.md b/docs/contributors/cherry-picks.md
index f9a464750..5e2c2db40 100644
--- a/docs/contributors/cherry-picks.md
+++ b/docs/contributors/cherry-picks.md
@@ -17,7 +17,7 @@ policy](../versioning.md#minor-releases-and-patch-releases).
 
 * A PR which was approved and merged into the main branch.
 * The PR was identified as a good candidate for backporting by an Antrea
-  maintainer: they will leave a comment on Github for the PR and provide a list
+  maintainer: they will label the PR with `action/backport` and comment a list
   of release branches to which the patch should be backported (example:
   [`release-1.0`](https://github.com/antrea-io/antrea/tree/release-1.0)).
 * Have the [Github CLI](https://cli.github.com/) installed (version >= 1.3) and
diff --git a/docs/contributors/docker-desktop-alternatives.md b/docs/contributors/docker-desktop-alternatives.md
new file mode 100644
index 000000000..97b20b0fd
--- /dev/null
+++ b/docs/contributors/docker-desktop-alternatives.md
@@ -0,0 +1,53 @@
+# Docker Desktop Alternatives
+
+The Antrea build system relies on Docker to build container images, which can
+then be used to test Antrea locally. As an Antrea developer, if you run `make`,
+`docker build` will be invoked to build the `antrea-ubuntu` container image. On
+Linux, Docker Engine (based on moby) runs natively, but if you use macOS or
+Windows for Antrea development, Docker needs to run inside a Linux Virtual
+Machine (VM). This VM is typically managed by [Docker
+Desktop](https://www.docker.com/products/docker-desktop). Starting January 31
+2022, Docker Desktop requires a per user paid subscription for professional use
+in "large" companies (more than 250 employees or more than $10 million in annual
+revenue). See <https://www.docker.com/pricing/faq> for details. For developers
+who contribute to Antrea as an employee of such a company (and not in their own
+individual capacity), it is no longer possible to use Docker Desktop to build
+(and possibly run) Antrea Docker images locally, unless they have a Docker
+subscription.
+
+For contributors who do not have a Docker subscription, we recommend the
+following Docker Desktop alternatives.
+
+## Colima (macOS)
+
+[Colima](https://github.com/abiosoft/colima) is a UI built with
+[Lima](https://github.com/lima-vm/lima). It supports running a container runtime
+(docker, containerd or kuberneters) on macOS, inside a Lima VM. Major benefits
+of Colima include its ability to be used as a drop-in replacement for Docker
+Desktop and its ability to coexist with Docker Desktop on the same macOS
+machine.
+
+To install and run Colima, follow these steps:
+
+* `brew install colima`
+* `colima start` to start Colima (the Linux VM) with the default
+  configuration. Check the Colima documentation for configuration options. By
+  default, Colima will use the Docker runtime. This means that you can keep
+  using the `docker` CLI and that no changes are required to build Antrea.
+* `docker context list` and check that the `colima` context is selected. You can
+  use `docker context use desktop-linux` to go back to Docker Desktop.
+* `make` to build Antrea locally. Check that the `antrea-ubuntu` image is
+  available by listing all images with `docker images`.
+
+TODO: validate that Kind can be used with Colima without any issue.
+
+## Rancher Desktop (macOS and Windows)
+
+Rancher Desktop is another possible alternative to Docker Desktop, which
+supports Windows in addition to macOS. On macOS, it also uses Lima as the Linux
+VM. Two major differences with Colima are that Rancher Desktop will always run
+Kubernetes, and that Rancher Desktop uses the
+[`nerdctl`](https://github.com/containerd/nerdctl) UI for container management
+instead of `docker`. However, the `nerdctl` and `docker` UIs are supposed to be
+compatible, so in theory it should be possible to alias `docker` to `nerdctl`
+and keep using the Antrea build system as is (to be tested).
diff --git a/docs/contributors/github-labels.md b/docs/contributors/github-labels.md
index cdea5aa57..480d50074 100644
--- a/docs/contributors/github-labels.md
+++ b/docs/contributors/github-labels.md
@@ -111,6 +111,8 @@ The labels in this list originated within Kubernetes at
 | triage/needs-information           | Indicates an issue needs more information in order to work on it. | Humans |
 | triage/not-reproducible            | Indicates an issue can not be reproduced as described. | Humans |
 | triage/unresolved                  | Indicates an issue that can not or will not be resolved. | Humans |
+| action/backport                    | Indicates a PR that requires backports. | Humans |
+| action/release-note                | Indicates a PR that should be included in release notes.  | Humans |
 
 ## Labels that apply only to issues
 
diff --git a/docs/maintainers/release.md b/docs/maintainers/release.md
index 1a91d966b..d2a9f0924 100644
--- a/docs/maintainers/release.md
+++ b/docs/maintainers/release.md
@@ -15,13 +15,14 @@ release. We use `<TAG>` as a placeholder for the release tag (e.g. `v1.4.0`).
 2. Open a PR (labelled with `kind/release`) against the appropriate release
    branch with the following commits:
    - a commit to update the [CHANGELOG](../../CHANGELOG). *For a minor release*,
-     all significant changes and all bug fixes since the first version of the
-     previous minor release should be mentioned, even bug fixes which have
-     already been included in some patch release. *For a patch release*, you
-     will mention all the bug fixes since the previous release with the same
-     minor version. The commit message must be *exactly* `"Update CHANGELOG for
-     <TAG> release"`, as a bot will look for this commit and cherry-pick it to
-     update the main branch (starting with Antrea v1.0). The
+     all significant changes and all bug fixes (labelled with
+     `action/release-note` since the first version of the previous minor release
+     should be mentioned, even bug fixes which have already been included in
+     some patch release. *For a patch release*, you will mention all the bug
+     fixes since the previous release with the same minor version. The commit
+     message must be *exactly* `"Update CHANGELOG for <TAG> release"`, as a bot
+     will look for this commit and cherry-pick it to update the main branch
+     (starting with Antrea v1.0). The
      [process-changelog.go](../../hack/release/process-changelog.go) script may
      be used to easily generate links to PRs and the Github profiles of PR
      authors.
diff --git a/hack/.notableofcontents b/hack/.notableofcontents
index 8b28362ff..42f5e577f 100644
--- a/hack/.notableofcontents
+++ b/hack/.notableofcontents
@@ -6,6 +6,7 @@ docs/assets/README.md
 docs/assets/logo/README.md
 docs/contributors/cherry-picks.md
 docs/contributors/code-generation.md
+docs/contributors/docker-desktop-alternatives.md
 docs/contributors/eks-terraform.md
 docs/contributors/github-labels.md
 docs/contributors/manual-installation.md
diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go
index 4ce1790d4..015f25085 100644
--- a/pkg/agent/agent.go
+++ b/pkg/agent/agent.go
@@ -343,6 +343,9 @@ func (i *Initializer) Initialize() error {
 		return err
 	}
 
+	i.networkConfig.IPv4Enabled = config.IsIPv4Enabled(i.nodeConfig, i.networkConfig.TrafficEncapMode)
+	i.networkConfig.IPv6Enabled = config.IsIPv6Enabled(i.nodeConfig, i.networkConfig.TrafficEncapMode)
+
 	if err := i.prepareHostNetwork(); err != nil {
 		return err
 	}
diff --git a/pkg/agent/apiserver/apiserver.go b/pkg/agent/apiserver/apiserver.go
index 434ccde40..763df7492 100644
--- a/pkg/agent/apiserver/apiserver.go
+++ b/pkg/agent/apiserver/apiserver.go
@@ -84,10 +84,10 @@ func installHandlers(aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolic
 	s.Handler.NonGoRestfulMux.HandleFunc("/ovstracing", ovstracing.HandleFunc(aq))
 }
 
-func installAPIGroup(s *genericapiserver.GenericAPIServer, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier) error {
+func installAPIGroup(s *genericapiserver.GenericAPIServer, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier, v4Enabled, v6Enabled bool) error {
 	systemGroup := genericapiserver.NewDefaultAPIGroupInfo(systemv1beta1.GroupName, scheme, metav1.ParameterCodec, codecs)
 	systemStorage := map[string]rest.Storage{}
-	supportBundleStorage := supportbundle.NewAgentStorage(ovsctl.NewClient(aq.GetNodeConfig().OVSBridge), aq, npq)
+	supportBundleStorage := supportbundle.NewAgentStorage(ovsctl.NewClient(aq.GetNodeConfig().OVSBridge), aq, npq, v4Enabled, v6Enabled)
 	systemStorage["supportbundles"] = supportBundleStorage.SupportBundle
 	systemStorage["supportbundles/download"] = supportBundleStorage.Download
 	systemGroup.VersionedResourcesStorageMap["v1beta1"] = systemStorage
@@ -96,7 +96,7 @@ func installAPIGroup(s *genericapiserver.GenericAPIServer, aq agentquerier.Agent
 
 // New creates an APIServer for running in antrea agent.
 func New(aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier, bindPort int,
-	enableMetrics bool, kubeconfig string, cipherSuites []uint16, tlsMinVersion uint16) (*agentAPIServer, error) {
+	enableMetrics bool, kubeconfig string, cipherSuites []uint16, tlsMinVersion uint16, v4Enabled, v6Enabled bool) (*agentAPIServer, error) {
 	cfg, err := newConfig(npq, bindPort, enableMetrics, kubeconfig)
 	if err != nil {
 		return nil, err
@@ -107,7 +107,7 @@ func New(aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier
 	}
 	s.SecureServingInfo.CipherSuites = cipherSuites
 	s.SecureServingInfo.MinTLSVersion = tlsMinVersion
-	if err := installAPIGroup(s, aq, npq); err != nil {
+	if err := installAPIGroup(s, aq, npq, v4Enabled, v6Enabled); err != nil {
 		return nil, err
 	}
 	installHandlers(aq, npq, s)
diff --git a/pkg/agent/config/node_config.go b/pkg/agent/config/node_config.go
index 248d9c835..01bd20a96 100644
--- a/pkg/agent/config/node_config.go
+++ b/pkg/agent/config/node_config.go
@@ -154,6 +154,8 @@ type NetworkConfig struct {
 	IPSecPSK              string
 	TransportIface        string
 	TransportIfaceCIDRs   []string
+	IPv4Enabled           bool
+	IPv6Enabled           bool
 }
 
 // IsIPv4Enabled returns true if the cluster network supports IPv4.
diff --git a/pkg/agent/controller/networkpolicy/fqdn.go b/pkg/agent/controller/networkpolicy/fqdn.go
index cfe0ddae0..f7962039e 100644
--- a/pkg/agent/controller/networkpolicy/fqdn.go
+++ b/pkg/agent/controller/networkpolicy/fqdn.go
@@ -148,9 +148,11 @@ type fqdnController struct {
 	selectorItemToFQDN map[fqdnSelectorItem]sets.String
 	// selectorItemToRuleIDs maps fqdnToSelectorItem to the rules that contains the selector.
 	selectorItemToRuleIDs map[fqdnSelectorItem]sets.String
+	ipv4Enabled           bool
+	ipv6Enabled           bool
 }
 
-func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string)) (*fqdnController, error) {
+func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool) (*fqdnController, error) {
 	controller := &fqdnController{
 		ofClient:               client,
 		dirtyRuleHandler:       dirtyRuleHandler,
@@ -162,6 +164,8 @@ func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServer
 		fqdnToSelectorItem:     map[string]map[fqdnSelectorItem]struct{}{},
 		selectorItemToFQDN:     map[fqdnSelectorItem]sets.String{},
 		selectorItemToRuleIDs:  map[fqdnSelectorItem]sets.String{},
+		ipv4Enabled:            v4Enabled,
+		ipv6Enabled:            v6Enabled,
 	}
 	if controller.ofClient != nil {
 		if err := controller.ofClient.NewDNSpacketInConjunction(dnsInterceptRuleID); err != nil {
@@ -584,14 +588,14 @@ func (f *fqdnController) parseDNSResponse(msg *dns.Msg) (string, map[string]net.
 	for _, ans := range msg.Answer {
 		switch r := ans.(type) {
 		case *dns.A:
-			if f.ofClient.IsIPv4Enabled() {
+			if f.ipv4Enabled {
 				responseIPs[r.A.String()] = r.A
 				if r.Header().Ttl < lowestTTL {
 					lowestTTL = r.Header().Ttl
 				}
 			}
 		case *dns.AAAA:
-			if f.ofClient.IsIPv6Enabled() {
+			if f.ipv6Enabled {
 				responseIPs[r.AAAA.String()] = r.AAAA
 				if r.Header().Ttl < lowestTTL {
 					lowestTTL = r.Header().Ttl
@@ -650,7 +654,7 @@ func (f *fqdnController) lookupIP(ctx context.Context, fqdn string) error {
 		return responseIPs
 	}
 
-	if f.ofClient.IsIPv4Enabled() {
+	if f.ipv4Enabled {
 		lookupTime := time.Now()
 		if ips, err := resolver.LookupIP(ctx, "ip4", fqdn); err == nil {
 			f.onDNSResponse(fqdn, makeResponseIPs(ips), defaultTTL, lookupTime, nil)
@@ -658,7 +662,7 @@ func (f *fqdnController) lookupIP(ctx context.Context, fqdn string) error {
 			v4ok = false
 		}
 	}
-	if f.ofClient.IsIPv6Enabled() {
+	if f.ipv6Enabled {
 		lookupTime := time.Now()
 		if ips, err := resolver.LookupIP(ctx, "ip6", fqdn); err == nil {
 			f.onDNSResponse(fqdn, makeResponseIPs(ips), defaultTTL, lookupTime, nil)
@@ -695,7 +699,7 @@ func (f *fqdnController) makeDNSRequest(ctx context.Context, fqdn string) error
 		return r, nil
 	}
 	v4ok, v6ok := true, true
-	if f.ofClient.IsIPv4Enabled() {
+	if f.ipv4Enabled {
 		m := dns.Msg{}
 		m.SetQuestion(fqdnToQuery, dns.TypeA)
 		lookupTime := time.Now()
@@ -705,7 +709,7 @@ func (f *fqdnController) makeDNSRequest(ctx context.Context, fqdn string) error
 			v4ok = false
 		}
 	}
-	if f.ofClient.IsIPv6Enabled() {
+	if f.ipv6Enabled {
 		m := dns.Msg{}
 		m.SetQuestion(fqdnToQuery, dns.TypeAAAA)
 		lookupTime := time.Now()
diff --git a/pkg/agent/controller/networkpolicy/networkpolicy_controller.go b/pkg/agent/controller/networkpolicy/networkpolicy_controller.go
index 2bb74f562..33ba8111d 100644
--- a/pkg/agent/controller/networkpolicy/networkpolicy_controller.go
+++ b/pkg/agent/controller/networkpolicy/networkpolicy_controller.go
@@ -120,7 +120,9 @@ func NewNetworkPolicyController(antreaClientGetter agent.AntreaClientProvider,
 	statusManagerEnabled bool,
 	loggingEnabled bool,
 	asyncRuleDeleteInterval time.Duration,
-	dnsServerOverride string) (*Controller, error) {
+	dnsServerOverride string,
+	v4Enabled bool,
+	v6Enabled bool) (*Controller, error) {
 	idAllocator := newIDAllocator(asyncRuleDeleteInterval, dnsInterceptRuleID)
 	c := &Controller{
 		antreaClientProvider: antreaClientGetter,
@@ -133,14 +135,14 @@ func NewNetworkPolicyController(antreaClientGetter agent.AntreaClientProvider,
 	}
 	if antreaPolicyEnabled {
 		var err error
-		if c.fqdnController, err = newFQDNController(ofClient, idAllocator, dnsServerOverride, c.enqueueRule); err != nil {
+		if c.fqdnController, err = newFQDNController(ofClient, idAllocator, dnsServerOverride, c.enqueueRule, v4Enabled, v6Enabled); err != nil {
 			return nil, err
 		}
 		if c.ofClient != nil {
 			c.ofClient.RegisterPacketInHandler(uint8(openflow.PacketInReasonNP), "dnsresponse", c.fqdnController)
 		}
 	}
-	c.reconciler = newReconciler(ofClient, ifaceStore, idAllocator, c.fqdnController, groupCounters)
+	c.reconciler = newReconciler(ofClient, ifaceStore, idAllocator, c.fqdnController, groupCounters, v4Enabled, v6Enabled)
 	c.ruleCache = newRuleCache(c.enqueueRule, entityUpdates, groupIDUpdates)
 	if statusManagerEnabled {
 		c.statusManager = newStatusController(antreaClientGetter, nodeName, c.ruleCache)
diff --git a/pkg/agent/controller/networkpolicy/reconciler.go b/pkg/agent/controller/networkpolicy/reconciler.go
index 06a75ae40..7d9a369d6 100644
--- a/pkg/agent/controller/networkpolicy/reconciler.go
+++ b/pkg/agent/controller/networkpolicy/reconciler.go
@@ -217,6 +217,8 @@ func newReconciler(ofClient openflow.Client,
 	idAllocator *idAllocator,
 	fqdnController *fqdnController,
 	groupCounters []proxytypes.GroupCounter,
+	v4Enabled bool,
+	v6Enabled bool,
 ) *reconciler {
 	priorityAssigners := map[uint8]*tablePriorityAssigner{}
 	for _, table := range openflow.GetAntreaPolicyBaselineTierTables() {
@@ -240,8 +242,8 @@ func newReconciler(ofClient openflow.Client,
 	}
 	// Check if ofClient is nil or not to be compatible with unit tests.
 	if ofClient != nil {
-		reconciler.ipv4Enabled = ofClient.IsIPv4Enabled()
-		reconciler.ipv6Enabled = ofClient.IsIPv6Enabled()
+		reconciler.ipv4Enabled = v4Enabled
+		reconciler.ipv6Enabled = v6Enabled
 	}
 	return reconciler
 }
diff --git a/pkg/agent/openflow/client.go b/pkg/agent/openflow/client.go
index ad577481a..09627844d 100644
--- a/pkg/agent/openflow/client.go
+++ b/pkg/agent/openflow/client.go
@@ -248,10 +248,6 @@ type Client interface {
 	StartPacketInHandler(packetInStartedReason []uint8, stopCh <-chan struct{})
 	// Get traffic metrics of each NetworkPolicy rule.
 	NetworkPolicyMetrics() map[uint32]*types.RuleMetric
-	// Returns if IPv4 is supported on this Node or not.
-	IsIPv4Enabled() bool
-	// Returns if IPv6 is supported on this Node or not.
-	IsIPv6Enabled() bool
 	// SendTCPPacketOut sends TCP packet as a packet-out to OVS.
 	SendTCPPacketOut(
 		srcMAC string,
@@ -652,7 +648,7 @@ func (c *client) InstallDefaultServiceFlows(nodePortAddressesIPv4, nodePortAddre
 		c.sessionAffinityReselectFlow(),
 		c.l2ForwardOutputServiceHairpinFlow(),
 	}
-	if c.IsIPv4Enabled() {
+	if c.networkConfig.IPv4Enabled {
 		flows = append(flows, c.serviceHairpinResponseDNATFlow(binding.ProtocolIP))
 		flows = append(flows, c.serviceLBBypassFlows(binding.ProtocolIP)...)
 		flows = append(flows, c.l3FwdServiceDefaultFlowsViaGW(binding.ProtocolIP, cookie.Service)...)
@@ -666,7 +662,7 @@ func (c *client) InstallDefaultServiceFlows(nodePortAddressesIPv4, nodePortAddre
 			flows = append(flows, c.serviceClassifierFlows(nodePortAddressesIPv4, binding.ProtocolIP)...)
 		}
 	}
-	if c.IsIPv6Enabled() {
+	if c.networkConfig.IPv6Enabled {
 		flows = append(flows, c.serviceHairpinResponseDNATFlow(binding.ProtocolIPv6))
 		flows = append(flows, c.serviceLBBypassFlows(binding.ProtocolIPv6)...)
 		flows = append(flows, c.l3FwdServiceDefaultFlowsViaGW(binding.ProtocolIPv6, cookie.Service)...)
@@ -786,10 +782,10 @@ func (c *client) Initialize(roundInfo types.RoundInfo, nodeConfig *config.NodeCo
 	c.nodeConfig = nodeConfig
 	c.networkConfig = networkConfig
 
-	if config.IsIPv4Enabled(nodeConfig, c.networkConfig.TrafficEncapMode) {
+	if networkConfig.IPv4Enabled {
 		c.ipProtocols = append(c.ipProtocols, binding.ProtocolIP)
 	}
-	if config.IsIPv6Enabled(nodeConfig, c.networkConfig.TrafficEncapMode) {
+	if networkConfig.IPv6Enabled {
 		c.ipProtocols = append(c.ipProtocols, binding.ProtocolIPv6)
 	}
 
@@ -1058,14 +1054,6 @@ func (c *client) InitialTLVMap() error {
 	return c.bridge.AddTLVMap(0x0104, 0x80, 4, 0)
 }
 
-func (c *client) IsIPv4Enabled() bool {
-	return config.IsIPv4Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode)
-}
-
-func (c *client) IsIPv6Enabled() bool {
-	return config.IsIPv6Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode)
-}
-
 // setBasePacketOutBuilder sets base IP properties of a packetOutBuilder which can have more packet data added.
 func setBasePacketOutBuilder(packetOutBuilder binding.PacketOutBuilder, srcMAC string, dstMAC string, srcIP string, dstIP string, inPort uint32, outPort uint32) (binding.PacketOutBuilder, error) {
 	// Set ethernet header.
diff --git a/pkg/agent/openflow/network_policy.go b/pkg/agent/openflow/network_policy.go
index af36a63f4..badd3020c 100644
--- a/pkg/agent/openflow/network_policy.go
+++ b/pkg/agent/openflow/network_policy.go
@@ -781,7 +781,7 @@ func (c *clause) addAddrFlows(client *client, addrType types.AddressType, addres
 func (c *clause) addServiceFlows(client *client, ports []v1beta2.Service, priority *uint16, matchSrc bool) []*conjMatchFlowContextChange {
 	var conjMatchFlowContextChanges []*conjMatchFlowContextChange
 	for _, port := range ports {
-		matches := generateServicePortConjMatches(c.ruleTable.GetID(), port, priority, client.IsIPv4Enabled(), client.IsIPv6Enabled(), matchSrc)
+		matches := generateServicePortConjMatches(c.ruleTable.GetID(), port, priority, client.networkConfig.IPv4Enabled, client.networkConfig.IPv6Enabled, matchSrc)
 		for _, match := range matches {
 			ctxChange := c.addConjunctiveMatchFlow(client, match)
 			conjMatchFlowContextChanges = append(conjMatchFlowContextChanges, ctxChange)
@@ -994,7 +994,7 @@ func (c *client) addRuleToConjunctiveMatch(conj *policyRuleConjunction, rule *ty
 	}
 	if conj.serviceClause != nil {
 		for _, port := range rule.Service {
-			matches := generateServicePortConjMatches(conj.serviceClause.ruleTable.GetID(), port, rule.Priority, c.IsIPv4Enabled(), c.IsIPv6Enabled(), false)
+			matches := generateServicePortConjMatches(conj.serviceClause.ruleTable.GetID(), port, rule.Priority, c.networkConfig.IPv4Enabled, c.networkConfig.IPv6Enabled, false)
 			for _, match := range matches {
 				c.addActionToConjunctiveMatch(conj.serviceClause, match)
 			}
diff --git a/pkg/agent/route/route_linux.go b/pkg/agent/route/route_linux.go
index 42dc5cc47..a62e8f721 100644
--- a/pkg/agent/route/route_linux.go
+++ b/pkg/agent/route/route_linux.go
@@ -394,10 +394,8 @@ func (c *Client) writeEKSMangleRule(iptablesData *bytes.Buffer) {
 // It's idempotent and can safely be called on every startup.
 func (c *Client) syncIPTables() error {
 	var err error
-	v4Enabled := config.IsIPv4Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode)
-	v6Enabled := config.IsIPv6Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode)
 
-	c.ipt, err = iptables.New(v4Enabled, v6Enabled)
+	c.ipt, err = iptables.New(c.networkConfig.IPv4Enabled, c.networkConfig.IPv6Enabled)
 	if err != nil {
 		return fmt.Errorf("error creating IPTables instance: %v", err)
 	}
@@ -443,7 +441,7 @@ func (c *Client) syncIPTables() error {
 		return true
 	})
 	// Use iptables-restore to configure IPv4 settings.
-	if v4Enabled {
+	if c.networkConfig.IPv4Enabled {
 		iptablesData := c.restoreIptablesData(c.nodeConfig.PodIPv4CIDR, antreaPodIPSet, localAntreaFlexibleIPAMPodIPSet, antreaNodePortIPSet, config.VirtualServiceIPv4, snatMarkToIPv4)
 		// Setting --noflush to keep the previous contents (i.e. non antrea managed chains) of the tables.
 		if err := c.ipt.Restore(iptablesData.Bytes(), false, false); err != nil {
@@ -452,7 +450,7 @@ func (c *Client) syncIPTables() error {
 	}
 
 	// Use ip6tables-restore to configure IPv6 settings.
-	if v6Enabled {
+	if c.networkConfig.IPv6Enabled {
 		iptablesData := c.restoreIptablesData(c.nodeConfig.PodIPv6CIDR, antreaPodIP6Set, localAntreaFlexibleIPAMPodIP6Set, antreaNodePortIP6Set, config.VirtualServiceIPv6, snatMarkToIPv6)
 		// Setting --noflush to keep the previous contents (i.e. non antrea managed chains) of the tables.
 		if err := c.ipt.Restore(iptablesData.Bytes(), false, true); err != nil {
@@ -668,12 +666,12 @@ func (c *Client) initIPRoutes() error {
 }
 
 func (c *Client) initServiceIPRoutes() error {
-	if config.IsIPv4Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) {
+	if c.networkConfig.IPv4Enabled {
 		if err := c.addVirtualServiceIPRoute(false); err != nil {
 			return err
 		}
 	}
-	if config.IsIPv6Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) {
+	if c.networkConfig.IPv6Enabled {
 		if err := c.addVirtualServiceIPRoute(true); err != nil {
 			return err
 		}
diff --git a/pkg/agent/route/route_windows.go b/pkg/agent/route/route_windows.go
index 047b45cf6..81b6fb7d8 100644
--- a/pkg/agent/route/route_windows.go
+++ b/pkg/agent/route/route_windows.go
@@ -106,12 +106,12 @@ func (c *Client) Initialize(nodeConfig *config.NodeConfig, done func()) error {
 }
 
 func (c *Client) initServiceIPRoutes() error {
-	if config.IsIPv4Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) {
+	if c.networkConfig.IPv4Enabled {
 		if err := c.addVirtualServiceIPRoute(false); err != nil {
 			return err
 		}
 	}
-	if config.IsIPv6Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) {
+	if c.networkConfig.IPv6Enabled {
 		return fmt.Errorf("IPv6 is not supported on Windows")
 	}
 	return nil
diff --git a/pkg/apiserver/registry/system/supportbundle/rest.go b/pkg/apiserver/registry/system/supportbundle/rest.go
index 7c7b6cb22..55dbf4ed8 100644
--- a/pkg/apiserver/registry/system/supportbundle/rest.go
+++ b/pkg/apiserver/registry/system/supportbundle/rest.go
@@ -71,7 +71,7 @@ func NewControllerStorage() Storage {
 }
 
 // NewAgentStorage creates a support bundle storage for working on antrea agent.
-func NewAgentStorage(client ovsctl.OVSCtlClient, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier) Storage {
+func NewAgentStorage(client ovsctl.OVSCtlClient, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier, v4Enabled, v6Enabled bool) Storage {
 	bundle := &supportBundleREST{
 		mode:         modeAgent,
 		ovsCtlClient: client,
@@ -81,6 +81,8 @@ func NewAgentStorage(client ovsctl.OVSCtlClient, aq agentquerier.AgentQuerier, n
 			ObjectMeta: metav1.ObjectMeta{Name: modeAgent},
 			Status:     systemv1beta1.SupportBundleStatusNone,
 		},
+		v4Enabled: v4Enabled,
+		v6Enabled: v6Enabled,
 	}
 	return Storage{
 		Mode:          modeAgent,
@@ -114,6 +116,8 @@ type supportBundleREST struct {
 	ovsCtlClient ovsctl.OVSCtlClient
 	aq           agentquerier.AgentQuerier
 	npq          querier.AgentNetworkPolicyInfoQuerier
+	v4Enabled    bool
+	v6Enabled    bool
 }
 
 // Create triggers a bundle generation. It only allows resource creation when
@@ -253,7 +257,7 @@ func (r *supportBundleREST) collect(ctx context.Context, dumpers ...func(string)
 }
 
 func (r *supportBundleREST) collectAgent(ctx context.Context, since string) (*systemv1beta1.SupportBundle, error) {
-	dumper := support.NewAgentDumper(defaultFS, defaultExecutor, r.ovsCtlClient, r.aq, r.npq, since)
+	dumper := support.NewAgentDumper(defaultFS, defaultExecutor, r.ovsCtlClient, r.aq, r.npq, since, r.v4Enabled, r.v6Enabled)
 	return r.collect(
 		ctx,
 		dumper.DumpLog,
diff --git a/pkg/support/dump.go b/pkg/support/dump.go
index f60e9b201..c8c621b7d 100644
--- a/pkg/support/dump.go
+++ b/pkg/support/dump.go
@@ -281,6 +281,8 @@ type agentDumper struct {
 	aq           agentquerier.AgentQuerier
 	npq          querier.AgentNetworkPolicyInfoQuerier
 	since        string
+	v4Enabled    bool
+	v6Enabled    bool
 }
 
 func (d *agentDumper) DumpAgentInfo(basedir string) error {
@@ -326,7 +328,7 @@ func (d *agentDumper) DumpOVSPorts(basedir string) error {
 	return writeFile(d.fs, filepath.Join(basedir, "ovsports"), "ports", []byte(strings.Join(portData, "\n")))
 }
 
-func NewAgentDumper(fs afero.Fs, executor exec.Interface, ovsCtlClient ovsctl.OVSCtlClient, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier, since string) AgentDumper {
+func NewAgentDumper(fs afero.Fs, executor exec.Interface, ovsCtlClient ovsctl.OVSCtlClient, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier, since string, v4Enabled, v6Enabled bool) AgentDumper {
 	return &agentDumper{
 		fs:           fs,
 		executor:     executor,
@@ -334,5 +336,7 @@ func NewAgentDumper(fs afero.Fs, executor exec.Interface, ovsCtlClient ovsctl.OV
 		aq:           aq,
 		npq:          npq,
 		since:        since,
+		v4Enabled:    v4Enabled,
+		v6Enabled:    v6Enabled,
 	}
 }
diff --git a/pkg/support/dump_others.go b/pkg/support/dump_others.go
index 36c81b700..b3b9e061c 100644
--- a/pkg/support/dump_others.go
+++ b/pkg/support/dump_others.go
@@ -22,7 +22,6 @@ import (
 	"path"
 	"path/filepath"
 
-	"antrea.io/antrea/pkg/agent/config"
 	"antrea.io/antrea/pkg/agent/util/iptables"
 	"antrea.io/antrea/pkg/util/logdir"
 )
@@ -48,11 +47,7 @@ func (d *agentDumper) DumpHostNetworkInfo(basedir string) error {
 }
 
 func (d *agentDumper) dumpIPTables(basedir string) error {
-	nodeConfig := d.aq.GetNodeConfig()
-	networkConfig := d.aq.GetNetworkConfig()
-	v4Enabled := config.IsIPv4Enabled(nodeConfig, networkConfig.TrafficEncapMode)
-	v6Enabled := config.IsIPv6Enabled(nodeConfig, networkConfig.TrafficEncapMode)
-	c, err := iptables.New(v4Enabled, v6Enabled)
+	c, err := iptables.New(d.v4Enabled, d.v6Enabled)
 	if err != nil {
 		return err
 	}


Test patch:
diff --git a/pkg/agent/controller/networkpolicy/fqdn_test.go b/pkg/agent/controller/networkpolicy/fqdn_test.go
index 3abe00ce2..273520d3a 100644
--- a/pkg/agent/controller/networkpolicy/fqdn_test.go
+++ b/pkg/agent/controller/networkpolicy/fqdn_test.go
@@ -29,8 +29,6 @@ import (
 
 func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServer *string) (*fqdnController, *openflowtest.MockClient) {
 	mockOFClient := openflowtest.NewMockClient(controller)
-	mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-	mockOFClient.EXPECT().IsIPv6Enabled().Return(false).AnyTimes()
 	mockOFClient.EXPECT().NewDNSpacketInConjunction(gomock.Any()).Return(nil).AnyTimes()
 	dirtyRuleHandler := func(rule string) {}
 	dnsServerAddr := "8.8.8.8:53" // dummy DNS server, will not be used since we don't send any request in these tests
@@ -42,6 +40,8 @@ func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServe
 		newIDAllocator(testAsyncDeleteInterval),
 		dnsServerAddr,
 		dirtyRuleHandler,
+		true,
+		false,
 	)
 	require.NoError(t, err)
 	return f, mockOFClient
diff --git a/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go b/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go
index 545fb338e..09fff89c3 100644
--- a/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go
+++ b/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go
@@ -56,7 +56,7 @@ func newTestController() (*Controller, *fake.Clientset, *mockReconciler) {
 	ch2 := make(chan string, 100)
 	groupCounters := []proxytypes.GroupCounter{proxytypes.NewGroupCounter(false, ch2)}
 	controller, _ := NewNetworkPolicyController(&antreaClientGetter{clientset}, nil, nil, "node1", ch, groupCounters, ch2,
-		true, true, true, true, testAsyncDeleteInterval, "8.8.8.8:53")
+		true, true, true, true, testAsyncDeleteInterval, "8.8.8.8:53", true, false)
 	reconciler := newMockReconciler()
 	controller.reconciler = reconciler
 	controller.antreaPolicyLogger = nil
diff --git a/pkg/agent/controller/networkpolicy/reconciler_test.go b/pkg/agent/controller/networkpolicy/reconciler_test.go
index ac4fc0366..4ffd07571 100644
--- a/pkg/agent/controller/networkpolicy/reconciler_test.go
+++ b/pkg/agent/controller/networkpolicy/reconciler_test.go
@@ -98,11 +98,11 @@ func newCIDR(cidrStr string) *net.IPNet {
 	return tmpIPNet
 }
 
-func newTestReconciler(t *testing.T, controller *gomock.Controller, ifaceStore interfacestore.InterfaceStore, ofClient *openflowtest.MockClient) *reconciler {
+func newTestReconciler(t *testing.T, controller *gomock.Controller, ifaceStore interfacestore.InterfaceStore, ofClient *openflowtest.MockClient, v4Enabled, v6Enabled bool) *reconciler {
 	f, _ := newMockFQDNController(t, controller, nil)
 	ch := make(chan string, 100)
 	groupCounters := []proxytypes.GroupCounter{proxytypes.NewGroupCounter(false, ch)}
-	r := newReconciler(ofClient, ifaceStore, newIDAllocator(testAsyncDeleteInterval), f, groupCounters)
+	r := newReconciler(ofClient, ifaceStore, newIDAllocator(testAsyncDeleteInterval), f, groupCounters, v4Enabled, v6Enabled)
 	return r
 }
 
@@ -177,8 +177,6 @@ func TestReconcilerForget(t *testing.T) {
 			defer controller.Finish()
 			ifaceStore := interfacestore.NewInterfaceStore()
 			mockOFClient := openflowtest.NewMockClient(controller)
-			mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-			mockOFClient.EXPECT().IsIPv6Enabled().Return(false).AnyTimes()
 			if len(tt.expectedOFRuleIDs) == 0 {
 				mockOFClient.EXPECT().UninstallPolicyRuleFlows(gomock.Any()).Times(0)
 			} else {
@@ -186,7 +184,7 @@ func TestReconcilerForget(t *testing.T) {
 					mockOFClient.EXPECT().UninstallPolicyRuleFlows(ofID)
 				}
 			}
-			r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+			r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, false)
 			for key, value := range tt.lastRealizeds {
 				r.lastRealizeds.Store(key, value)
 			}
@@ -524,13 +522,11 @@ func TestReconcilerReconcile(t *testing.T) {
 			controller := gomock.NewController(t)
 			defer controller.Finish()
 			mockOFClient := openflowtest.NewMockClient(controller)
-			mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-			mockOFClient.EXPECT().IsIPv6Enabled().Return(false).AnyTimes()
 			// TODO: mock idAllocator and priorityAssigner
 			for i := 0; i < len(tt.expectedOFRules); i++ {
 				mockOFClient.EXPECT().InstallPolicyRuleFlows(gomock.Any())
 			}
-			r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+			r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, false)
 			if err := r.Reconcile(tt.args); (err != nil) != tt.wantErr {
 				t.Fatalf("Reconcile() error = %v, wantErr %v", err, tt.wantErr)
 			}
@@ -589,9 +585,7 @@ func TestReconcileWithTransientError(t *testing.T) {
 	controller := gomock.NewController(t)
 	defer controller.Finish()
 	mockOFClient := openflowtest.NewMockClient(controller)
-	mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-	mockOFClient.EXPECT().IsIPv6Enabled().Return(true).AnyTimes()
-	r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+	r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, true)
 	// Set deleteInterval to verify openflow ID is released immediately.
 	r.idAllocator.deleteInterval = 0
 
@@ -750,9 +744,7 @@ func TestReconcilerBatchReconcile(t *testing.T) {
 			controller := gomock.NewController(t)
 			defer controller.Finish()
 			mockOFClient := openflowtest.NewMockClient(controller)
-			mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-			mockOFClient.EXPECT().IsIPv6Enabled().Return(false).AnyTimes()
-			r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+			r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, true)
 			if tt.numInstalledRules > 0 {
 				// BatchInstall should skip rules already installed
 				r.lastRealizeds.Store(tt.args[0].ID, newLastRealized(tt.args[0]))
@@ -947,8 +939,6 @@ func TestReconcilerUpdate(t *testing.T) {
 			defer controller.Finish()
 			mockOFClient := openflowtest.NewMockClient(controller)
 			mockOFClient.EXPECT().InstallPolicyRuleFlows(gomock.Any()).MaxTimes(2)
-			mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-			mockOFClient.EXPECT().IsIPv6Enabled().Return(true).AnyTimes()
 			priority := gomock.Any()
 			if !tt.originalRule.isAntreaNetworkPolicyRule() {
 				priority = nil
@@ -968,7 +958,7 @@ func TestReconcilerUpdate(t *testing.T) {
 			if len(tt.expectedDeletedTo) > 0 {
 				mockOFClient.EXPECT().DeletePolicyRuleAddress(gomock.Any(), types.DstAddress, gomock.Eq(tt.expectedDeletedTo), priority)
 			}
-			r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+			r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, true)
 			if err := r.Reconcile(tt.originalRule); (err != nil) != tt.wantErr {
 				t.Fatalf("Reconcile() error = %v, wantErr %v", err, tt.wantErr)
 			}
@@ -1468,13 +1458,11 @@ func TestReconcilerReconcileIPv6Only(t *testing.T) {
 			controller := gomock.NewController(t)
 			defer controller.Finish()
 			mockOFClient := openflowtest.NewMockClient(controller)
-			mockOFClient.EXPECT().IsIPv4Enabled().Return(false).AnyTimes()
-			mockOFClient.EXPECT().IsIPv6Enabled().Return(true).AnyTimes()
 			// TODO: mock idAllocator and priorityAssigner
 			for i := 0; i < len(tt.expectedOFRules); i++ {
 				mockOFClient.EXPECT().InstallPolicyRuleFlows(gomock.Any())
 			}
-			r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+			r := newTestReconciler(t, controller, ifaceStore, mockOFClient, false, true)
 			if err := r.Reconcile(tt.args); (err != nil) != tt.wantErr {
 				t.Fatalf("Reconcile() error = %v, wantErr %v", err, tt.wantErr)
 			}
@@ -1871,13 +1859,11 @@ func TestReconcilerReconcileDualStack(t *testing.T) {
 			controller := gomock.NewController(t)
 			defer controller.Finish()
 			mockOFClient := openflowtest.NewMockClient(controller)
-			mockOFClient.EXPECT().IsIPv4Enabled().Return(true).AnyTimes()
-			mockOFClient.EXPECT().IsIPv6Enabled().Return(true).AnyTimes()
 			// TODO: mock idAllocator and priorityAssigner
 			for i := 0; i < len(tt.expectedOFRules); i++ {
 				mockOFClient.EXPECT().InstallPolicyRuleFlows(gomock.Any())
 			}
-			r := newTestReconciler(t, controller, ifaceStore, mockOFClient)
+			r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, true)
 			if err := r.Reconcile(tt.args); (err != nil) != tt.wantErr {
 				t.Fatalf("Reconcile() error = %v, wantErr %v", err, tt.wantErr)
 			}
diff --git a/pkg/agent/openflow/network_policy_test.go b/pkg/agent/openflow/network_policy_test.go
index d1c983d44..4b547fa9f 100644
--- a/pkg/agent/openflow/network_policy_test.go
+++ b/pkg/agent/openflow/network_policy_test.go
@@ -156,7 +156,7 @@ func TestInstallPolicyRuleFlows(t *testing.T) {
 
 	c = prepareClient(ctrl)
 	c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil}
-	c.networkConfig = &config.NetworkConfig{}
+	c.networkConfig = &config.NetworkConfig{IPv4Enabled: true}
 	c.ipProtocols = []binding.Protocol{binding.ProtocolIP}
 	defaultAction := crdv1alpha1.RuleActionAllow
 	ruleID1 := uint32(101)
@@ -512,7 +512,7 @@ func TestBatchInstallPolicyRuleFlows(t *testing.T) {
 			c.cookieAllocator = cookie.NewAllocator(0)
 			c.ofEntryOperations = mockOperations
 			c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil}
-			c.networkConfig = &config.NetworkConfig{}
+			c.networkConfig = &config.NetworkConfig{IPv4Enabled: true}
 			c.ipProtocols = []binding.Protocol{binding.ProtocolIP}
 			c.deterministic = true
 
@@ -638,7 +638,7 @@ func TestInstallPolicyRuleFlowsInDualStackCluster(t *testing.T) {
 
 	c = prepareClient(ctrl)
 	c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR}
-	c.networkConfig = &config.NetworkConfig{}
+	c.networkConfig = &config.NetworkConfig{IPv4Enabled: true, IPv6Enabled: true}
 	c.ipProtocols = []binding.Protocol{binding.ProtocolIP, binding.ProtocolIPv6}
 	defaultAction := crdv1alpha1.RuleActionAllow
 	ruleID1 := uint32(101)
@@ -1114,7 +1114,7 @@ func TestGetMatchFlowUpdates(t *testing.T) {
 
 	c = prepareClient(ctrl)
 	c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil}
-	c.networkConfig = &config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap}
+	c.networkConfig = &config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap, IPv4Enabled: true}
 	c.ipProtocols = []binding.Protocol{binding.ProtocolIP}
 	outDropTable.EXPECT().BuildFlow(gomock.Any()).Return(newMockDropFlowBuilder(ctrl)).AnyTimes()
 	cnpOutTable.EXPECT().BuildFlow(gomock.Any()).Return(newMockRuleFlowBuilder(ctrl)).AnyTimes()
diff --git a/pkg/agent/openflow/testing/mock_openflow.go b/pkg/agent/openflow/testing/mock_openflow.go
index 7647ebe10..ab49be308 100644
--- a/pkg/agent/openflow/testing/mock_openflow.go
+++ b/pkg/agent/openflow/testing/mock_openflow.go
@@ -533,34 +533,6 @@ func (mr *MockClientMockRecorder) IsConnected() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsConnected", reflect.TypeOf((*MockClient)(nil).IsConnected))
 }
 
-// IsIPv4Enabled mocks base method
-func (m *MockClient) IsIPv4Enabled() bool {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsIPv4Enabled")
-	ret0, _ := ret[0].(bool)
-	return ret0
-}
-
-// IsIPv4Enabled indicates an expected call of IsIPv4Enabled
-func (mr *MockClientMockRecorder) IsIPv4Enabled() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsIPv4Enabled", reflect.TypeOf((*MockClient)(nil).IsIPv4Enabled))
-}
-
-// IsIPv6Enabled mocks base method
-func (m *MockClient) IsIPv6Enabled() bool {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsIPv6Enabled")
-	ret0, _ := ret[0].(bool)
-	return ret0
-}
-
-// IsIPv6Enabled indicates an expected call of IsIPv6Enabled
-func (mr *MockClientMockRecorder) IsIPv6Enabled() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsIPv6Enabled", reflect.TypeOf((*MockClient)(nil).IsIPv6Enabled))
-}
-
 // NetworkPolicyMetrics mocks base method
 func (m *MockClient) NetworkPolicyMetrics() map[uint32]*types.RuleMetric {
 	m.ctrl.T.Helper()
diff --git a/test/integration/agent/openflow_test.go b/test/integration/agent/openflow_test.go
index a2f1c30db..e4ddef735 100644
--- a/test/integration/agent/openflow_test.go
+++ b/test/integration/agent/openflow_test.go
@@ -329,7 +329,7 @@ func testReplayFlows(t *testing.T) {
 }
 
 func testInitialize(t *testing.T, config *testConfig) {
-	if _, err := c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}); err != nil {
+	if _, err := c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap, IPv4Enabled: config.enableIPv4, IPv6Enabled: config.enableIPv6}); err != nil {
 		t.Errorf("Failed to initialize openflow client: %v", err)
 	}
 	for _, tableFlow := range prepareDefaultFlows(config) {
@@ -427,7 +427,7 @@ func TestNetworkPolicyFlows(t *testing.T) {
 	err := ofTestUtils.PrepareOVSBridge(br)
 	require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br))
 
-	_, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR, GatewayConfig: gwConfig}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap})
+	_, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR, GatewayConfig: gwConfig}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap, IPv4Enabled: true, IPv6Enabled: true})
 	require.Nil(t, err, "Failed to initialize OFClient")
 
 	defer func() {
@@ -579,7 +579,7 @@ func TestProxyServiceFlows(t *testing.T) {
 	err := ofTestUtils.PrepareOVSBridge(br)
 	require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br))
 
-	_, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR, GatewayConfig: gwConfig}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap})
+	_, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR, GatewayConfig: gwConfig}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap, IPv4Enabled: true})
 	require.Nil(t, err, "Failed to initialize OFClient")
 
 	defer func() {
@@ -1519,7 +1519,7 @@ func TestSNATFlows(t *testing.T) {
 	require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br))
 
 	config := prepareConfiguration()
-	_, err = c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap})
+	_, err = c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap, IPv4Enabled: true, IPv6Enabled: true})
 	require.Nil(t, err, "Failed to initialize OFClient")
 
 	defer func() {
diff --git a/test/integration/agent/route_test.go b/test/integration/agent/route_test.go
index d5c30245b..ab72879d1 100644
--- a/test/integration/agent/route_test.go
+++ b/test/integration/agent/route_test.go
@@ -109,6 +109,7 @@ func TestInitialize(t *testing.T) {
 		{
 			networkConfig: &config.NetworkConfig{
 				TrafficEncapMode: config.TrafficEncapModeNoEncap,
+				IPv4Enabled:      true,
 			},
 			expectNoTrackRules: false,
 		},
@@ -116,6 +117,7 @@ func TestInitialize(t *testing.T) {
 			networkConfig: &config.NetworkConfig{
 				TrafficEncapMode: config.TrafficEncapModeHybrid,
 				TunnelType:       ovsconfig.GeneveTunnel,
+				IPv4Enabled:      true,
 			},
 			noSNAT:               true,
 			expectNoTrackRules:   true,
@@ -125,6 +127,7 @@ func TestInitialize(t *testing.T) {
 			networkConfig: &config.NetworkConfig{
 				TrafficEncapMode: config.TrafficEncapModeEncap,
 				TunnelType:       ovsconfig.VXLANTunnel,
+				IPv4Enabled:      true,
 			},
 			expectNoTrackRules:   true,
 			expectUDPPortInRules: 4789,
@@ -132,6 +135,7 @@ func TestInitialize(t *testing.T) {
 		{
 			networkConfig: &config.NetworkConfig{
 				TrafficEncapMode: config.TrafficEncapModeNoEncap,
+				IPv4Enabled:      true,
 			},
 			xtablesHoldDuration: 5 * time.Second,
 			expectNoTrackRules:  false,
@@ -246,7 +250,7 @@ func TestIpTablesSync(t *testing.T) {
 	gwLink := createDummyGW(t)
 	defer netlink.LinkDel(gwLink)
 
-	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap}, false, false, false, false)
+	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap, IPv4Enabled: true}, false, false, false, false)
 	assert.Nil(t, err)
 
 	inited := make(chan struct{})
@@ -297,7 +301,7 @@ func TestAddAndDeleteSNATRule(t *testing.T) {
 	gwLink := createDummyGW(t)
 	defer netlink.LinkDel(gwLink)
 
-	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap}, false, false, false, false)
+	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap, IPv4Enabled: true}, false, false, false, false)
 	assert.Nil(t, err)
 
 	inited := make(chan struct{})
@@ -351,7 +355,7 @@ func TestAddAndDeleteRoutes(t *testing.T) {
 
 	for _, tc := range tcs {
 		t.Logf("Running test with mode %s peer cidr %s peer ip %s node config %s", tc.mode, tc.peerCIDR, tc.peerIP, nodeConfig)
-		routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: tc.mode}, false, false, false, false)
+		routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: tc.mode, IPv4Enabled: true}, false, false, false, false)
 		assert.NoError(t, err)
 		err = routeClient.Initialize(nodeConfig, func() {})
 		assert.NoError(t, err)
@@ -414,7 +418,7 @@ func TestSyncRoutes(t *testing.T) {
 
 	for _, tc := range tcs {
 		t.Logf("Running test with mode %s peer cidr %s peer ip %s node config %s", tc.mode, tc.peerCIDR, tc.peerIP, nodeConfig)
-		routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: tc.mode}, false, false, false, false)
+		routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: tc.mode, IPv4Enabled: true}, false, false, false, false)
 		assert.NoError(t, err)
 		err = routeClient.Initialize(nodeConfig, func() {})
 		assert.NoError(t, err)
@@ -555,7 +559,7 @@ func TestReconcile(t *testing.T) {
 
 	for _, tc := range tcs {
 		t.Logf("Running test with mode %s added routes %v desired routes %v", tc.mode, tc.addedRoutes, tc.desiredPeerCIDRs)
-		routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: tc.mode}, false, false, false, false)
+		routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: tc.mode, IPv4Enabled: true}, false, false, false, false)
 		assert.NoError(t, err)
 		err = routeClient.Initialize(nodeConfig, func() {})
 		assert.NoError(t, err)
@@ -593,7 +597,7 @@ func TestRouteTablePolicyOnly(t *testing.T) {
 	gwLink := createDummyGW(t)
 	defer netlink.LinkDel(gwLink)
 
-	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeNetworkPolicyOnly}, false, false, false, false)
+	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeNetworkPolicyOnly, IPv4Enabled: true}, false, false, false, false)
 	assert.NoError(t, err)
 	err = routeClient.Initialize(nodeConfig, func() {})
 	assert.NoError(t, err)
@@ -649,7 +653,7 @@ func TestIPv6RoutesAndNeighbors(t *testing.T) {
 	gwLink := createDummyGW(t)
 	defer netlink.LinkDel(gwLink)
 
-	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap}, false, false, false, false)
+	routeClient, err := route.NewClient(&config.NetworkConfig{TrafficEncapMode: config.TrafficEncapModeEncap, IPv4Enabled: true, IPv6Enabled: true}, false, false, false, false)
 	assert.Nil(t, err)
 	_, ipv6Subnet, _ := net.ParseCIDR("fd74:ca9b:172:19::/64")
 	gwIPv6 := net.ParseIP("fd74:ca9b:172:19::1")