Repository: antrea-io/antrea
Pull request #2191 (2021-05-19 11:07:37)

Move GetNodeAddr function to k8s util package
The function is consumed by antctl and several places in antrea-agent. Importing it from "pkg/agent/controller/noderoute" is weird and complicate the module dependency. This patch moves it to k8s util package which contains K8s specific helper functions. With it, antctl binary size is reduced about 8KB.

Besides, it moves pkg/k8s to pkg/util/k8s.

Signed-off-by: Quan Tian <qtian@vmware.com>

codecov-commenter: # [Codecov](https://codecov.io/gh/antrea-io/antrea/pull/2191?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#2191](https://codecov.io/gh/antrea-io/antrea/pull/2191?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (f6599bb) into [main](https://codecov.io/gh/antrea-io/antrea/commit/a9c77449f2db0458f5c686a3bcc47f0ee72c7419?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (a9c7744) will **decrease** coverage by `20.37%`.
> The diff coverage is `75.00%`.

[![Impacted file tree graph](https://codecov.io/gh/antrea-io/antrea/pull/2191/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/2191?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

```diff
@@             Coverage Diff             @@
##             main    #2191       +/-   ##
===========================================
- Coverage   61.30%   40.92%   -20.38%     
===========================================
  Files         273      137      -136     
  Lines       20646    16741     -3905     
===========================================
- Hits        12656     6852     -5804     
- Misses       6688     9303     +2615     
+ Partials     1302      586      -716     
```

| Flag | Coverage Δ | |
|---|---|---|
| kind-e2e-tests | `?` | |
| unit-tests | `40.92% <75.00%> (-0.11%)` | :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/2191?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [pkg/agent/agent.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2FnZW50Lmdv) | `11.16% <0.00%> (-36.58%)` | :arrow_down: |
| [...gent/controller/noderoute/node\_route\_controller.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2NvbnRyb2xsZXIvbm9kZXJvdXRlL25vZGVfcm91dGVfY29udHJvbGxlci5nbw==) | `33.45% <50.00%> (-12.94%)` | :arrow_down: |
| [pkg/k8s/node.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2s4cy9ub2RlLmdv) | `84.61% <84.61%> (ø)` | |
| [pkg/k8s/name.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2s4cy9uYW1lLmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/agent/agent\_linux.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2FnZW50X2xpbnV4Lmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/ovs/openflow/default.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/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/agent/config/node\_config.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2NvbmZpZy9ub2RlX2NvbmZpZy5nbw==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/apis/controlplane/helper.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FwaXMvY29udHJvbHBsYW5lL2hlbHBlci5nbw==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/agent/cniserver/pod\_configuration\_linux.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FnZW50L2NuaXNlcnZlci9wb2RfY29uZmlndXJhdGlvbl9saW51eC5nbw==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/apis/controlplane/v1beta1/register.go](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2FwaXMvY29udHJvbHBsYW5lL3YxYmV0YTEvcmVnaXN0ZXIuZ28=) | `0.00% <0.00%> (-92.86%)` | :arrow_down: |
| ... and [233 more](https://codecov.io/gh/antrea-io/antrea/pull/2191/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | |

tnqn: /test-all
tnqn: @antoninbas thanks for the review.

> I wonder if `k8s` should be kept as a top-level package, or should be moved to `pkg/util/k8s`

Moving to `pkg/util/k8s` is fine by me, I have made the change.


tnqn: /test-all

Patch:
diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml
index 7424edfe7..5214daa3f 100644
--- a/build/yamls/antrea-aks.yml
+++ b/build/yamls/antrea-aks.yml
@@ -3617,9 +3617,10 @@ data:
     # to the Antrea Flow Aggregator service. If IP, it can be either IPv4 or IPv6.
     # However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -3640,9 +3641,6 @@ data:
     # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
     #idleFlowExportTimeout: "15s"
 
-    # Enable TLS communication from flow exporter to flow aggregator.
-    #enableTLSToFlowAggregator: true
-
     # Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
     # whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
     # and all Node traffic directed to that port will be forwarded to the Pod.
@@ -3742,7 +3740,7 @@ metadata:
   annotations: {}
   labels:
     app: antrea
-  name: antrea-config-kt9gdmf62t
+  name: antrea-config-t9hc8tf75d
   namespace: kube-system
 ---
 apiVersion: v1
@@ -3862,7 +3860,7 @@ spec:
         key: node-role.kubernetes.io/master
       volumes:
       - configMap:
-          name: antrea-config-kt9gdmf62t
+          name: antrea-config-t9hc8tf75d
         name: antrea-config
       - name: antrea-controller-tls
         secret:
@@ -4173,7 +4171,7 @@ spec:
         operator: Exists
       volumes:
       - configMap:
-          name: antrea-config-kt9gdmf62t
+          name: antrea-config-t9hc8tf75d
         name: antrea-config
       - hostPath:
           path: /etc/cni/net.d
diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml
index 78c207004..c72314d7e 100644
--- a/build/yamls/antrea-eks.yml
+++ b/build/yamls/antrea-eks.yml
@@ -3617,9 +3617,10 @@ data:
     # to the Antrea Flow Aggregator service. If IP, it can be either IPv4 or IPv6.
     # However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -3640,9 +3641,6 @@ data:
     # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
     #idleFlowExportTimeout: "15s"
 
-    # Enable TLS communication from flow exporter to flow aggregator.
-    #enableTLSToFlowAggregator: true
-
     # Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
     # whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
     # and all Node traffic directed to that port will be forwarded to the Pod.
@@ -3742,7 +3740,7 @@ metadata:
   annotations: {}
   labels:
     app: antrea
-  name: antrea-config-kt9gdmf62t
+  name: antrea-config-t9hc8tf75d
   namespace: kube-system
 ---
 apiVersion: v1
@@ -3862,7 +3860,7 @@ spec:
         key: node-role.kubernetes.io/master
       volumes:
       - configMap:
-          name: antrea-config-kt9gdmf62t
+          name: antrea-config-t9hc8tf75d
         name: antrea-config
       - name: antrea-controller-tls
         secret:
@@ -4175,7 +4173,7 @@ spec:
         operator: Exists
       volumes:
       - configMap:
-          name: antrea-config-kt9gdmf62t
+          name: antrea-config-t9hc8tf75d
         name: antrea-config
       - hostPath:
           path: /etc/cni/net.d
diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml
index d9eba3e58..4ba925d12 100644
--- a/build/yamls/antrea-gke.yml
+++ b/build/yamls/antrea-gke.yml
@@ -3617,9 +3617,10 @@ data:
     # to the Antrea Flow Aggregator service. If IP, it can be either IPv4 or IPv6.
     # However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -3640,9 +3641,6 @@ data:
     # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
     #idleFlowExportTimeout: "15s"
 
-    # Enable TLS communication from flow exporter to flow aggregator.
-    #enableTLSToFlowAggregator: true
-
     # Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
     # whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
     # and all Node traffic directed to that port will be forwarded to the Pod.
@@ -3742,7 +3740,7 @@ metadata:
   annotations: {}
   labels:
     app: antrea
-  name: antrea-config-c8bf7gddbb
+  name: antrea-config-9g829tktd6
   namespace: kube-system
 ---
 apiVersion: v1
@@ -3862,7 +3860,7 @@ spec:
         key: node-role.kubernetes.io/master
       volumes:
       - configMap:
-          name: antrea-config-c8bf7gddbb
+          name: antrea-config-9g829tktd6
         name: antrea-config
       - name: antrea-controller-tls
         secret:
@@ -4176,7 +4174,7 @@ spec:
           path: /home/kubernetes/bin
         name: host-cni-bin
       - configMap:
-          name: antrea-config-c8bf7gddbb
+          name: antrea-config-9g829tktd6
         name: antrea-config
       - hostPath:
           path: /etc/cni/net.d
diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml
index 0472cbde9..a4001f5c0 100644
--- a/build/yamls/antrea-ipsec.yml
+++ b/build/yamls/antrea-ipsec.yml
@@ -3622,9 +3622,10 @@ data:
     # to the Antrea Flow Aggregator service. If IP, it can be either IPv4 or IPv6.
     # However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -3645,9 +3646,6 @@ data:
     # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
     #idleFlowExportTimeout: "15s"
 
-    # Enable TLS communication from flow exporter to flow aggregator.
-    #enableTLSToFlowAggregator: true
-
     # Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
     # whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
     # and all Node traffic directed to that port will be forwarded to the Pod.
@@ -3747,7 +3745,7 @@ metadata:
   annotations: {}
   labels:
     app: antrea
-  name: antrea-config-dh7f7g969b
+  name: antrea-config-h5kbhh859d
   namespace: kube-system
 ---
 apiVersion: v1
@@ -3876,7 +3874,7 @@ spec:
         key: node-role.kubernetes.io/master
       volumes:
       - configMap:
-          name: antrea-config-dh7f7g969b
+          name: antrea-config-h5kbhh859d
         name: antrea-config
       - name: antrea-controller-tls
         secret:
@@ -4222,7 +4220,7 @@ spec:
         operator: Exists
       volumes:
       - configMap:
-          name: antrea-config-dh7f7g969b
+          name: antrea-config-h5kbhh859d
         name: antrea-config
       - hostPath:
           path: /etc/cni/net.d
diff --git a/build/yamls/antrea-windows.yml b/build/yamls/antrea-windows.yml
index d6d79e548..f3a512676 100644
--- a/build/yamls/antrea-windows.yml
+++ b/build/yamls/antrea-windows.yml
@@ -60,9 +60,10 @@ data:
     # HOST can only be IP right now because there is a DNS resolution issue in current Windows support.
     # IP can be either IPv4 or IPv6. However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: ""
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -103,7 +104,7 @@ kind: ConfigMap
 metadata:
   labels:
     app: antrea
-  name: antrea-windows-config-cm7h2cd86m
+  name: antrea-windows-config-6cmd972m6b
   namespace: kube-system
 ---
 apiVersion: apps/v1
@@ -191,7 +192,7 @@ spec:
         operator: Exists
       volumes:
       - configMap:
-          name: antrea-windows-config-cm7h2cd86m
+          name: antrea-windows-config-6cmd972m6b
         name: antrea-windows-config
       - configMap:
           defaultMode: 420
diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml
index 126159898..5620778eb 100644
--- a/build/yamls/antrea.yml
+++ b/build/yamls/antrea.yml
@@ -3622,9 +3622,10 @@ data:
     # to the Antrea Flow Aggregator service. If IP, it can be either IPv4 or IPv6.
     # However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -3645,9 +3646,6 @@ data:
     # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
     #idleFlowExportTimeout: "15s"
 
-    # Enable TLS communication from flow exporter to flow aggregator.
-    #enableTLSToFlowAggregator: true
-
     # Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
     # whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
     # and all Node traffic directed to that port will be forwarded to the Pod.
@@ -3747,7 +3745,7 @@ metadata:
   annotations: {}
   labels:
     app: antrea
-  name: antrea-config-42cft4gc5f
+  name: antrea-config-cbfh568k9m
   namespace: kube-system
 ---
 apiVersion: v1
@@ -3867,7 +3865,7 @@ spec:
         key: node-role.kubernetes.io/master
       volumes:
       - configMap:
-          name: antrea-config-42cft4gc5f
+          name: antrea-config-cbfh568k9m
         name: antrea-config
       - name: antrea-controller-tls
         secret:
@@ -4178,7 +4176,7 @@ spec:
         operator: Exists
       volumes:
       - configMap:
-          name: antrea-config-42cft4gc5f
+          name: antrea-config-cbfh568k9m
         name: antrea-config
       - hostPath:
           path: /etc/cni/net.d
diff --git a/build/yamls/base/conf/antrea-agent.conf b/build/yamls/base/conf/antrea-agent.conf
index 3bf7a3c57..01591e983 100644
--- a/build/yamls/base/conf/antrea-agent.conf
+++ b/build/yamls/base/conf/antrea-agent.conf
@@ -106,9 +106,10 @@ featureGates:
 # to the Antrea Flow Aggregator service. If IP, it can be either IPv4 or IPv6.
 # However, IPv6 address should be wrapped with [].
 # If PORT is empty, we default to 4739, the standard IPFIX port.
-# If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-# L4 transport protocols.
-#flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+# If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+# "udp" protocols. "tls" is used for securing communication between flow exporter and
+# flow aggregator.
+#flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
 # Provide flow poll interval as a duration string. This determines how often the
 # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -129,9 +130,6 @@ featureGates:
 # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
 #idleFlowExportTimeout: "15s"
 
-# Enable TLS communication from flow exporter to flow aggregator.
-#enableTLSToFlowAggregator: true
-
 # Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
 # whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
 # and all Node traffic directed to that port will be forwarded to the Pod.
diff --git a/build/yamls/windows/base/conf/antrea-agent.conf b/build/yamls/windows/base/conf/antrea-agent.conf
index 7dd2807cc..a0a7af490 100644
--- a/build/yamls/windows/base/conf/antrea-agent.conf
+++ b/build/yamls/windows/base/conf/antrea-agent.conf
@@ -42,9 +42,10 @@ featureGates:
 # HOST can only be IP right now because there is a DNS resolution issue in current Windows support.
 # IP can be either IPv4 or IPv6. However, IPv6 address should be wrapped with [].
 # If PORT is empty, we default to 4739, the standard IPFIX port.
-# If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-# L4 transport protocols.
-#flowCollectorAddr: ""
+# If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+# "udp" protocols. "tls" is used for securing communication between flow exporter and
+# flow aggregator.
+#flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
 
 # Provide flow poll interval as a duration string. This determines how often the
 # flow exporter dumps connections from the conntrack module. Flow poll interval
diff --git a/cmd/antrea-agent-simulator/simulator.go b/cmd/antrea-agent-simulator/simulator.go
index 7f6db4da3..ba12eec28 100644
--- a/cmd/antrea-agent-simulator/simulator.go
+++ b/cmd/antrea-agent-simulator/simulator.go
@@ -30,9 +30,9 @@ import (
 	"k8s.io/klog/v2"
 
 	"antrea.io/antrea/pkg/agent"
-	"antrea.io/antrea/pkg/k8s"
 	"antrea.io/antrea/pkg/signals"
 	"antrea.io/antrea/pkg/util/env"
+	"antrea.io/antrea/pkg/util/k8s"
 	"antrea.io/antrea/pkg/version"
 )
 
diff --git a/cmd/antrea-agent/agent.go b/cmd/antrea-agent/agent.go
index 2f9562519..d372e486b 100644
--- a/cmd/antrea-agent/agent.go
+++ b/cmd/antrea-agent/agent.go
@@ -45,13 +45,13 @@ import (
 	"antrea.io/antrea/pkg/agent/types"
 	crdinformers "antrea.io/antrea/pkg/client/informers/externalversions"
 	"antrea.io/antrea/pkg/features"
-	"antrea.io/antrea/pkg/k8s"
 	"antrea.io/antrea/pkg/log"
 	"antrea.io/antrea/pkg/monitor"
 	ofconfig "antrea.io/antrea/pkg/ovs/openflow"
 	"antrea.io/antrea/pkg/ovs/ovsconfig"
 	"antrea.io/antrea/pkg/signals"
 	"antrea.io/antrea/pkg/util/cipher"
+	"antrea.io/antrea/pkg/util/k8s"
 	"antrea.io/antrea/pkg/version"
 )
 
@@ -375,7 +375,6 @@ func run(o *Options) error {
 			o.flowCollectorProto,
 			o.activeFlowTimeout,
 			o.idleFlowTimeout,
-			o.config.EnableTLSToFlowAggregator,
 			v4Enabled,
 			v6Enabled,
 			k8sClient,
diff --git a/cmd/antrea-agent/config.go b/cmd/antrea-agent/config.go
index 81395c8eb..9e3f13725 100644
--- a/cmd/antrea-agent/config.go
+++ b/cmd/antrea-agent/config.go
@@ -132,9 +132,6 @@ type AgentConfig struct {
 	// Defaults to "15s". Valid time units are "ns", "us" (or "µs"), "ms", "s",
 	// "m", "h".
 	IdleFlowExportTimeout string `yaml:"idleFlowExportTimeout,omitempty"`
-	// Enable TLS communication from flow exporter to flow aggregator.
-	// Defaults to true.
-	EnableTLSToFlowAggregator bool `yaml:"enableTLSToFlowAggregator,omitempty"`
 	// Provide the port range used by NodePortLocal. When the NodePortLocal feature is enabled, a port from that range will be assigned
 	// whenever a Pod's container defines a specific port to be exposed (each container can define a list of ports as pod.spec.containers[].ports),
 	// and all Node traffic directed to that port will be forwarded to the Pod.
diff --git a/cmd/antrea-agent/options.go b/cmd/antrea-agent/options.go
index e219e9061..7e4aa315d 100644
--- a/cmd/antrea-agent/options.go
+++ b/cmd/antrea-agent/options.go
@@ -38,8 +38,8 @@ const (
 	defaultHostProcPathPrefix      = "/host"
 	defaultServiceCIDR             = "10.96.0.0/12"
 	defaultTunnelType              = ovsconfig.GeneveTunnel
-	defaultFlowCollectorAddress    = "flow-aggregator.flow-aggregator.svc:4739:tcp"
-	defaultFlowCollectorTransport  = "tcp"
+	defaultFlowCollectorAddress    = "flow-aggregator.flow-aggregator.svc:4739:tls"
+	defaultFlowCollectorTransport  = "tls"
 	defaultFlowCollectorPort       = "4739"
 	defaultFlowPollInterval        = 5 * time.Second
 	defaultActiveFlowExportTimeout = 30 * time.Second
@@ -54,7 +54,7 @@ type Options struct {
 	config *AgentConfig
 	// IPFIX flow collector address
 	flowCollectorAddr string
-	// IPFIX flow collector L4 protocol
+	// IPFIX flow collector protocol
 	flowCollectorProto string
 	// Flow exporter poll interval
 	pollInterval time.Duration
@@ -67,8 +67,7 @@ type Options struct {
 func newOptions() *Options {
 	return &Options{
 		config: &AgentConfig{
-			EnablePrometheusMetrics:   true,
-			EnableTLSToFlowAggregator: true,
+			EnablePrometheusMetrics: true,
 		},
 	}
 }
diff --git a/cmd/antrea-controller/controller.go b/cmd/antrea-controller/controller.go
index 022ac55a8..8b8f18cde 100644
--- a/cmd/antrea-controller/controller.go
+++ b/cmd/antrea-controller/controller.go
@@ -49,13 +49,13 @@ import (
 	"antrea.io/antrea/pkg/controller/stats"
 	"antrea.io/antrea/pkg/controller/traceflow"
 	"antrea.io/antrea/pkg/features"
-	"antrea.io/antrea/pkg/k8s"
 	legacycrdinformers "antrea.io/antrea/pkg/legacyclient/informers/externalversions"
 	"antrea.io/antrea/pkg/log"
 	"antrea.io/antrea/pkg/monitor"
 	"antrea.io/antrea/pkg/signals"
 	"antrea.io/antrea/pkg/util/cipher"
 	"antrea.io/antrea/pkg/util/env"
+	"antrea.io/antrea/pkg/util/k8s"
 	"antrea.io/antrea/pkg/version"
 )
 
diff --git a/cmd/flow-aggregator/config.go b/cmd/flow-aggregator/config.go
index 8dda33cf0..a91e52f2c 100644
--- a/cmd/flow-aggregator/config.go
+++ b/cmd/flow-aggregator/config.go
@@ -41,7 +41,7 @@ type FlowAggregatorConfig struct {
 	AggregatorTransportProtocol flowaggregator.AggregatorTransportProtocol `yaml:"aggregatorTransportProtocol,omitempty"`
 	// Provide DNS name or IP address of flow aggregator for generating TLS certificate.
 	// Defaults to "flow-aggregator.flow-aggregator.svc"
-	flowAggregatorAddress string `yaml:"flowAggregatorAddress,omitempty"`
+	FlowAggregatorAddress string `yaml:"flowAggregatorAddress,omitempty"`
 	// Provide the 32-bit Observation Domain ID which will uniquely identify this instance of the flow
 	// aggregator to an external flow collector. If omitted, an Observation Domain ID will be generated
 	// from the persistent cluster UUID generated by Antrea. Failing that (e.g. because the cluster UUID
diff --git a/cmd/flow-aggregator/options.go b/cmd/flow-aggregator/options.go
index 0364aa4a4..4b1d17cf1 100644
--- a/cmd/flow-aggregator/options.go
+++ b/cmd/flow-aggregator/options.go
@@ -118,10 +118,10 @@ func (o *Options) validate(args []string) error {
 		}
 		o.aggregatorTransportProtocol = transportProtocol
 	}
-	if o.config.flowAggregatorAddress == "" {
+	if o.config.FlowAggregatorAddress == "" {
 		o.flowAggregatorAddress = defaultFlowAggregatorAddress
 	} else {
-		o.flowAggregatorAddress = o.config.flowAggregatorAddress
+		o.flowAggregatorAddress = o.config.FlowAggregatorAddress
 	}
 	return nil
 }
diff --git a/docs/network-flow-visibility.md b/docs/network-flow-visibility.md
index 9e99e60c3..90da63ccb 100644
--- a/docs/network-flow-visibility.md
+++ b/docs/network-flow-visibility.md
@@ -85,9 +85,10 @@ parameters have to be set in the Antrea Agent ConfigMap:
     # to the Antrea Flow Aggregator Service. If IP, it can be either IPv4 or IPv6.
     # However, IPv6 address should be wrapped with [].
     # If PORT is empty, we default to 4739, the standard IPFIX port.
-    # If no PROTO is given, we consider "tcp" as default. We support "tcp" and "udp"
-    # L4 transport protocols.
-    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tcp"
+    # If no PROTO is given, we consider "tls" as default. We support "tls", "tcp" and
+    # "udp" protocols. "tls" is used for securing communication between flow exporter and
+    # flow aggregator.
+    #flowCollectorAddr: "flow-aggregator.flow-aggregator.svc:4739:tls"
     
     # Provide flow poll interval as a duration string. This determines how often the
     # flow exporter dumps connections from the conntrack module. Flow poll interval
@@ -107,12 +108,9 @@ parameters have to be set in the Antrea Agent ConfigMap:
     # packet matching this flow has been observed since the last export event.
     # Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
     #idleFlowExportTimeout: "15s"
-
-    # Enable TLS communication from flow exporter to flow aggregator.
-    #enableTLSToFlowAggregator: true
 ```
 
-Please note that the default value for `flowCollectorAddr` is `"flow-aggregator.flow-aggregator.svc:4739:tcp"`,
+Please note that the default value for `flowCollectorAddr` is `"flow-aggregator.flow-aggregator.svc:4739:tls"`,
 which uses the DNS name of the Flow Aggregator Service, if the Service is deployed
 with the Name and Namespace set to `flow-aggregator`. For Antrea Agent running on
 a Windows node, the user is required to change the default value of `HOST` in `flowCollectorAddr`
@@ -279,11 +277,10 @@ flow-aggregator.conf: |
 
 Please note that the default values for `flowExportInterval`, `aggregatorTransportProtocol`,
 and `flowAggregatorAddress` parameters are set to `60s`, `tls` and `flow-aggregator.flow-aggregator.svc`,
-respectively. Please make sure that `aggregatorTransportProtocol` is set to `tls` and
-`enableTLSToFlowAggregator` in `agent-agent.conf` is set to true to guarantee secure communication
-works properly. `enableTLSToFlowAggregator` and `aggregatorTransportProtocol` must always match,
-so TLS must either be enabled for both sides or disabled for both sides. Please modify the parameters
-as per your requirements.
+respectively. Please make sure that `aggregatorTransportProtocol` and protocol of `flowCollectorAddr` in
+`agent-agent.conf` are set to `tls` to guarantee secure communication works properly. Protocol of
+`flowCollectorAddr` and `aggregatorTransportProtocol` must always match, so TLS must either be enabled for
+both sides or disabled for both sides. Please modify the parameters as per your requirements.
 
 ### IPFIX Information Elements (IEs) in an Aggregated Flow Record
 
diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go
index b99a0c790..d059c9b30 100644
--- a/pkg/agent/agent.go
+++ b/pkg/agent/agent.go
@@ -43,6 +43,7 @@ import (
 	"antrea.io/antrea/pkg/agent/util"
 	"antrea.io/antrea/pkg/ovs/ovsconfig"
 	"antrea.io/antrea/pkg/util/env"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
@@ -641,7 +642,7 @@ func (i *Initializer) initNodeLocalConfig() error {
 		return err
 	}
 
-	ipAddr, err := noderoute.GetNodeAddr(node)
+	ipAddr, err := k8s.GetNodeAddr(node)
 	if err != nil {
 		return fmt.Errorf("failed to obtain local IP address from K8s: %w", err)
 	}
diff --git a/pkg/agent/agent_windows.go b/pkg/agent/agent_windows.go
index 2b93af516..cc8ace704 100644
--- a/pkg/agent/agent_windows.go
+++ b/pkg/agent/agent_windows.go
@@ -36,6 +36,10 @@ func (i *Initializer) prepareHostNetwork() error {
 	// If the HNS Network already exists, return immediately.
 	hnsNetwork, err := hcsshim.GetHNSNetworkByName(util.LocalHNSNetwork)
 	if err == nil {
+		// Enable RSC for existing vSwitch.
+		if err = util.EnableRSCOnVSwitch(util.LocalHNSNetwork); err != nil {
+			return err
+		}
 		// Save the uplink adapter name to check if the OVS uplink port has been created in prepareOVSBridge stage.
 		i.nodeConfig.UplinkNetConfig.Name = hnsNetwork.NetworkAdapterName
 		return nil
diff --git a/pkg/agent/cniserver/interface_configuration_windows.go b/pkg/agent/cniserver/interface_configuration_windows.go
index fd4c0ab77..e0e2b7127 100644
--- a/pkg/agent/cniserver/interface_configuration_windows.go
+++ b/pkg/agent/cniserver/interface_configuration_windows.go
@@ -34,8 +34,8 @@ import (
 	"antrea.io/antrea/pkg/agent/interfacestore"
 	"antrea.io/antrea/pkg/agent/util"
 	cnipb "antrea.io/antrea/pkg/apis/cni/v1beta1"
-	"antrea.io/antrea/pkg/k8s"
 	"antrea.io/antrea/pkg/ovs/ovsconfig"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
diff --git a/pkg/agent/cniserver/pod_configuration.go b/pkg/agent/cniserver/pod_configuration.go
index 4cf604109..57417e890 100644
--- a/pkg/agent/cniserver/pod_configuration.go
+++ b/pkg/agent/cniserver/pod_configuration.go
@@ -33,8 +33,8 @@ import (
 	"antrea.io/antrea/pkg/agent/types"
 	"antrea.io/antrea/pkg/agent/util"
 	"antrea.io/antrea/pkg/apis/controlplane/v1beta2"
-	"antrea.io/antrea/pkg/k8s"
 	"antrea.io/antrea/pkg/ovs/ovsconfig"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 type vethPair struct {
diff --git a/pkg/agent/controller/egress/egress_controller.go b/pkg/agent/controller/egress/egress_controller.go
index 91e088a56..f75761517 100644
--- a/pkg/agent/controller/egress/egress_controller.go
+++ b/pkg/agent/controller/egress/egress_controller.go
@@ -41,7 +41,7 @@ import (
 	crdv1a2 "antrea.io/antrea/pkg/apis/crd/v1alpha2"
 	crdinformers "antrea.io/antrea/pkg/client/informers/externalversions/crd/v1alpha2"
 	crdlisters "antrea.io/antrea/pkg/client/listers/crd/v1alpha2"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
diff --git a/pkg/agent/controller/noderoute/node_route_controller.go b/pkg/agent/controller/noderoute/node_route_controller.go
index 26086a7e3..3b90b8c5d 100644
--- a/pkg/agent/controller/noderoute/node_route_controller.go
+++ b/pkg/agent/controller/noderoute/node_route_controller.go
@@ -38,6 +38,7 @@ import (
 	"antrea.io/antrea/pkg/agent/util"
 	"antrea.io/antrea/pkg/ovs/ovsconfig"
 	utilip "antrea.io/antrea/pkg/util/ip"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
@@ -212,7 +213,7 @@ func (c *Controller) removeStaleTunnelPorts() error {
 				continue
 			}
 
-			peerNodeIP, err := GetNodeAddr(node)
+			peerNodeIP, err := k8s.GetNodeAddr(node)
 			if err != nil {
 				klog.Errorf("Failed to retrieve IP address of Node %s: %v", node.Name, err)
 				continue
@@ -456,7 +457,7 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error {
 		podCIDRs = append(podCIDRs, peerPodCIDR)
 	}
 
-	peerNodeIP, err := GetNodeAddr(node)
+	peerNodeIP, err := k8s.GetNodeAddr(node)
 	if err != nil {
 		klog.Errorf("Failed to retrieve IP address of Node %s: %v", nodeName, err)
 		return nil
@@ -593,31 +594,6 @@ func ParseTunnelInterfaceConfig(
 	return interfaceConfig
 }
 
-// GetNodeAddr gets the available IP address of a Node. GetNodeAddr will first try to get the
-// NodeInternalIP, then try to get the NodeExternalIP.
-// Note: Although K8s supports dual-stack, there is only a single Internal address per Node because of issue (
-// kubernetes/kubernetes#91940 ). The Node might have multiple addresses after the issue is fixed, and one per address
-// family. And we should change the return type at that time.
-func GetNodeAddr(node *corev1.Node) (net.IP, error) {
-	addresses := make(map[corev1.NodeAddressType]string)
-	for _, addr := range node.Status.Addresses {
-		addresses[addr.Type] = addr.Address
-	}
-	var ipAddrStr string
-	if internalIP, ok := addresses[corev1.NodeInternalIP]; ok {
-		ipAddrStr = internalIP
-	} else if externalIP, ok := addresses[corev1.NodeExternalIP]; ok {
-		ipAddrStr = externalIP
-	} else {
-		return nil, fmt.Errorf("node %s has neither external ip nor internal ip", node.Name)
-	}
-	ipAddr := net.ParseIP(ipAddrStr)
-	if ipAddr == nil {
-		return nil, fmt.Errorf("<%v> is not a valid ip address", ipAddrStr)
-	}
-	return ipAddr, nil
-}
-
 func (c *Controller) IPInPodSubnets(ip net.IP) bool {
 	var ipCIDR *net.IPNet
 	var curNodeCIDRStr string
diff --git a/pkg/agent/flowexporter/connections/connections.go b/pkg/agent/flowexporter/connections/connections.go
index c1f444cb7..602361d9d 100644
--- a/pkg/agent/flowexporter/connections/connections.go
+++ b/pkg/agent/flowexporter/connections/connections.go
@@ -174,6 +174,9 @@ func (cs *connectionStore) addOrUpdateConn(conn *flowexporter.Connection) {
 		// IDs stored in the connection label.
 		if len(conn.Labels) != 0 {
 			klog.V(4).Infof("connection label: %x; label masks: %x", conn.Labels, conn.LabelsMask)
+			// We always expect labels from conntrack dumper to be added in little-endian format right now
+			// In kernel datapath, the labels uses the "native" endianness for the system, which are little-endian
+			// on most of the modern CPUs based on x86 architecture like Intel, AMD, etc.
 			ingressOfID := binary.LittleEndian.Uint32(conn.Labels[:4])
 			egressOfID := binary.LittleEndian.Uint32(conn.Labels[4:8])
 			if ingressOfID != 0 {
diff --git a/pkg/agent/flowexporter/connections/conntrack_linux.go b/pkg/agent/flowexporter/connections/conntrack_linux.go
index 91aeb579c..f12fa6165 100644
--- a/pkg/agent/flowexporter/connections/conntrack_linux.go
+++ b/pkg/agent/flowexporter/connections/conntrack_linux.go
@@ -41,6 +41,8 @@ type connTrackSystem struct {
 	connTrack            NetFilterConnTrack
 }
 
+// TODO: detect the endianness of the system when initializing conntrack dumper to handle situations on big-endian platforms.
+// All connection labels are required to store in little endian format in conntrack dumper.
 func NewConnTrackSystem(nodeConfig *config.NodeConfig, serviceCIDRv4 *net.IPNet, serviceCIDRv6 *net.IPNet, isAntreaProxyEnabled bool) *connTrackSystem {
 	if err := SetupConntrackParameters(); err != nil {
 		// Do not fail, but continue after logging an error as we can still dump flows with missing information.
diff --git a/pkg/agent/flowexporter/connections/conntrack_ovs.go b/pkg/agent/flowexporter/connections/conntrack_ovs.go
index 91baf5cfd..a81830be8 100644
--- a/pkg/agent/flowexporter/connections/conntrack_ovs.go
+++ b/pkg/agent/flowexporter/connections/conntrack_ovs.go
@@ -15,6 +15,7 @@
 package connections
 
 import (
+	"encoding/hex"
 	"fmt"
 	"net"
 	"strconv"
@@ -125,7 +126,7 @@ func (ct *connTrackOvsCtl) ovsAppctlDumpConnections(zoneFilter uint16) ([]*flowe
 
 // flowStringToAntreaConnection parses the flow string and converts to Antrea connection.
 // Example of flow string:
-// "tcp,orig=(src=127.0.0.1,dst=127.0.0.1,sport=45218,dport=2379,packets=320108,bytes=24615344),reply=(src=127.0.0.1,dst=127.0.0.1,sport=2379,dport=45218,packets=239595,bytes=24347883),start=2020-07-24T05:07:03.998,id=3750535678,status=SEEN_REPLY|ASSURED|CONFIRMED|SRC_NAT_DONE|DST_NAT_DONE,timeout=86399,protoinfo=(state_orig=ESTABLISHED,state_reply=ESTABLISHED,wscale_orig=7,wscale_reply=7,flags_orig=WINDOW_SCALE|SACK_PERM|MAXACK_SET,flags_reply=WINDOW_SCALE|SACK_PERM|MAXACK_SET)"
+// "tcp,orig=(src=127.0.0.1,dst=127.0.0.1,sport=45218,dport=2379,packets=320108,bytes=24615344),reply=(src=127.0.0.1,dst=127.0.0.1,sport=2379,dport=45218,packets=239595,bytes=24347883),start=2020-07-24T05:07:03.998,id=3750535678,status=SEEN_REPLY|ASSURED|CONFIRMED|SRC_NAT_DONE|DST_NAT_DONE,timeout=86399,labels=0x200000001,protoinfo=(state_orig=ESTABLISHED,state_reply=ESTABLISHED,wscale_orig=7,wscale_reply=7,flags_orig=WINDOW_SCALE|SACK_PERM|MAXACK_SET,flags_reply=WINDOW_SCALE|SACK_PERM|MAXACK_SET)"
 func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter.Connection, error) {
 	conn := flowexporter.Connection{}
 	flowSlice := strings.Split(flow, ",")
@@ -163,7 +164,7 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 16)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of sport %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of sport %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			if !isReply {
 				conn.TupleOrig.SourcePort = uint16(val)
@@ -176,7 +177,7 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 16)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of dport %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of dport %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			if !isReply {
 				conn.TupleOrig.DestinationPort = uint16(val)
@@ -187,7 +188,7 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 64)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of packets %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of packets %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			if !isReply {
 				conn.OriginalPackets = uint64(val)
@@ -199,7 +200,7 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 64)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of bytes %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of bytes %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			if !isReply {
 				conn.OriginalBytes = uint64(val)
@@ -213,7 +214,7 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			timeString := fields[len(fields)-1] + "Z"
 			val, err := time.Parse(time.RFC3339, timeString)
 			if err != nil {
-				return nil, fmt.Errorf("parsing start time %s failed", timeString)
+				return nil, fmt.Errorf("parsing start time %s failed: %v", timeString, err)
 			}
 			conn.StartTime = val
 		// TODO: We didn't find stoptime related field in flow string right now, need to investigate how stoptime is recorded and dumped.
@@ -224,7 +225,7 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 16)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of zone %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of zone %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			if zoneFilter != uint16(val) {
 				break
@@ -236,21 +237,37 @@ func flowStringToAntreaConnection(flow string, zoneFilter uint16) (*flowexporter
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 32)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of mark '%s' to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of mark '%s' to int failed: %v", fields[len(fields)-1], err)
 			}
 			conn.Mark = uint32(val)
 		case strings.Contains(fs, "timeout"):
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 32)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of timeout %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of timeout %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			conn.Timeout = uint32(val)
+		case strings.Contains(fs, "labels"):
+			fields := strings.Split(fs, "=")
+			labelStr := strings.Replace(fields[len(fields)-1], "0x", "", -1)
+			// Add leading zeros since DecodeString() expects the input string has even length
+			if len(labelStr) < 16 {
+				labelStr = strings.Repeat("0", 16-len(labelStr)) + labelStr
+			}
+			hexval, err := hex.DecodeString(labelStr)
+			if err != nil {
+				return nil, fmt.Errorf("conversion of label string %s to []byte failed: %v", labelStr, err)
+			}
+			// Reverse the []byte slice to align with kernel side's result which is little endian
+			for i := 0; i < len(hexval)/2; i++ {
+				hexval[i], hexval[len(hexval)-i-1] = hexval[len(hexval)-i-1], hexval[i]
+			}
+			conn.Labels = hexval
 		case strings.Contains(fs, "id"):
 			fields := strings.Split(fs, "=")
 			val, err := strconv.ParseUint(fields[len(fields)-1], 10, 32)
 			if err != nil {
-				return nil, fmt.Errorf("conversion of id %s to int failed", fields[len(fields)-1])
+				return nil, fmt.Errorf("conversion of id %s to int failed: %v", fields[len(fields)-1], err)
 			}
 			conn.ID = uint32(val)
 		case strings.Contains(fs, "protoinfo"):
diff --git a/pkg/agent/flowexporter/exporter/exporter.go b/pkg/agent/flowexporter/exporter/exporter.go
index 3e51c6427..11dd98126 100644
--- a/pkg/agent/flowexporter/exporter/exporter.go
+++ b/pkg/agent/flowexporter/exporter/exporter.go
@@ -79,25 +79,24 @@ var (
 )
 
 type flowExporter struct {
-	connStore                 connections.ConnectionStore
-	flowRecords               *flowrecords.FlowRecords
-	process                   ipfix.IPFIXExportingProcess
-	elementsListv4            []*ipfixentities.InfoElementWithValue
-	elementsListv6            []*ipfixentities.InfoElementWithValue
-	ipfixSet                  ipfixentities.Set
-	numDataSetsSent           uint64 // used for unit tests.
-	templateIDv4              uint16
-	templateIDv6              uint16
-	registry                  ipfix.IPFIXRegistry
-	v4Enabled                 bool
-	v6Enabled                 bool
-	exporterInput             exporter.ExporterInput
-	activeFlowTimeout         time.Duration
-	idleFlowTimeout           time.Duration
-	enableTLSToFlowAggregator bool
-	k8sClient                 kubernetes.Interface
-	nodeRouteController       *noderoute.Controller
-	isNetworkPolicyOnly       bool
+	connStore           connections.ConnectionStore
+	flowRecords         *flowrecords.FlowRecords
+	process             ipfix.IPFIXExportingProcess
+	elementsListv4      []*ipfixentities.InfoElementWithValue
+	elementsListv6      []*ipfixentities.InfoElementWithValue
+	ipfixSet            ipfixentities.Set
+	numDataSetsSent     uint64 // used for unit tests.
+	templateIDv4        uint16
+	templateIDv6        uint16
+	registry            ipfix.IPFIXRegistry
+	v4Enabled           bool
+	v6Enabled           bool
+	exporterInput       exporter.ExporterInput
+	activeFlowTimeout   time.Duration
+	idleFlowTimeout     time.Duration
+	k8sClient           kubernetes.Interface
+	nodeRouteController *noderoute.Controller
+	isNetworkPolicyOnly bool
 }
 
 func genObservationID() (uint32, error) {
@@ -119,7 +118,13 @@ func prepareExporterInputArgs(collectorAddr, collectorProto string) (exporter.Ex
 		return expInput, err
 	}
 	expInput.CollectorAddress = collectorAddr
-	expInput.CollectorProtocol = collectorProto
+	if collectorProto == "tls" {
+		expInput.IsEncrypted = true
+		expInput.CollectorProtocol = "tcp"
+	} else {
+		expInput.IsEncrypted = false
+		expInput.CollectorProtocol = collectorProto
+	}
 	expInput.PathMTU = 0
 
 	return expInput, nil
@@ -127,7 +132,7 @@ func prepareExporterInputArgs(collectorAddr, collectorProto string) (exporter.Ex
 
 func NewFlowExporter(connStore connections.ConnectionStore, records *flowrecords.FlowRecords,
 	collectorAddr string, collectorProto string, activeFlowTimeout time.Duration, idleFlowTimeout time.Duration,
-	enableTLSToFlowAggregator bool, v4Enabled bool, v6Enabled bool, k8sClient kubernetes.Interface,
+	v4Enabled bool, v6Enabled bool, k8sClient kubernetes.Interface,
 	nodeRouteController *noderoute.Controller, isNetworkPolicyOnly bool) (*flowExporter, error) {
 	// Initialize IPFIX registry
 	registry := ipfix.NewIPFIXRegistry()
@@ -140,19 +145,18 @@ func NewFlowExporter(connStore connections.ConnectionStore, records *flowrecords
 	}
 
 	return &flowExporter{
-		connStore:                 connStore,
-		flowRecords:               records,
-		registry:                  registry,
-		v4Enabled:                 v4Enabled,
-		v6Enabled:                 v6Enabled,
-		exporterInput:             expInput,
-		activeFlowTimeout:         activeFlowTimeout,
-		idleFlowTimeout:           idleFlowTimeout,
-		ipfixSet:                  ipfixentities.NewSet(false),
-		enableTLSToFlowAggregator: enableTLSToFlowAggregator,
-		k8sClient:                 k8sClient,
-		nodeRouteController:       nodeRouteController,
-		isNetworkPolicyOnly:       isNetworkPolicyOnly,
+		connStore:           connStore,
+		flowRecords:         records,
+		registry:            registry,
+		v4Enabled:           v4Enabled,
+		v6Enabled:           v6Enabled,
+		exporterInput:       expInput,
+		activeFlowTimeout:   activeFlowTimeout,
+		idleFlowTimeout:     idleFlowTimeout,
+		ipfixSet:            ipfixentities.NewSet(false),
+		k8sClient:           k8sClient,
+		nodeRouteController: nodeRouteController,
+		isNetworkPolicyOnly: isNetworkPolicyOnly,
 	}, nil
 }
 
@@ -194,7 +198,7 @@ func (exp *flowExporter) Export() {
 
 func (exp *flowExporter) initFlowExporter() error {
 	var err error
-	if exp.enableTLSToFlowAggregator {
+	if exp.exporterInput.IsEncrypted {
 		// if CA certificate, client certificate and key do not exist during initialization,
 		// it will retry to obtain the credentials in next export cycle
 		exp.exporterInput.CACert, err = getCACert(exp.k8sClient)
@@ -207,17 +211,14 @@ func (exp *flowExporter) initFlowExporter() error {
 		}
 		// TLS transport does not need any tempRefTimeout, so sending 0.
 		exp.exporterInput.TempRefTimeout = 0
-		exp.exporterInput.IsEncrypted = true
 	} else if exp.exporterInput.CollectorProtocol == "tcp" {
 		// TCP transport does not need any tempRefTimeout, so sending 0.
 		// tempRefTimeout is the template refresh timeout, which specifies how often
 		// the exporting process should send the template again.
 		exp.exporterInput.TempRefTimeout = 0
-		exp.exporterInput.IsEncrypted = false
 	} else {
 		// For UDP transport, hardcoding tempRefTimeout value as 1800s.
 		exp.exporterInput.TempRefTimeout = 1800
-		exp.exporterInput.IsEncrypted = false
 	}
 	expProcess, err := ipfix.NewIPFIXExportingProcess(exp.exporterInput)
 	if err != nil {
diff --git a/pkg/agent/interfacestore/interface_cache.go b/pkg/agent/interfacestore/interface_cache.go
index cb4cb4468..51deb3d92 100644
--- a/pkg/agent/interfacestore/interface_cache.go
+++ b/pkg/agent/interfacestore/interface_cache.go
@@ -21,7 +21,7 @@ import (
 
 	"antrea.io/antrea/pkg/agent/metrics"
 	"antrea.io/antrea/pkg/agent/util"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
diff --git a/pkg/agent/util/net_windows.go b/pkg/agent/util/net_windows.go
index c95cd1a0d..c540d752a 100644
--- a/pkg/agent/util/net_windows.go
+++ b/pkg/agent/util/net_windows.go
@@ -367,10 +367,45 @@ func PrepareHNSNetwork(subnetCIDR *net.IPNet, nodeIPNet *net.IPNet, uplinkAdapte
 		hnsNet.Delete()
 		return err
 	}
+
+	if err = EnableRSCOnVSwitch(LocalHNSNetwork); err != nil {
+		return err
+	}
+
 	klog.Infof("Created HNSNetwork with name %s id %s", hnsNet.Name, hnsNet.Id)
 	return nil
 }
 
+// EnableRSCOnVSwitch enables RSC in the vSwitch to reduce host CPU utilization and increase throughput for virtual
+// workloads by coalescing multiple TCP segments into fewer, but larger segments.
+func EnableRSCOnVSwitch(vSwitch string) error {
+	cmd := fmt.Sprintf("Get-VMSwitch -Name %s | Select-Object -Property SoftwareRscEnabled | Format-Table -HideTableHeaders", vSwitch)
+	stdout, err := CallPSCommand(cmd)
+	if err != nil {
+		return err
+	}
+	stdout = strings.TrimSpace(stdout)
+	// RSC doc says it applies to Windows Server 2019, which is the only Windows operating system supported so far, so
+	// this should not happen. However, this is only an optimization, no need to crash the process even if it's not
+	// supported.
+	// https://docs.microsoft.com/en-us/windows-server/networking/technologies/hpn/rsc-in-the-vswitch
+	if len(stdout) == 0 {
+		klog.Warning("Receive Segment Coalescing (RSC) is not supported by this Windows Server version")
+		return nil
+	}
+	if strings.EqualFold(stdout, "True") {
+		klog.Infof("Receive Segment Coalescing (RSC) for vSwitch %s is already enabled", vSwitch)
+		return nil
+	}
+	cmd = fmt.Sprintf("Set-VMSwitch -Name %s -EnableSoftwareRsc $True", vSwitch)
+	err = InvokePSCommand(cmd)
+	if err != nil {
+		return err
+	}
+	klog.Infof("Enabled Receive Segment Coalescing (RSC) for vSwitch %s", vSwitch)
+	return nil
+}
+
 func enableHNSOnOVS(hnsNet *hcsshim.HNSNetwork) error {
 	// Release OS management for HNS Network if Hyper-V is enabled.
 	hypervEnabled, err := WindowsHyperVEnabled()
diff --git a/pkg/antctl/raw/proxy/command.go b/pkg/antctl/raw/proxy/command.go
index 2ccfca841..11c93b6b1 100644
--- a/pkg/antctl/raw/proxy/command.go
+++ b/pkg/antctl/raw/proxy/command.go
@@ -29,10 +29,10 @@ import (
 	"k8s.io/klog/v2"
 	"k8s.io/kubectl/pkg/proxy"
 
-	"antrea.io/antrea/pkg/agent/controller/noderoute"
 	"antrea.io/antrea/pkg/antctl/runtime"
 	clusterinformationv1beta1 "antrea.io/antrea/pkg/apis/crd/v1beta1"
 	antrea "antrea.io/antrea/pkg/client/clientset/versioned"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
@@ -185,7 +185,7 @@ func createAgentClientCfg(k8sClientset kubernetes.Interface, antreaClientset ant
 	if agentInfo == nil {
 		return nil, fmt.Errorf("no Antrea Agent found for Node name %s", nodeName)
 	}
-	nodeIP, err := noderoute.GetNodeAddr(node)
+	nodeIP, err := k8s.GetNodeAddr(node)
 	if err != nil {
 		return nil, fmt.Errorf("error when parsing IP of Node %s", nodeName)
 	}
@@ -205,7 +205,7 @@ func createControllerClientCfg(k8sClientset kubernetes.Interface, antreaClientse
 		return nil, fmt.Errorf("error when searching the Node of the controller: %w", err)
 	}
 	var controllerNodeIP net.IP
-	controllerNodeIP, err = noderoute.GetNodeAddr(controllerNode)
+	controllerNodeIP, err = k8s.GetNodeAddr(controllerNode)
 	if err != nil {
 		return nil, fmt.Errorf("error when parsing controller IP: %w", err)
 	}
diff --git a/pkg/antctl/raw/supportbundle/command.go b/pkg/antctl/raw/supportbundle/command.go
index efa92e5f6..96d9f7c67 100644
--- a/pkg/antctl/raw/supportbundle/command.go
+++ b/pkg/antctl/raw/supportbundle/command.go
@@ -42,12 +42,12 @@ import (
 	"k8s.io/klog/v2"
 
 	agentapiserver "antrea.io/antrea/pkg/agent/apiserver"
-	"antrea.io/antrea/pkg/agent/controller/noderoute"
 	"antrea.io/antrea/pkg/antctl/runtime"
 	"antrea.io/antrea/pkg/apis"
 	systemv1beta1 "antrea.io/antrea/pkg/apis/system/v1beta1"
 	controllerapiserver "antrea.io/antrea/pkg/apiserver"
 	antrea "antrea.io/antrea/pkg/client/clientset/versioned"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
@@ -326,7 +326,7 @@ func createAgentClients(k8sClientset kubernetes.Interface, antreaClientset antre
 		if !ok {
 			continue
 		}
-		ip, err := noderoute.GetNodeAddr(&node)
+		ip, err := k8s.GetNodeAddr(&node)
 		if err != nil {
 			klog.Warningf("Error when parsing IP of Node %s", node.Name)
 			continue
@@ -354,7 +354,7 @@ func createControllerClient(k8sClientset kubernetes.Interface, antreaClientset a
 		return nil, fmt.Errorf("error when searching the Node of the controller: %w", err)
 	}
 	var controllerNodeIP net.IP
-	controllerNodeIP, err = noderoute.GetNodeAddr(controllerNode)
+	controllerNodeIP, err = k8s.GetNodeAddr(controllerNode)
 	if err != nil {
 		return nil, fmt.Errorf("error when parsing controllre IP: %w", err)
 	}
diff --git a/pkg/controller/networkpolicy/networkpolicy_controller.go b/pkg/controller/networkpolicy/networkpolicy_controller.go
index 0bb29fc3c..d7ff3c47b 100644
--- a/pkg/controller/networkpolicy/networkpolicy_controller.go
+++ b/pkg/controller/networkpolicy/networkpolicy_controller.go
@@ -60,7 +60,7 @@ import (
 	"antrea.io/antrea/pkg/controller/networkpolicy/store"
 	antreatypes "antrea.io/antrea/pkg/controller/types"
 	"antrea.io/antrea/pkg/features"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 	utilsets "antrea.io/antrea/pkg/util/sets"
 )
 
diff --git a/pkg/controller/networkpolicy/store/group.go b/pkg/controller/networkpolicy/store/group.go
index 58c4127f1..d83165926 100644
--- a/pkg/controller/networkpolicy/store/group.go
+++ b/pkg/controller/networkpolicy/store/group.go
@@ -23,7 +23,7 @@ import (
 	"antrea.io/antrea/pkg/apiserver/storage"
 	"antrea.io/antrea/pkg/apiserver/storage/ram"
 	antreatypes "antrea.io/antrea/pkg/controller/types"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
diff --git a/pkg/controller/stats/aggregator.go b/pkg/controller/stats/aggregator.go
index 503706c3f..e39bc1179 100644
--- a/pkg/controller/stats/aggregator.go
+++ b/pkg/controller/stats/aggregator.go
@@ -30,7 +30,7 @@ import (
 	statsv1alpha1 "antrea.io/antrea/pkg/apis/stats/v1alpha1"
 	crdvinformers "antrea.io/antrea/pkg/client/informers/externalversions/crd/v1alpha1"
 	"antrea.io/antrea/pkg/features"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
diff --git a/pkg/util/flowexport/flowexport.go b/pkg/util/flowexport/flowexport.go
index 8d917cc40..3e204ad94 100644
--- a/pkg/util/flowexport/flowexport.go
+++ b/pkg/util/flowexport/flowexport.go
@@ -45,7 +45,7 @@ func ParseFlowCollectorAddr(addr string, defaultPort string, defaultProtocol str
 		} else {
 			port = strSlice[1]
 		}
-		if (strSlice[2] != "udp") && (strSlice[2] != "tcp") {
+		if (strSlice[2] != "tls") && (strSlice[2] != "tcp") && (strSlice[2] != "udp") {
 			return host, port, proto, fmt.Errorf("connection over %s transport proto is not supported", strSlice[2])
 		}
 		proto = strSlice[2]
diff --git a/pkg/k8s/client.go b/pkg/util/k8s/client.go
similarity index 100%
rename from pkg/k8s/client.go
rename to pkg/util/k8s/client.go
diff --git a/pkg/k8s/doc.go b/pkg/util/k8s/doc.go
similarity index 100%
rename from pkg/k8s/doc.go
rename to pkg/util/k8s/doc.go
diff --git a/pkg/k8s/name.go b/pkg/util/k8s/name.go
similarity index 100%
rename from pkg/k8s/name.go
rename to pkg/util/k8s/name.go
diff --git a/pkg/util/k8s/node.go b/pkg/util/k8s/node.go
new file mode 100644
index 000000000..0f4b48796
--- /dev/null
+++ b/pkg/util/k8s/node.go
@@ -0,0 +1,47 @@
+// Copyright 2021 Antrea Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package k8s
+
+import (
+	"fmt"
+	"net"
+
+	v1 "k8s.io/api/core/v1"
+)
+
+// GetNodeAddr gets the available IP address of a Node. GetNodeAddr will first try to get the NodeInternalIP, then try
+// to get the NodeExternalIP.
+// Note: Although K8s supports dual-stack, there is only a single Internal address per Node because of issue (
+// kubernetes/kubernetes#91940 ). The Node might have multiple addresses after the issue is fixed, and one per address
+// family. And we should change the return type at that time.
+func GetNodeAddr(node *v1.Node) (net.IP, error) {
+	addresses := make(map[v1.NodeAddressType]string)
+	for _, addr := range node.Status.Addresses {
+		addresses[addr.Type] = addr.Address
+	}
+	var ipAddrStr string
+	if internalIP, ok := addresses[v1.NodeInternalIP]; ok {
+		ipAddrStr = internalIP
+	} else if externalIP, ok := addresses[v1.NodeExternalIP]; ok {
+		ipAddrStr = externalIP
+	} else {
+		return nil, fmt.Errorf("Node %s has neither external ip nor internal ip", node.Name)
+	}
+	ipAddr := net.ParseIP(ipAddrStr)
+	if ipAddr == nil {
+		return nil, fmt.Errorf("<%v> is not a valid ip address", ipAddrStr)
+	}
+	return ipAddr, nil
+}


Test patch:
diff --git a/ci/test-elk-flow-collector.sh b/ci/test-elk-flow-collector.sh
index 4872a1157..b3f28b001 100755
--- a/ci/test-elk-flow-collector.sh
+++ b/ci/test-elk-flow-collector.sh
@@ -77,7 +77,6 @@ config_antrea() {
   echo "=== Configuring Antrea Flow Exporter Address ==="
   sed -i -e "s/#flowCollectorAddr.*/flowCollectorAddr: \"${LOGSTASH_IP}:${LOGSTASH_PORT}:${LOGSTASH_PROTOCOL}\"/g" ${GIT_CHECKOUT_DIR}/build/yamls/antrea.yml
   sed -i -e "s/#  FlowExporter: false/  FlowExporter: true/g" ${GIT_CHECKOUT_DIR}/build/yamls/antrea.yml
-  sed -i -e "s/#enableTLSToFlowAggregator: true/enableTLSToFlowAggregator: false/g" ${GIT_CHECKOUT_DIR}/build/yamls/antrea.yml
 }
 
 # Antrea agent flow exporter starts to send CoreDNS flow records.
diff --git a/pkg/agent/controller/egress/egress_controller_test.go b/pkg/agent/controller/egress/egress_controller_test.go
index b41a08717..832cd74e1 100644
--- a/pkg/agent/controller/egress/egress_controller_test.go
+++ b/pkg/agent/controller/egress/egress_controller_test.go
@@ -40,7 +40,7 @@ import (
 	"antrea.io/antrea/pkg/client/clientset/versioned/fake"
 	fakeversioned "antrea.io/antrea/pkg/client/clientset/versioned/fake"
 	crdinformers "antrea.io/antrea/pkg/client/informers/externalversions"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 const (
diff --git a/pkg/agent/flowexporter/connections/conntrack_linux_test.go b/pkg/agent/flowexporter/connections/conntrack_linux_test.go
index 5a81d5193..8240cf3fb 100644
--- a/pkg/agent/flowexporter/connections/conntrack_linux_test.go
+++ b/pkg/agent/flowexporter/connections/conntrack_linux_test.go
@@ -143,7 +143,7 @@ func TestConnTrackOvsAppCtl_DumpFlows(t *testing.T) {
 	// Set expect call for mock ovsCtlClient
 	ovsctlCmdOutput := []byte("tcp,orig=(src=127.0.0.1,dst=127.0.0.1,sport=45218,dport=2379,packets=320108,bytes=24615344),reply=(src=127.0.0.1,dst=127.0.0.1,sport=2379,dport=45218,packets=239595,bytes=24347883),start=2020-07-24T05:07:03.998,id=3750535678,status=SEEN_REPLY|ASSURED|CONFIRMED|SRC_NAT_DONE|DST_NAT_DONE,timeout=86399,protoinfo=(state_orig=ESTABLISHED,state_reply=ESTABLISHED,wscale_orig=7,wscale_reply=7,flags_orig=WINDOW_SCALE|SACK_PERM|MAXACK_SET,flags_reply=WINDOW_SCALE|SACK_PERM|MAXACK_SET)\n" +
 		"tcp,orig=(src=127.0.0.1,dst=8.7.6.5,sport=45170,dport=2379,packets=80743,bytes=5416239),reply=(src=8.7.6.5,dst=127.0.0.1,sport=2379,dport=45170,packets=63361,bytes=4811261),start=2020-07-24T05:07:01.591,id=462801621,zone=65520,status=SEEN_REPLY|ASSURED|CONFIRMED|SRC_NAT_DONE|DST_NAT_DONE,timeout=86397,protoinfo=(state_orig=ESTABLISHED,state_reply=ESTABLISHED,wscale_orig=7,wscale_reply=7,flags_orig=WINDOW_SCALE|SACK_PERM|MAXACK_SET,flags_reply=WINDOW_SCALE|SACK_PERM|MAXACK_SET)\n" +
-		"tcp,orig=(src=100.10.0.105,dst=10.96.0.1,sport=41284,dport=443,packets=343260,bytes=19340621),reply=(src=100.10.0.106,dst=100.10.0.105,sport=6443,dport=41284,packets=381035,bytes=181176472),start=2020-07-25T08:40:08.959,id=982464968,zone=65520,status=SEEN_REPLY|ASSURED|CONFIRMED|DST_NAT|DST_NAT_DONE,timeout=86399,mark=33,protoinfo=(state_orig=ESTABLISHED,state_reply=ESTABLISHED,wscale_orig=7,wscale_reply=7,flags_orig=WINDOW_SCALE|SACK_PERM|MAXACK_SET,flags_reply=WINDOW_SCALE|SACK_PERM|MAXACK_SET)")
+		"tcp,orig=(src=100.10.0.105,dst=10.96.0.1,sport=41284,dport=443,packets=343260,bytes=19340621),reply=(src=100.10.0.106,dst=100.10.0.105,sport=6443,dport=41284,packets=381035,bytes=181176472),start=2020-07-25T08:40:08.959,id=982464968,zone=65520,status=SEEN_REPLY|ASSURED|CONFIRMED|DST_NAT|DST_NAT_DONE,timeout=86399,labels=0x200000001,mark=33,protoinfo=(state_orig=ESTABLISHED,state_reply=ESTABLISHED,wscale_orig=7,wscale_reply=7,flags_orig=WINDOW_SCALE|SACK_PERM|MAXACK_SET,flags_reply=WINDOW_SCALE|SACK_PERM|MAXACK_SET)")
 	outputFlow := strings.Split(string(ovsctlCmdOutput), "\n")
 	expConn := &flowexporter.Connection{
 		ID:         982464968,
@@ -177,6 +177,7 @@ func TestConnTrackOvsAppCtl_DumpFlows(t *testing.T) {
 		DestinationPodNamespace: "",
 		DestinationPodName:      "",
 		TCPState:                "ESTABLISHED",
+		Labels:                  []byte{1, 0, 0, 0, 2, 0, 0, 0},
 	}
 	mockOVSCtlClient.EXPECT().RunAppctlCmd("dpctl/dump-conntrack", false, "-m", "-s").Return(ovsctlCmdOutput, nil)
 
diff --git a/pkg/apiserver/registry/networkpolicy/groupassociation/rest_test.go b/pkg/apiserver/registry/networkpolicy/groupassociation/rest_test.go
index be8f4bfa9..57879223c 100644
--- a/pkg/apiserver/registry/networkpolicy/groupassociation/rest_test.go
+++ b/pkg/apiserver/registry/networkpolicy/groupassociation/rest_test.go
@@ -25,7 +25,7 @@ import (
 
 	"antrea.io/antrea/pkg/apis/controlplane"
 	antreatypes "antrea.io/antrea/pkg/controller/types"
-	"antrea.io/antrea/pkg/k8s"
+	"antrea.io/antrea/pkg/util/k8s"
 )
 
 type fakeQuerier struct {
diff --git a/pkg/util/k8s/node_test.go b/pkg/util/k8s/node_test.go
new file mode 100644
index 000000000..07362337a
--- /dev/null
+++ b/pkg/util/k8s/node_test.go
@@ -0,0 +1,108 @@
+// Copyright 2021 Antrea Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package k8s
+
+import (
+	"fmt"
+	"net"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestGetNodeAddr(t *testing.T) {
+	tests := []struct {
+		name         string
+		node         *corev1.Node
+		expectedAddr net.IP
+		expectedErr  error
+	}{
+		{
+			name: "internal address first",
+			node: &corev1.Node{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "foo",
+				},
+				Status: corev1.NodeStatus{
+					Addresses: []corev1.NodeAddress{
+						{
+							Type:    corev1.NodeInternalIP,
+							Address: "192.168.10.10",
+						},
+						{
+							Type:    corev1.NodeExternalIP,
+							Address: "1.1.1.1",
+						},
+						{
+							Type:    corev1.NodeHostName,
+							Address: "foo",
+						},
+					},
+				},
+			},
+			expectedAddr: net.ParseIP("192.168.10.10"),
+			expectedErr:  nil,
+		},
+		{
+			name: "external address",
+			node: &corev1.Node{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "foo",
+				},
+				Status: corev1.NodeStatus{
+					Addresses: []corev1.NodeAddress{
+						{
+							Type:    corev1.NodeExternalIP,
+							Address: "1.1.1.1",
+						},
+						{
+							Type:    corev1.NodeHostName,
+							Address: "foo",
+						},
+					},
+				},
+			},
+			expectedAddr: net.ParseIP("1.1.1.1"),
+			expectedErr:  nil,
+		},
+		{
+			name: "no valid address",
+			node: &corev1.Node{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "foo",
+				},
+				Status: corev1.NodeStatus{
+					Addresses: []corev1.NodeAddress{
+						{
+							Type:    corev1.NodeHostName,
+							Address: "foo",
+						},
+					},
+				},
+			},
+			expectedAddr: nil,
+			expectedErr:  fmt.Errorf("Node foo has neither external ip nor internal ip"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			addr, err := GetNodeAddr(tt.node)
+			assert.Equal(t, tt.expectedErr, err)
+			assert.Equal(t, tt.expectedAddr, addr)
+		})
+	}
+}
diff --git a/test/e2e/fixtures.go b/test/e2e/fixtures.go
index f1549a21a..22940065d 100644
--- a/test/e2e/fixtures.go
+++ b/test/e2e/fixtures.go
@@ -190,7 +190,7 @@ func setupTestWithIPFIXCollector(tb testing.TB) (*TestData, bool, bool, error) {
 	if testOptions.providerName == "kind" {
 		// In Kind cluster, there are issues with DNS name resolution on worker nodes.
 		// Please note that CoreDNS services are forced on to control-plane Node.
-		faClusterIPAddr = fmt.Sprintf("%s:%s:tcp", faClusterIP, ipfixCollectorPort)
+		faClusterIPAddr = fmt.Sprintf("%s:%s:tls", faClusterIP, ipfixCollectorPort)
 	}
 	tb.Logf("Deploying flow exporter with collector address: %s", faClusterIPAddr)
 	if err = testData.deployAntreaFlowExporter(faClusterIPAddr); err != nil {
diff --git a/test/e2e/framework.go b/test/e2e/framework.go
index 37e82fa62..b94d6567c 100644
--- a/test/e2e/framework.go
+++ b/test/e2e/framework.go
@@ -569,11 +569,6 @@ func (data *TestData) deployAntreaFlowExporter(ipfixCollector string) error {
 	if ipfixCollector != "" {
 		ac = append(ac, configChange{"flowCollectorAddr", fmt.Sprintf("\"%s\"", ipfixCollector), false})
 	}
-	if testOptions.providerName == "kind" {
-		// In Kind cluster, there are issues with DNS name resolution on worker nodes.
-		// We will skip TLS testing for Kind cluster because the server certificate is generated with Flow aggregator's DNS name
-		ac = append(ac, configChange{"enableTLSToFlowAggregator", "false", false})
-	}
 	return data.mutateAntreaConfigMap(nil, ac, false, true)
 }
 
@@ -587,7 +582,11 @@ func (data *TestData) deployFlowAggregator(ipfixCollector string) (string, error
 	if err != nil || rc != 0 {
 		return "", fmt.Errorf("error when deploying flow aggregator; %s not available on the control-plane Node", flowAggYaml)
 	}
-	if err = data.mutateFlowAggregatorConfigMap(ipfixCollector); err != nil {
+	svc, err := data.clientset.CoreV1().Services(flowAggregatorNamespace).Get(context.TODO(), flowAggregatorDeployment, metav1.GetOptions{})
+	if err != nil {
+		return "", fmt.Errorf("unable to get service %v: %v", flowAggregatorDeployment, err)
+	}
+	if err = data.mutateFlowAggregatorConfigMap(ipfixCollector, svc.Spec.ClusterIP); err != nil {
 		return "", err
 	}
 	if rc, _, _, err = provider.RunCommandOnNode(controlPlaneNodeName(), fmt.Sprintf("kubectl -n %s rollout status deployment/%s --timeout=%v", flowAggregatorNamespace, flowAggregatorDeployment, 2*defaultTimeout)); err != nil || rc != 0 {
@@ -595,14 +594,10 @@ func (data *TestData) deployFlowAggregator(ipfixCollector string) (string, error
 		_, logStdout, _, _ := provider.RunCommandOnNode(controlPlaneNodeName(), fmt.Sprintf("kubectl -n %s logs -l app=flow-aggregator", flowAggregatorNamespace))
 		return stdout, fmt.Errorf("error when waiting for flow aggregator rollout to complete. kubectl describe output: %s, logs: %s", stdout, logStdout)
 	}
-	svc, err := data.clientset.CoreV1().Services(flowAggregatorNamespace).Get(context.TODO(), flowAggregatorDeployment, metav1.GetOptions{})
-	if err != nil {
-		return "", fmt.Errorf("unable to get service %v: %v", flowAggregatorDeployment, err)
-	}
 	return svc.Spec.ClusterIP, nil
 }
 
-func (data *TestData) mutateFlowAggregatorConfigMap(ipfixCollector string) error {
+func (data *TestData) mutateFlowAggregatorConfigMap(ipfixCollector string, faClusterIP string) error {
 	configMap, err := data.GetFlowAggregatorConfigMap()
 	if err != nil {
 		return err
@@ -616,8 +611,9 @@ func (data *TestData) mutateFlowAggregatorConfigMap(ipfixCollector string) error
 	flowAggregatorConf = strings.Replace(flowAggregatorConf, "#inactiveFlowRecordTimeout: 90s", "inactiveFlowRecordTimeout: 6s", 1)
 	if testOptions.providerName == "kind" {
 		// In Kind cluster, there are issues with DNS name resolution on worker nodes.
-		// We will skip TLS testing for Kind cluster because the server certificate is generated with Flow aggregator's DNS name
-		flowAggregatorConf = strings.Replace(flowAggregatorConf, "#aggregatorTransportProtocol: \"tls\"", "aggregatorTransportProtocol: \"tcp\"", 1)
+		// We will use flow aggregator service cluster IP to generate the server certificate for tls communication
+		faAddress := fmt.Sprintf("flowAggregatorAddress: %s", faClusterIP)
+		flowAggregatorConf = strings.Replace(flowAggregatorConf, "#flowAggregatorAddress: \"flow-aggregator.flow-aggregator.svc\"", faAddress, 1)
 	}
 	configMap.Data[flowAggregatorConfName] = flowAggregatorConf
 	if _, err := data.clientset.CoreV1().ConfigMaps(flowAggregatorNamespace).Update(context.TODO(), configMap, metav1.UpdateOptions{}); err != nil {