Repository: antrea-io/antrea
Pull request #4065 (2022-08-01 09:37:55)

Support configurable tunnel port for TrafficEncapMode
This PR supports configurable tunnel port for TrafficEncapMode. When
deployed on the public cloud, the tunnel type and tunnel port used by
the underlying network may be the same as the antrea container network,
so it needs to support the modification of the tunnel port to prevent
conflicts.

Signed-off-by: Wu zhengdong <zhengdong.wu@transwarp.io>

codecov[bot]: # [Codecov](https://codecov.io/gh/antrea-io/antrea/pull/4065?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) Report
> Merging [#4065](https://codecov.io/gh/antrea-io/antrea/pull/4065?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) (b9fb7f9) into [main](https://codecov.io/gh/antrea-io/antrea/commit/d0d9851800418eb23e6c03bd46ded9d0a593ba94?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) (d0d9851) will **decrease** coverage by `9.13%`.
> The diff coverage is `37.50%`.

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

```diff
@@            Coverage Diff             @@
##             main    #4065      +/-   ##
==========================================
- Coverage   62.58%   53.44%   -9.14%     
==========================================
  Files         289      292       +3     
  Lines       43464    44857    +1393     
==========================================
- Hits        27201    23976    -3225     
- Misses      14060    18882    +4822     
+ Partials     2203     1999     -204     
```

| Flag | Coverage Δ | | *Carryforward flag |
|---|---|---|---|
| kind-e2e-tests | `30.88% <20.83%> (-16.87%)` | :arrow_down: | |
| unit-tests | `44.19% <30.76%> (ø)` | | Carriedforward from [d0d9851](https://codecov.io/gh/antrea-io/antrea/commit/d0d9851800418eb23e6c03bd46ded9d0a593ba94?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) |

*This pull request uses carry forward flags. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) to find out more.

| [Impacted Files](https://codecov.io/gh/antrea-io/antrea/pull/4065?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) | Coverage Δ | |
|---|---|---|
| [pkg/agent/config/node\_config.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L2NvbmZpZy9ub2RlX2NvbmZpZy5nbw==) | `86.36% <ø> (-13.64%)` | :arrow_down: |
| [pkg/ovs/ovsconfig/ovs\_client.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL292cy9vdnNjb25maWcvb3ZzX2NsaWVudC5nbw==) | `27.23% <7.69%> (-19.55%)` | :arrow_down: |
| [...gent/controller/noderoute/node\_route\_controller.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L2NvbnRyb2xsZXIvbm9kZXJvdXRlL25vZGVfcm91dGVfY29udHJvbGxlci5nbw==) | `54.97% <33.33%> (-2.98%)` | :arrow_down: |
| [pkg/agent/agent.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L2FnZW50Lmdv) | `45.94% <66.66%> (-6.85%)` | :arrow_down: |
| [pkg/agent/controller/trafficcontrol/controller.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L2NvbnRyb2xsZXIvdHJhZmZpY2NvbnRyb2wvY29udHJvbGxlci5nbw==) | `64.40% <100.00%> (-13.13%)` | :arrow_down: |
| [pkg/agent/interfacestore/types.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L2ludGVyZmFjZXN0b3JlL3R5cGVzLmdv) | `55.55% <100.00%> (-13.89%)` | :arrow_down: |
| [pkg/ovs/openflow/logs.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL292cy9vcGVuZmxvdy9sb2dzLmdv) | `10.34% <0.00%> (-89.66%)` | :arrow_down: |
| [pkg/agent/nodeportlocal/k8s/annotations.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L25vZGVwb3J0bG9jYWwvazhzL2Fubm90YXRpb25zLmdv) | `0.00% <0.00%> (-84.45%)` | :arrow_down: |
| [pkg/agent/proxy/endpointslicecache.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L3Byb3h5L2VuZHBvaW50c2xpY2VjYWNoZS5nbw==) | `0.00% <0.00%> (-83.60%)` | :arrow_down: |
| [pkg/agent/nodeportlocal/util/parse\_port.go](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io#diff-cGtnL2FnZW50L25vZGVwb3J0bG9jYWwvdXRpbC9wYXJzZV9wb3J0Lmdv) | `0.00% <0.00%> (-70.00%)` | :arrow_down: |
| ... and [131 more](https://codecov.io/gh/antrea-io/antrea/pull/4065/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=antrea-io) | |

tnqn: /test-all

Patch:
diff --git a/build/charts/antrea/README.md b/build/charts/antrea/README.md
index 8276efaae..2743da639 100644
--- a/build/charts/antrea/README.md
+++ b/build/charts/antrea/README.md
@@ -106,6 +106,7 @@ Kubernetes: `>= 1.16.0-0`
 | trafficEncryptionMode | string | `"none"` | Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.It must be one of "none", "ipsec", "wireGuard". |
 | transportInterface | string | `""` | Name of the interface on Node which is used for tunneling or routing the traffic across Nodes. |
 | transportInterfaceCIDRs | list | `[]` | Network CIDRs of the interface on Node which is used for tunneling or routing the traffic across Nodes. |
+| tunnelPort | int | `0` | TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT). If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN, and 7471 for STT. |
 | tunnelType | string | `"geneve"` | Tunnel protocol used for encapsulating traffic across Nodes. It must be one of "geneve", "vxlan", "gre", "stt". |
 | webhooks.labelsMutator.enable | bool | `false` |  |
 | whereabouts.enable | bool | `false` |  |
diff --git a/build/charts/antrea/conf/antrea-agent.conf b/build/charts/antrea/conf/antrea-agent.conf
index ed3f4dbd9..72d03382e 100644
--- a/build/charts/antrea/conf/antrea-agent.conf
+++ b/build/charts/antrea/conf/antrea-agent.conf
@@ -110,6 +110,11 @@ noSNAT: {{ .Values.noSNAT }}
 # Note that "gre" is not supported for IPv6 clusters (IPv6-only or dual-stack clusters).
 tunnelType: {{ .Values.tunnelType | quote }}
 
+# TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+# If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+# and 7471 for STT.
+tunnelPort: {{ .Values.tunnelPort }}
+
 # Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.
 # It has the following options:
 # - none (default):  Inter-node Pod traffic will not be encrypted.
diff --git a/build/charts/antrea/values.yaml b/build/charts/antrea/values.yaml
index dd9a48087..7eaf6510a 100644
--- a/build/charts/antrea/values.yaml
+++ b/build/charts/antrea/values.yaml
@@ -10,6 +10,10 @@ trafficEncapMode: "encap"
 # -- Tunnel protocol used for encapsulating traffic across Nodes. It must be one
 # of "geneve", "vxlan", "gre", "stt".
 tunnelType: "geneve"
+# -- TunnelPort is the destination port for UDP and TCP based tunnel protocols
+# (Geneve, VXLAN, and STT). If zero, it will use the assigned IANA port for the
+# protocol, i.e. 6081 for Geneve, 4789 for VXLAN, and 7471 for STT.
+tunnelPort: 0
 # -- Determines how tunnel traffic is encrypted. Currently encryption only works
 # with encap mode.It must be one of "none", "ipsec", "wireGuard".
 trafficEncryptionMode: "none"
diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml
index 02f8d5344..4311a1a48 100644
--- a/build/yamls/antrea-aks.yml
+++ b/build/yamls/antrea-aks.yml
@@ -2686,6 +2686,11 @@ data:
     # Note that "gre" is not supported for IPv6 clusters (IPv6-only or dual-stack clusters).
     tunnelType: "geneve"
 
+    # TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+    # If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+    # and 7471 for STT.
+    tunnelPort: 0
+
     # Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.
     # It has the following options:
     # - none (default):  Inter-node Pod traffic will not be encrypted.
@@ -3732,7 +3737,7 @@ spec:
         kubectl.kubernetes.io/default-container: antrea-agent
         # Automatically restart Pods with a RollingUpdate if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: beca655f34bfd122082c7efa73505680278a8aa97e74099ca6040bcc4311622f
+        checksum/config: 814300ca95f9d7451131665ebed709cb7639deec890e2ff5ae4c357ae9b00c41
       labels:
         app: antrea
         component: antrea-agent
@@ -3973,7 +3978,7 @@ spec:
       annotations:
         # Automatically restart Pod if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: beca655f34bfd122082c7efa73505680278a8aa97e74099ca6040bcc4311622f
+        checksum/config: 814300ca95f9d7451131665ebed709cb7639deec890e2ff5ae4c357ae9b00c41
       labels:
         app: antrea
         component: antrea-controller
diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml
index 42001d9c5..b06179ad8 100644
--- a/build/yamls/antrea-eks.yml
+++ b/build/yamls/antrea-eks.yml
@@ -2686,6 +2686,11 @@ data:
     # Note that "gre" is not supported for IPv6 clusters (IPv6-only or dual-stack clusters).
     tunnelType: "geneve"
 
+    # TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+    # If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+    # and 7471 for STT.
+    tunnelPort: 0
+
     # Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.
     # It has the following options:
     # - none (default):  Inter-node Pod traffic will not be encrypted.
@@ -3732,7 +3737,7 @@ spec:
         kubectl.kubernetes.io/default-container: antrea-agent
         # Automatically restart Pods with a RollingUpdate if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: beca655f34bfd122082c7efa73505680278a8aa97e74099ca6040bcc4311622f
+        checksum/config: 814300ca95f9d7451131665ebed709cb7639deec890e2ff5ae4c357ae9b00c41
       labels:
         app: antrea
         component: antrea-agent
@@ -3975,7 +3980,7 @@ spec:
       annotations:
         # Automatically restart Pod if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: beca655f34bfd122082c7efa73505680278a8aa97e74099ca6040bcc4311622f
+        checksum/config: 814300ca95f9d7451131665ebed709cb7639deec890e2ff5ae4c357ae9b00c41
       labels:
         app: antrea
         component: antrea-controller
diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml
index fa2f02d27..be9b7d2b2 100644
--- a/build/yamls/antrea-gke.yml
+++ b/build/yamls/antrea-gke.yml
@@ -2686,6 +2686,11 @@ data:
     # Note that "gre" is not supported for IPv6 clusters (IPv6-only or dual-stack clusters).
     tunnelType: "geneve"
 
+    # TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+    # If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+    # and 7471 for STT.
+    tunnelPort: 0
+
     # Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.
     # It has the following options:
     # - none (default):  Inter-node Pod traffic will not be encrypted.
@@ -3732,7 +3737,7 @@ spec:
         kubectl.kubernetes.io/default-container: antrea-agent
         # Automatically restart Pods with a RollingUpdate if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: 741b313c6ab0ed98e7d994985861722f503a93529f90a5141b8a6e0c124d8904
+        checksum/config: 3f9907e9f0f4db91b926904114567c4e2c496f6fe03abb4ef80df5af937c0f19
       labels:
         app: antrea
         component: antrea-agent
@@ -3972,7 +3977,7 @@ spec:
       annotations:
         # Automatically restart Pod if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: 741b313c6ab0ed98e7d994985861722f503a93529f90a5141b8a6e0c124d8904
+        checksum/config: 3f9907e9f0f4db91b926904114567c4e2c496f6fe03abb4ef80df5af937c0f19
       labels:
         app: antrea
         component: antrea-controller
diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml
index bb735b76f..ac3a844b6 100644
--- a/build/yamls/antrea-ipsec.yml
+++ b/build/yamls/antrea-ipsec.yml
@@ -2699,6 +2699,11 @@ data:
     # Note that "gre" is not supported for IPv6 clusters (IPv6-only or dual-stack clusters).
     tunnelType: "gre"
 
+    # TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+    # If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+    # and 7471 for STT.
+    tunnelPort: 0
+
     # Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.
     # It has the following options:
     # - none (default):  Inter-node Pod traffic will not be encrypted.
@@ -3745,7 +3750,7 @@ spec:
         kubectl.kubernetes.io/default-container: antrea-agent
         # Automatically restart Pods with a RollingUpdate if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: c74f29ceba3905db50cef22ee46f73e1c101c108a70e70918b17413c174081e8
+        checksum/config: 7b6ba1830aabcf74b5b0a71c74edf49254c4237d604f9d984428252903901f98
         checksum/ipsec-secret: d0eb9c52d0cd4311b6d252a951126bf9bea27ec05590bed8a394f0f792dcb2a4
       labels:
         app: antrea
@@ -4031,7 +4036,7 @@ spec:
       annotations:
         # Automatically restart Pod if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: c74f29ceba3905db50cef22ee46f73e1c101c108a70e70918b17413c174081e8
+        checksum/config: 7b6ba1830aabcf74b5b0a71c74edf49254c4237d604f9d984428252903901f98
       labels:
         app: antrea
         component: antrea-controller
diff --git a/build/yamls/antrea-windows.yml b/build/yamls/antrea-windows.yml
index 7af06ec8e..21984166d 100644
--- a/build/yamls/antrea-windows.yml
+++ b/build/yamls/antrea-windows.yml
@@ -48,6 +48,11 @@ data:
     # - stt
     #tunnelType: geneve
 
+    # TunnelPort is the destination port for UDP and TCP based tunnel protocols
+    # (Geneve, VXLAN, and STT). If zero, it will use the assigned IANA port for the
+    # protocol, i.e. 6081 for Geneve, 4789 for VXLAN, and 7471 for STT.
+    #tunnelPort: 0
+
     # Default MTU to use for the host gateway interface and the network interface of each Pod.
     # If omitted, antrea-agent will discover the MTU of the Node's primary interface and
     # also adjust MTU to accommodate for tunnel encapsulation overhead.
@@ -163,7 +168,7 @@ kind: ConfigMap
 metadata:
   labels:
     app: antrea
-  name: antrea-windows-config-8kfkb8t957
+  name: antrea-windows-config-4mtgd4ghdf
   namespace: kube-system
 ---
 apiVersion: apps/v1
@@ -251,7 +256,7 @@ spec:
         operator: Exists
       volumes:
       - configMap:
-          name: antrea-windows-config-8kfkb8t957
+          name: antrea-windows-config-4mtgd4ghdf
         name: antrea-windows-config
       - configMap:
           defaultMode: 420
diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml
index c1f3d3782..356dc6290 100644
--- a/build/yamls/antrea.yml
+++ b/build/yamls/antrea.yml
@@ -2686,6 +2686,11 @@ data:
     # Note that "gre" is not supported for IPv6 clusters (IPv6-only or dual-stack clusters).
     tunnelType: "geneve"
 
+    # TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+    # If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+    # and 7471 for STT.
+    tunnelPort: 0
+
     # Determines how tunnel traffic is encrypted. Currently encryption only works with encap mode.
     # It has the following options:
     # - none (default):  Inter-node Pod traffic will not be encrypted.
@@ -3732,7 +3737,7 @@ spec:
         kubectl.kubernetes.io/default-container: antrea-agent
         # Automatically restart Pods with a RollingUpdate if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: 056a828ba2400e94aa9c43e6e74a4b007027bf6b95a68e1e15f34cd6ffeb2baa
+        checksum/config: 05340bff4942128434fb2b7ab2c0288d9586d3324d58da987c1a58db78aab6d3
       labels:
         app: antrea
         component: antrea-agent
@@ -3972,7 +3977,7 @@ spec:
       annotations:
         # Automatically restart Pod if the ConfigMap changes
         # See https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
-        checksum/config: 056a828ba2400e94aa9c43e6e74a4b007027bf6b95a68e1e15f34cd6ffeb2baa
+        checksum/config: 05340bff4942128434fb2b7ab2c0288d9586d3324d58da987c1a58db78aab6d3
       labels:
         app: antrea
         component: antrea-controller
diff --git a/build/yamls/windows/base/conf/antrea-agent.conf b/build/yamls/windows/base/conf/antrea-agent.conf
index ab72ae1e4..e9e4d633d 100644
--- a/build/yamls/windows/base/conf/antrea-agent.conf
+++ b/build/yamls/windows/base/conf/antrea-agent.conf
@@ -30,6 +30,11 @@ featureGates:
 # - stt
 #tunnelType: geneve
 
+# TunnelPort is the destination port for UDP and TCP based tunnel protocols
+# (Geneve, VXLAN, and STT). If zero, it will use the assigned IANA port for the
+# protocol, i.e. 6081 for Geneve, 4789 for VXLAN, and 7471 for STT.
+#tunnelPort: 0
+
 # Default MTU to use for the host gateway interface and the network interface of each Pod.
 # If omitted, antrea-agent will discover the MTU of the Node's primary interface and
 # also adjust MTU to accommodate for tunnel encapsulation overhead.
diff --git a/cmd/antrea-agent/agent.go b/cmd/antrea-agent/agent.go
index 78e715e60..2f0ea0695 100644
--- a/cmd/antrea-agent/agent.go
+++ b/cmd/antrea-agent/agent.go
@@ -162,6 +162,7 @@ func run(o *Options) error {
 	_, ipsecAuthenticationMode := config.GetIPsecAuthenticationModeFromStr(o.config.IPsec.AuthenticationMode)
 	networkConfig := &config.NetworkConfig{
 		TunnelType:            ovsconfig.TunnelType(o.config.TunnelType),
+		TunnelPort:            o.config.TunnelPort,
 		TrafficEncapMode:      encapMode,
 		TrafficEncryptionMode: encryptionMode,
 		TransportIface:        o.config.TransportInterface,
diff --git a/docs/antrea-network-policy.md b/docs/antrea-network-policy.md
index ff8186eb2..fcc18d6ea 100644
--- a/docs/antrea-network-policy.md
+++ b/docs/antrea-network-policy.md
@@ -647,11 +647,33 @@ The rules are logged in the following format:
     Deduplication:
     <yyyy/mm/dd> <time> <ovs-table-name> <antrea-native-policy-reference> <action> <openflow-priority> <source-ip> <source-port> <destination-ip> <destination-port> <protocol> <packet-length> [<num of packets> packets in <duplicate duration>]
 
-    Example:
+    Examples:
     2020/11/02 22:21:21.148395 AntreaPolicyAppTierIngressRule AntreaNetworkPolicy:default/test-anp Allow 61800 10.10.1.65 35402 10.0.0.5 80 TCP 60
     2021/06/24 23:56:41.346165 AntreaPolicyEgressRule AntreaNetworkPolicy:default/test-anp Drop 44900 10.10.1.65 35402 10.0.0.5 80 TCP 60 [3 packets in 1.011379442s]
 ```
 
+Kubernetes NetworkPolicies can also be audited using Antrea logging to the same file
+(`/var/log/antrea/networkpolicy/np.log`). Add Annotation
+`networkpolicy.antrea.io/enable-logging: "true` on a Namespace to enable logging
+for all NetworkPolicies in the Namespace. Packets of any connection that match
+a NetworkPolicy rule will be logged with a reference to the NetworkPolicy name,
+but packets dropped by the implicit "default drop" (not allowed by any NetworkPolicy)
+will only be logged with consistent name `K8sNetworkPolicy` for reference.
+Note that currently, Antrea only retrieves the logging Annotation once when adding
+NetworkPolicies and in case of agent restart, users should not update Namespace
+logging Annotations, otherwise it would risk NetworkPolicies working in a stale state.
+The rules are logged in the following format:
+
+```text
+    <yyyy/mm/dd> <time> <ovs-table-name> <k8s-network-policy-reference> Allow <openflow-priority> <source-ip> <source-port> <destination-ip> <destination-port> <protocol> <packet-length>
+    Default dropped traffic:
+    <yyyy/mm/dd> <time> <ovs-table-name> K8sNetworkPolicy Drop -1 <source-ip> <source-port> <destination-ip> <destination-port> <protocol> <packet-length> [<num of packets> packets in <duplicate duration>]
+
+    Examples:
+    2022/07/26 06:55:56.170456 IngressRule K8sNetworkPolicy:default/test-np-log Allow 190 10.10.1.82 49518 10.10.1.84 80 TCP 60
+    2022/07/26 06:55:57.142206 IngressDefaultRule K8sNetworkPolicy Drop -1 10.10.1.83 38608 10.10.1.84 80 TCP 60
+```
+
 Fluentd can be used to assist with collecting and analyzing the logs. Refer to the
 [Fluentd cookbook](cookbooks/fluentd) for documentation.
 
diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go
index 67a54f21f..9b1f51f95 100644
--- a/pkg/agent/agent.go
+++ b/pkg/agent/agent.go
@@ -726,6 +726,7 @@ func (i *Initializer) setupDefaultTunnelInterface() error {
 	if portExists {
 		if i.networkConfig.TrafficEncapMode.SupportsEncap() &&
 			tunnelIface.TunnelInterfaceConfig.Type == i.networkConfig.TunnelType &&
+			tunnelIface.TunnelInterfaceConfig.DestinationPort == i.networkConfig.TunnelPort &&
 			tunnelIface.TunnelInterfaceConfig.LocalIP.Equal(localIP) {
 			klog.V(2).Infof("Tunnel port %s already exists on OVS bridge", tunnelPortName)
 			// This could happen when upgrading from previous versions that didn't set it.
@@ -761,7 +762,12 @@ func (i *Initializer) setupDefaultTunnelInterface() error {
 		externalIDs := map[string]interface{}{
 			interfacestore.AntreaInterfaceTypeKey: interfacestore.AntreaTunnel,
 		}
-		tunnelPortUUID, err := i.ovsBridgeClient.CreateTunnelPortExt(tunnelPortName, i.networkConfig.TunnelType, config.DefaultTunOFPort, shouldEnableCsum, localIPStr, "", "", "", nil, externalIDs)
+		extraOptions := map[string]interface{}{}
+		if i.networkConfig.TunnelPort != 0 {
+			extraOptions["dst_port"] = strconv.Itoa(int(i.networkConfig.TunnelPort))
+		}
+		tunnelPortUUID, err := i.ovsBridgeClient.CreateTunnelPortExt(tunnelPortName,
+			i.networkConfig.TunnelType, config.DefaultTunOFPort, shouldEnableCsum, localIPStr, "", "", "", extraOptions, externalIDs)
 		if err != nil {
 			klog.ErrorS(err, "Failed to create tunnel port on OVS bridge", "port", tunnelPortName, "type", i.networkConfig.TunnelType)
 			return err
@@ -772,7 +778,7 @@ func (i *Initializer) setupDefaultTunnelInterface() error {
 			return err
 		}
 		klog.InfoS("Allocated OpenFlow port for tunnel interface", "port", tunnelPortName, "ofPort", tunPort)
-		tunnelIface = interfacestore.NewTunnelInterface(tunnelPortName, i.networkConfig.TunnelType, localIP, shouldEnableCsum)
+		tunnelIface = interfacestore.NewTunnelInterface(tunnelPortName, i.networkConfig.TunnelType, i.networkConfig.TunnelPort, localIP, shouldEnableCsum)
 		tunnelIface.OVSPortConfig = &interfacestore.OVSPortConfig{PortUUID: tunnelPortUUID, OFPort: tunPort}
 		i.ifaceStore.AddInterface(tunnelIface)
 		i.nodeConfig.TunnelOFPort = uint32(tunPort)
diff --git a/pkg/agent/config/node_config.go b/pkg/agent/config/node_config.go
index 650c25fc4..b230fb578 100644
--- a/pkg/agent/config/node_config.go
+++ b/pkg/agent/config/node_config.go
@@ -172,6 +172,7 @@ type IPsecConfig struct {
 type NetworkConfig struct {
 	TrafficEncapMode      TrafficEncapModeType
 	TunnelType            ovsconfig.TunnelType
+	TunnelPort            int32
 	TrafficEncryptionMode TrafficEncryptionModeType
 	IPsecConfig           IPsecConfig
 	TransportIface        string
diff --git a/pkg/agent/controller/networkpolicy/audit_logging.go b/pkg/agent/controller/networkpolicy/audit_logging.go
index 97265d12b..c29190374 100644
--- a/pkg/agent/controller/networkpolicy/audit_logging.go
+++ b/pkg/agent/controller/networkpolicy/audit_logging.go
@@ -28,6 +28,7 @@ import (
 	"k8s.io/klog/v2"
 
 	"antrea.io/antrea/pkg/agent/openflow"
+	"antrea.io/antrea/pkg/apis/controlplane/v1beta2"
 	binding "antrea.io/antrea/pkg/ovs/openflow"
 	"antrea.io/antrea/pkg/util/ip"
 	"antrea.io/antrea/pkg/util/logdir"
@@ -196,13 +197,17 @@ func getNetworkPolicyInfo(pktIn *ofctrl.PacketIn, c *Controller, ob *logInfo) er
 	// Set match to corresponding ingress/egress reg according to disposition.
 	match = getMatch(matchers, tableID, info)
 
-	// Get Network Policy full name and OF priority of the conjunction.
-	info, err = getInfoInReg(match, nil)
-	if err != nil {
-		return fmt.Errorf("received error while unloading conjunction id from reg: %v", err)
+	if match != nil {
+		// Get NetworkPolicy full name and OF priority of the conjunction.
+		info, err = getInfoInReg(match, nil)
+		if err != nil {
+			return fmt.Errorf("received error while unloading conjunction id from reg: %v", err)
+		}
+		ob.npRef, ob.ofPriority = c.ofClient.GetPolicyInfoFromConjunction(info)
+	} else {
+		// For K8s NetworkPolicy implicit drop action, we cannot get Namespace/name.
+		ob.npRef, ob.ofPriority = string(v1beta2.K8sNetworkPolicy), "-1"
 	}
-	ob.npRef, ob.ofPriority = c.ofClient.GetPolicyInfoFromConjunction(info)
-
 	return nil
 }
 
diff --git a/pkg/agent/controller/networkpolicy/packetin.go b/pkg/agent/controller/networkpolicy/packetin.go
index b14942149..7730641f1 100644
--- a/pkg/agent/controller/networkpolicy/packetin.go
+++ b/pkg/agent/controller/networkpolicy/packetin.go
@@ -180,7 +180,7 @@ func (c *Controller) storeDenyConnection(pktIn *ofctrl.PacketIn) error {
 			denyConn.EgressNetworkPolicyRuleAction = flowexporter.RuleActionToUint8(disposition)
 		}
 	} else {
-		// For K8s NetworkPolicy implicit drop action, we cannot get name/namespace.
+		// For K8s NetworkPolicy implicit drop action, we cannot get Namespace/name.
 		if tableID == openflow.IngressDefaultTable.GetID() {
 			denyConn.IngressNetworkPolicyType = registry.PolicyTypeK8sNetworkPolicy
 			denyConn.IngressNetworkPolicyRuleAction = flowexporter.RuleActionToUint8(disposition)
diff --git a/pkg/agent/controller/networkpolicy/reconciler.go b/pkg/agent/controller/networkpolicy/reconciler.go
index f08718d77..f8a0d2cf3 100644
--- a/pkg/agent/controller/networkpolicy/reconciler.go
+++ b/pkg/agent/controller/networkpolicy/reconciler.go
@@ -734,7 +734,7 @@ func (r *reconciler) update(lastRealized *lastRealized, newRule *CompletedRule,
 		} else {
 			addedTo := ofPortsToOFAddresses(newOFPorts.Difference(lastRealized.podOFPorts[igmpServicesKey]))
 			deletedTo := ofPortsToOFAddresses(lastRealized.podOFPorts[igmpServicesKey].Difference(newOFPorts))
-			if err := r.updateOFRule(ofID, nil, addedTo, nil, deletedTo, ofPriority); err != nil {
+			if err := r.updateOFRule(ofID, nil, addedTo, nil, deletedTo, ofPriority, newRule.EnableLogging); err != nil {
 				return err
 			}
 		}
@@ -803,7 +803,7 @@ func (r *reconciler) update(lastRealized *lastRealized, newRule *CompletedRule,
 					addedTo = ofPortsToOFAddresses(newOFPorts.Difference(originalOfPortsSet))
 					deletedTo = ofPortsToOFAddresses(originalOfPortsSet.Difference(newOFPorts))
 				}
-				if err := r.updateOFRule(ofID, addedFrom, addedTo, deletedFrom, deletedTo, ofPriority); err != nil {
+				if err := r.updateOFRule(ofID, addedFrom, addedTo, deletedFrom, deletedTo, ofPriority, newRule.EnableLogging); err != nil {
 					return err
 				}
 				// Delete valid servicesKey from staleOFIDs.
@@ -893,7 +893,7 @@ func (r *reconciler) update(lastRealized *lastRealized, newRule *CompletedRule,
 					addedTo = svcGroupIDsToOFAddresses(newGroupIDSet.Difference(originalGroupIDSet))
 					deletedTo = svcGroupIDsToOFAddresses(originalGroupIDSet.Difference(newGroupIDSet))
 				}
-				if err := r.updateOFRule(ofID, addedFrom, addedTo, deletedFrom, deletedTo, ofPriority); err != nil {
+				if err := r.updateOFRule(ofID, addedFrom, addedTo, deletedFrom, deletedTo, ofPriority, newRule.EnableLogging); err != nil {
 					return err
 				}
 				if r.fqdnController != nil {
@@ -930,17 +930,17 @@ func (r *reconciler) installOFRule(ofRule *types.PolicyRule) error {
 	return nil
 }
 
-func (r *reconciler) updateOFRule(ofID uint32, addedFrom []types.Address, addedTo []types.Address, deletedFrom []types.Address, deletedTo []types.Address, priority *uint16) error {
+func (r *reconciler) updateOFRule(ofID uint32, addedFrom []types.Address, addedTo []types.Address, deletedFrom []types.Address, deletedTo []types.Address, priority *uint16, enableLogging bool) error {
 	klog.V(2).Infof("Updating ofRule %d (addedFrom: %d, addedTo: %d, deleteFrom: %d, deletedTo: %d)",
 		ofID, len(addedFrom), len(addedTo), len(deletedFrom), len(deletedTo))
 	// TODO: This might be unnecessarily complex and hard for error handling, consider revising the Openflow interfaces.
 	if len(addedFrom) > 0 {
-		if err := r.ofClient.AddPolicyRuleAddress(ofID, types.SrcAddress, addedFrom, priority); err != nil {
+		if err := r.ofClient.AddPolicyRuleAddress(ofID, types.SrcAddress, addedFrom, priority, enableLogging); err != nil {
 			return fmt.Errorf("error adding policy rule source addresses for ofRule %v: %v", ofID, err)
 		}
 	}
 	if len(addedTo) > 0 {
-		if err := r.ofClient.AddPolicyRuleAddress(ofID, types.DstAddress, addedTo, priority); err != nil {
+		if err := r.ofClient.AddPolicyRuleAddress(ofID, types.DstAddress, addedTo, priority, enableLogging); err != nil {
 			return fmt.Errorf("error adding policy rule destination addresses for ofRule %v: %v", ofID, err)
 		}
 	}
diff --git a/pkg/agent/controller/noderoute/node_route_controller.go b/pkg/agent/controller/noderoute/node_route_controller.go
index 03f98df73..bcfae3ce5 100644
--- a/pkg/agent/controller/noderoute/node_route_controller.go
+++ b/pkg/agent/controller/noderoute/node_route_controller.go
@@ -729,7 +729,7 @@ func ParseTunnelInterfaceConfig(
 		klog.V(2).Infof("OVS port %s has no options", portData.Name)
 		return nil
 	}
-	remoteIP, localIP, psk, remoteName, csum := ovsconfig.ParseTunnelInterfaceOptions(portData)
+	remoteIP, localIP, tunnelPort, psk, remoteName, csum := ovsconfig.ParseTunnelInterfaceOptions(portData)
 
 	var interfaceConfig *interfacestore.InterfaceConfig
 	var nodeName string
@@ -746,7 +746,12 @@ func ParseTunnelInterfaceConfig(
 			remoteName,
 		)
 	} else {
-		interfaceConfig = interfacestore.NewTunnelInterface(portData.Name, ovsconfig.TunnelType(portData.IFType), localIP, csum)
+		interfaceConfig = interfacestore.NewTunnelInterface(
+			portData.Name,
+			ovsconfig.TunnelType(portData.IFType),
+			tunnelPort,
+			localIP,
+			csum)
 	}
 	interfaceConfig.OVSPortConfig = portConfig
 	return interfaceConfig
diff --git a/pkg/agent/interfacestore/types.go b/pkg/agent/interfacestore/types.go
index fa838a116..c5ea76029 100644
--- a/pkg/agent/interfacestore/types.go
+++ b/pkg/agent/interfacestore/types.go
@@ -145,8 +145,8 @@ func NewGatewayInterface(gatewayName string) *InterfaceConfig {
 
 // NewTunnelInterface creates InterfaceConfig for the default tunnel port
 // interface.
-func NewTunnelInterface(tunnelName string, tunnelType ovsconfig.TunnelType, localIP net.IP, csum bool) *InterfaceConfig {
-	tunnelConfig := &TunnelInterfaceConfig{Type: tunnelType, LocalIP: localIP, Csum: csum}
+func NewTunnelInterface(tunnelName string, tunnelType ovsconfig.TunnelType, destinationPort int32, localIP net.IP, csum bool) *InterfaceConfig {
+	tunnelConfig := &TunnelInterfaceConfig{Type: tunnelType, DestinationPort: destinationPort, LocalIP: localIP, Csum: csum}
 	return &InterfaceConfig{InterfaceName: tunnelName, Type: TunnelInterface, TunnelInterfaceConfig: tunnelConfig}
 }
 
diff --git a/pkg/agent/openflow/client.go b/pkg/agent/openflow/client.go
index cd0cea0b9..f3a791744 100644
--- a/pkg/agent/openflow/client.go
+++ b/pkg/agent/openflow/client.go
@@ -125,7 +125,7 @@ type Client interface {
 
 	// AddPolicyRuleAddress adds one or multiple addresses to the specified NetworkPolicy rule. If addrType is true, the
 	// addresses are added to PolicyRule.From, else to PolicyRule.To.
-	AddPolicyRuleAddress(ruleID uint32, addrType types.AddressType, addresses []types.Address, priority *uint16) error
+	AddPolicyRuleAddress(ruleID uint32, addrType types.AddressType, addresses []types.Address, priority *uint16, enableLogging bool) error
 
 	// DeletePolicyRuleAddress removes addresses from the specified NetworkPolicy rule. If addrType is srcAddress, the addresses
 	// are removed from PolicyRule.From, else from PolicyRule.To.
diff --git a/pkg/agent/openflow/network_policy.go b/pkg/agent/openflow/network_policy.go
index 20624b95e..489a956dc 100644
--- a/pkg/agent/openflow/network_policy.go
+++ b/pkg/agent/openflow/network_policy.go
@@ -422,6 +422,8 @@ type conjMatchFlowContext struct {
 	// empty, and uninstalled when both two are empty. When the dropFlow is uninstalled from the switch, the
 	// conjMatchFlowContext is removed from the cache.
 	dropFlow binding.Flow
+	// dropFlowEnableLogging describes the logging requirement of the dropFlow.
+	dropFlowEnableLogging bool
 }
 
 // createOrUpdateConjunctiveMatchFlow creates or updates the conjunctive match flow with the latest actions. It returns
@@ -684,7 +686,7 @@ func (c *client) NewDNSpacketInConjunction(id uint32) error {
 
 	c.featureNetworkPolicy.conjMatchFlowLock.Lock()
 	defer c.featureNetworkPolicy.conjMatchFlowLock.Unlock()
-	ctxChanges := conj.serviceClause.addServiceFlows(c.featureNetworkPolicy, []v1beta2.Service{udpService}, &dnsPriority, true)
+	ctxChanges := conj.serviceClause.addServiceFlows(c.featureNetworkPolicy, []v1beta2.Service{udpService}, &dnsPriority, true, false)
 	if err := c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges); err != nil {
 		return err
 	}
@@ -695,7 +697,7 @@ func (c *client) NewDNSpacketInConjunction(id uint32) error {
 
 func (c *client) AddAddressToDNSConjunction(id uint32, addrs []types.Address) error {
 	dnsPriority := priorityDNSIntercept
-	return c.AddPolicyRuleAddress(id, types.DstAddress, addrs, &dnsPriority)
+	return c.AddPolicyRuleAddress(id, types.DstAddress, addrs, &dnsPriority, false)
 }
 
 func (c *client) DeleteAddressFromDNSConjunction(id uint32, addrs []types.Address) error {
@@ -703,7 +705,7 @@ func (c *client) DeleteAddressFromDNSConjunction(id uint32, addrs []types.Addres
 	return c.DeletePolicyRuleAddress(id, types.DstAddress, addrs, &dnsPriority)
 }
 
-func (c *clause) addConjunctiveMatchFlow(featureNetworkPolicy *featureNetworkPolicy, match *conjunctiveMatch) *conjMatchFlowContextChange {
+func (c *clause) addConjunctiveMatchFlow(featureNetworkPolicy *featureNetworkPolicy, match *conjunctiveMatch, enableLogging bool) *conjMatchFlowContextChange {
 	matcherKey := match.generateGlobalMapKey()
 	_, found := c.matches[matcherKey]
 	if found {
@@ -718,19 +720,29 @@ func (c *clause) addConjunctiveMatchFlow(featureNetworkPolicy *featureNetworkPol
 	context, found = featureNetworkPolicy.globalConjMatchFlowCache[matcherKey]
 	if !found {
 		context = &conjMatchFlowContext{
-			conjunctiveMatch:     match,
-			actions:              make(map[uint32]*conjunctiveAction),
-			featureNetworkPolicy: featureNetworkPolicy,
+			conjunctiveMatch:      match,
+			actions:               make(map[uint32]*conjunctiveAction),
+			featureNetworkPolicy:  featureNetworkPolicy,
+			dropFlowEnableLogging: enableLogging,
 		}
 		ctxType = insertion
 
 		// Generate the default drop flow if dropTable is not nil and the default drop flow is not set yet.
 		if c.dropTable != nil && context.dropFlow == nil {
 			dropFlow = &flowChange{
-				flow:       context.featureNetworkPolicy.defaultDropFlow(c.dropTable, match.matchPairs),
+				flow:       context.featureNetworkPolicy.defaultDropFlow(c.dropTable, match.matchPairs, enableLogging),
 				changeType: insertion,
 			}
 		}
+	} else if context.dropFlowEnableLogging != enableLogging {
+		// Logging requirement of the rule has changed, modify default drop flow accordingly.
+		context.dropFlowEnableLogging = enableLogging
+		if c.dropTable != nil && context.dropFlow != nil {
+			dropFlow = &flowChange{
+				flow:       context.featureNetworkPolicy.defaultDropFlow(c.dropTable, match.matchPairs, enableLogging),
+				changeType: modification,
+			}
+		}
 	}
 
 	// Calculate the change on the conjMatchFlowContext.
@@ -923,12 +935,12 @@ func serviceToBitRanges(service v1beta2.Service) []types.BitRange {
 
 // addAddrFlows translates the specified addresses to conjunctiveMatchFlows, and returns the corresponding changes on the
 // conjunctiveMatchFlows.
-func (c *clause) addAddrFlows(featureNetworkPolicy *featureNetworkPolicy, addrType types.AddressType, addresses []types.Address, priority *uint16) []*conjMatchFlowContextChange {
+func (c *clause) addAddrFlows(featureNetworkPolicy *featureNetworkPolicy, addrType types.AddressType, addresses []types.Address, priority *uint16, enableLogging bool) []*conjMatchFlowContextChange {
 	var conjMatchFlowContextChanges []*conjMatchFlowContextChange
 	// Calculate Openflow changes for the added addresses.
 	for _, addr := range addresses {
 		match := generateAddressConjMatch(c.ruleTable.GetID(), addr, addrType, priority)
-		ctxChange := c.addConjunctiveMatchFlow(featureNetworkPolicy, match)
+		ctxChange := c.addConjunctiveMatchFlow(featureNetworkPolicy, match, enableLogging)
 		if ctxChange != nil {
 			conjMatchFlowContextChanges = append(conjMatchFlowContextChanges, ctxChange)
 		}
@@ -938,12 +950,12 @@ func (c *clause) addAddrFlows(featureNetworkPolicy *featureNetworkPolicy, addrTy
 
 // addServiceFlows translates the specified Antrea Service to conjunctiveMatchFlow,
 // and returns corresponding conjMatchFlowContextChange.
-func (c *clause) addServiceFlows(featureNetworkPolicy *featureNetworkPolicy, services []v1beta2.Service, priority *uint16, matchSrc bool) []*conjMatchFlowContextChange {
+func (c *clause) addServiceFlows(featureNetworkPolicy *featureNetworkPolicy, services []v1beta2.Service, priority *uint16, matchSrc bool, enableLogging bool) []*conjMatchFlowContextChange {
 	var conjMatchFlowContextChanges []*conjMatchFlowContextChange
 	for _, service := range services {
 		matches := generateServiceConjMatches(c.ruleTable.GetID(), service, priority, featureNetworkPolicy.ipProtocols, matchSrc)
 		for _, match := range matches {
-			ctxChange := c.addConjunctiveMatchFlow(featureNetworkPolicy, match)
+			ctxChange := c.addConjunctiveMatchFlow(featureNetworkPolicy, match, enableLogging)
 			conjMatchFlowContextChanges = append(conjMatchFlowContextChanges, ctxChange)
 		}
 	}
@@ -1143,20 +1155,20 @@ func (f *featureNetworkPolicy) addRuleToConjunctiveMatch(conj *policyRuleConjunc
 	if conj.fromClause != nil {
 		for _, addr := range rule.From {
 			match := generateAddressConjMatch(conj.fromClause.ruleTable.GetID(), addr, types.SrcAddress, rule.Priority)
-			f.addActionToConjunctiveMatch(conj.fromClause, match)
+			f.addActionToConjunctiveMatch(conj.fromClause, match, rule.EnableLogging)
 		}
 	}
 	if conj.toClause != nil {
 		for _, addr := range rule.To {
 			match := generateAddressConjMatch(conj.toClause.ruleTable.GetID(), addr, types.DstAddress, rule.Priority)
-			f.addActionToConjunctiveMatch(conj.toClause, match)
+			f.addActionToConjunctiveMatch(conj.toClause, match, rule.EnableLogging)
 		}
 	}
 	if conj.serviceClause != nil {
 		for _, eachService := range rule.Service {
 			matches := generateServiceConjMatches(conj.serviceClause.ruleTable.GetID(), eachService, rule.Priority, f.ipProtocols, false)
 			for _, match := range matches {
-				f.addActionToConjunctiveMatch(conj.serviceClause, match)
+				f.addActionToConjunctiveMatch(conj.serviceClause, match, rule.EnableLogging)
 			}
 		}
 	}
@@ -1165,7 +1177,7 @@ func (f *featureNetworkPolicy) addRuleToConjunctiveMatch(conj *policyRuleConjunc
 // addActionToConjunctiveMatch adds a clause to corresponding conjunctive match context.
 // It updates the context status directly and doesn't calculate the match flow, which is supposed to be calculated after
 // all actions are added. It's used in initial batch install only.
-func (f *featureNetworkPolicy) addActionToConjunctiveMatch(clause *clause, match *conjunctiveMatch) {
+func (f *featureNetworkPolicy) addActionToConjunctiveMatch(clause *clause, match *conjunctiveMatch, enableLogging bool) {
 	matcherKey := match.generateGlobalMapKey()
 	_, found := clause.matches[matcherKey]
 	if found {
@@ -1178,13 +1190,14 @@ func (f *featureNetworkPolicy) addActionToConjunctiveMatch(clause *clause, match
 	context, found = f.globalConjMatchFlowCache[matcherKey]
 	if !found {
 		context = &conjMatchFlowContext{
-			conjunctiveMatch:     match,
-			actions:              make(map[uint32]*conjunctiveAction),
-			featureNetworkPolicy: f,
+			conjunctiveMatch:      match,
+			actions:               make(map[uint32]*conjunctiveAction),
+			featureNetworkPolicy:  f,
+			dropFlowEnableLogging: enableLogging,
 		}
 		// Generate the default drop flow if dropTable is not nil.
 		if clause.dropTable != nil {
-			context.dropFlow = context.featureNetworkPolicy.defaultDropFlow(clause.dropTable, match.matchPairs)
+			context.dropFlow = context.featureNetworkPolicy.defaultDropFlow(clause.dropTable, match.matchPairs, enableLogging)
 		}
 		f.globalConjMatchFlowCache[matcherKey] = context
 	}
@@ -1371,13 +1384,13 @@ func (c *policyRuleConjunction) calculateClauses(rule *types.PolicyRule) (uint8,
 func (c *policyRuleConjunction) calculateChangesForRuleCreation(featureNetworkPolicy *featureNetworkPolicy, rule *types.PolicyRule) []*conjMatchFlowContextChange {
 	var ctxChanges []*conjMatchFlowContextChange
 	if c.fromClause != nil {
-		ctxChanges = append(ctxChanges, c.fromClause.addAddrFlows(featureNetworkPolicy, types.SrcAddress, rule.From, rule.Priority)...)
+		ctxChanges = append(ctxChanges, c.fromClause.addAddrFlows(featureNetworkPolicy, types.SrcAddress, rule.From, rule.Priority, rule.EnableLogging)...)
 	}
 	if c.toClause != nil {
-		ctxChanges = append(ctxChanges, c.toClause.addAddrFlows(featureNetworkPolicy, types.DstAddress, rule.To, rule.Priority)...)
+		ctxChanges = append(ctxChanges, c.toClause.addAddrFlows(featureNetworkPolicy, types.DstAddress, rule.To, rule.Priority, rule.EnableLogging)...)
 	}
 	if c.serviceClause != nil {
-		ctxChanges = append(ctxChanges, c.serviceClause.addServiceFlows(featureNetworkPolicy, rule.Service, rule.Priority, false)...)
+		ctxChanges = append(ctxChanges, c.serviceClause.addServiceFlows(featureNetworkPolicy, rule.Service, rule.Priority, false, rule.EnableLogging)...)
 	}
 	return ctxChanges
 }
@@ -1550,7 +1563,7 @@ func (f *featureNetworkPolicy) replayFlows() []binding.Flow {
 
 // AddPolicyRuleAddress adds one or multiple addresses to the specified NetworkPolicy rule. If addrType is srcAddress, the
 // addresses are added to PolicyRule.From, else to PolicyRule.To.
-func (c *client) AddPolicyRuleAddress(ruleID uint32, addrType types.AddressType, addresses []types.Address, priority *uint16) error {
+func (c *client) AddPolicyRuleAddress(ruleID uint32, addrType types.AddressType, addresses []types.Address, priority *uint16, enableLogging bool) error {
 	c.replayMutex.RLock()
 	defer c.replayMutex.RUnlock()
 
@@ -1569,7 +1582,7 @@ func (c *client) AddPolicyRuleAddress(ruleID uint32, addrType types.AddressType,
 
 	c.featureNetworkPolicy.conjMatchFlowLock.Lock()
 	defer c.featureNetworkPolicy.conjMatchFlowLock.Unlock()
-	flowChanges := clause.addAddrFlows(c.featureNetworkPolicy, addrType, addresses, priority)
+	flowChanges := clause.addAddrFlows(c.featureNetworkPolicy, addrType, addresses, priority, enableLogging)
 	return c.featureNetworkPolicy.applyConjunctiveMatchFlows(flowChanges)
 }
 
diff --git a/pkg/agent/openflow/pipeline.go b/pkg/agent/openflow/pipeline.go
index 0abfae8c1..0bf44bb7e 100644
--- a/pkg/agent/openflow/pipeline.go
+++ b/pkg/agent/openflow/pipeline.go
@@ -2118,22 +2118,28 @@ func (f *featureNetworkPolicy) conjunctiveMatchFlow(tableID uint8, matchPairs []
 }
 
 // defaultDropFlow generates the flow to drop packets if the match condition is matched.
-func (f *featureNetworkPolicy) defaultDropFlow(table binding.Table, matchPairs []matchPair) binding.Flow {
+func (f *featureNetworkPolicy) defaultDropFlow(table binding.Table, matchPairs []matchPair, enableLogging bool) binding.Flow {
 	cookieID := f.cookieAllocator.Request(f.category).Raw()
 	fb := table.BuildFlow(priorityNormal)
 	for _, eachMatchPair := range matchPairs {
 		fb = f.addFlowMatch(fb, eachMatchPair.matchKey, eachMatchPair.matchValue)
 	}
+	fb = fb.Action().Drop()
+
+	var customReason int
 	if f.enableDenyTracking {
-		return fb.Action().Drop().
-			Action().LoadRegMark(DispositionDropRegMark, CustomReasonDenyRegMark).
-			Action().SendToController(uint8(PacketInReasonNP)).
-			Cookie(cookieID).
-			Done()
+		customReason += CustomReasonDeny
 	}
-	return fb.Action().Drop().
-		Cookie(cookieID).
-		Done()
+	if enableLogging {
+		customReason += CustomReasonLogging
+	}
+
+	if enableLogging || f.enableDenyTracking {
+		fb = fb.Action().LoadRegMark(DispositionDropRegMark).
+			Action().LoadToRegField(CustomReasonField, uint32(customReason)).
+			Action().SendToController(uint8(PacketInReasonNP))
+	}
+	return fb.Cookie(cookieID).Done()
 }
 
 // dnsPacketInFlow generates the flow to send dns response packets of fqdn policy selected Pods to the fqdnController for
diff --git a/pkg/config/agent/config.go b/pkg/config/agent/config.go
index 2d0d9d43a..caf4bbe86 100644
--- a/pkg/config/agent/config.go
+++ b/pkg/config/agent/config.go
@@ -71,6 +71,10 @@ type AgentConfig struct {
 	// - gre
 	// - stt
 	TunnelType string `yaml:"tunnelType,omitempty"`
+	// TunnelPort is the destination port for UDP and TCP based tunnel protocols (Geneve, VXLAN, and STT).
+	// If zero, it will use the assigned IANA port for the protocol, i.e. 6081 for Geneve, 4789 for VXLAN,
+	// and 7471 for STT.
+	TunnelPort int32 `yaml:"tunnelPort,omitempty"`
 	// Default MTU to use for the host gateway interface and the network interface of each Pod.
 	// If omitted, antrea-agent will discover the MTU of the Node's primary interface and
 	// also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable).
diff --git a/pkg/controller/networkpolicy/clusternetworkpolicy.go b/pkg/controller/networkpolicy/clusternetworkpolicy.go
index cd6ce44a9..886c5abdf 100644
--- a/pkg/controller/networkpolicy/clusternetworkpolicy.go
+++ b/pkg/controller/networkpolicy/clusternetworkpolicy.go
@@ -214,10 +214,12 @@ func (n *NetworkPolicyController) addNamespace(obj interface{}) {
 
 // updateNamespace receives Namespace UPDATE events and triggers all ClusterNetworkPolicies that have a
 // per-namespace rule applied to either the original or the new Namespace to be re-processed.
+// It also triggers all K8s NetworkPolicies in the new Namespace to be re-processed
+// if the logging Annotation changes.
 func (n *NetworkPolicyController) updateNamespace(oldObj, curObj interface{}) {
 	defer n.heartbeat("updateNamespace")
 	oldNamespace, curNamespace := oldObj.(*v1.Namespace), curObj.(*v1.Namespace)
-	klog.V(2).Infof("Processing Namespace %s UPDATE event, labels: %v", curNamespace.Name, curNamespace.Labels)
+	klog.V(2).Infof("Processing Namespace %s UPDATE event, labels: %v, annotations: %v", curNamespace.Name, curNamespace.Labels, curNamespace.Annotations)
 	oldLabelSet, curLabelSet := labels.Set(oldNamespace.Labels), labels.Set(curNamespace.Labels)
 	affectedACNPsByOldLabels := n.filterPerNamespaceRuleACNPsByNSLabels(oldLabelSet)
 	affectedACNPsByCurLabels := n.filterPerNamespaceRuleACNPsByNSLabels(curLabelSet)
@@ -227,6 +229,12 @@ func (n *NetworkPolicyController) updateNamespace(oldObj, curObj interface{}) {
 			n.reprocessCNP(cnp, false)
 		}
 	}
+	if oldNamespace.Annotations[EnableNPLoggingAnnotationKey] != curNamespace.Annotations[EnableNPLoggingAnnotationKey] {
+		affectedNPs, _ := n.networkPolicyLister.NetworkPolicies(curNamespace.Name).List(labels.Everything())
+		for _, np := range affectedNPs {
+			n.updateNetworkPolicy(np, np)
+		}
+	}
 }
 
 // deleteNamespace receives Namespace DELETE events and triggers all ClusterNetworkPolicies that have a
diff --git a/pkg/controller/networkpolicy/networkpolicy_controller.go b/pkg/controller/networkpolicy/networkpolicy_controller.go
index 02d7856f8..ce5e819ea 100644
--- a/pkg/controller/networkpolicy/networkpolicy_controller.go
+++ b/pkg/controller/networkpolicy/networkpolicy_controller.go
@@ -81,6 +81,8 @@ const (
 	PriorityIndex = "priority"
 	// ClusterGroupIndex is used to index ClusterNetworkPolicies by ClusterGroup names.
 	ClusterGroupIndex = "clustergroup"
+	// EnableNPLoggingAnnotationKey can be added to Namespace to enable logging K8s NP.
+	EnableNPLoggingAnnotationKey = "networkpolicy.antrea.io/enable-logging"
 
 	appliedToGroupType grouping.GroupType = "appliedToGroup"
 	addressGroupType   grouping.GroupType = "addressGroup"
@@ -566,6 +568,12 @@ func (n *NetworkPolicyController) processNetworkPolicy(np *networkingv1.NetworkP
 	appliedToGroupKey := n.createAppliedToGroup(np.Namespace, &np.Spec.PodSelector, nil, nil)
 	appliedToGroupNames := []string{appliedToGroupKey}
 	rules := make([]controlplane.NetworkPolicyRule, 0, len(np.Spec.Ingress)+len(np.Spec.Egress))
+	// Retrieve Namespace logging annotation.
+	enableLogging := false
+	namespace, err := n.namespaceLister.Get(np.Namespace)
+	if err == nil {
+		enableLogging, _ = strconv.ParseBool(namespace.Annotations[EnableNPLoggingAnnotationKey])
+	}
 	var ingressRuleExists, egressRuleExists bool
 	// Compute NetworkPolicyRule for Ingress Rule.
 	for _, ingressRule := range np.Spec.Ingress {
@@ -577,7 +585,7 @@ func (n *NetworkPolicyController) processNetworkPolicy(np *networkingv1.NetworkP
 			Services:      services,
 			Priority:      defaultRulePriority,
 			Action:        &defaultAction,
-			EnableLogging: false,
+			EnableLogging: enableLogging,
 		})
 	}
 	// Compute NetworkPolicyRule for Egress Rule.
@@ -590,7 +598,7 @@ func (n *NetworkPolicyController) processNetworkPolicy(np *networkingv1.NetworkP
 			Services:      services,
 			Priority:      defaultRulePriority,
 			Action:        &defaultAction,
-			EnableLogging: false,
+			EnableLogging: enableLogging,
 		})
 	}
 
diff --git a/pkg/ovs/ovsconfig/ovs_client.go b/pkg/ovs/ovsconfig/ovs_client.go
index 69dbf108e..b214052ba 100644
--- a/pkg/ovs/ovsconfig/ovs_client.go
+++ b/pkg/ovs/ovsconfig/ovs_client.go
@@ -500,15 +500,16 @@ func (br *OVSBridge) SetInterfaceOptions(name string, options map[string]interfa
 
 // ParseTunnelInterfaceOptions reads remote IP, local IP, IPsec PSK, and csum
 // from the tunnel interface options and returns them.
-func ParseTunnelInterfaceOptions(portData *OVSPortData) (net.IP, net.IP, string, string, bool) {
+func ParseTunnelInterfaceOptions(portData *OVSPortData) (net.IP, net.IP, int32, string, string, bool) {
 	if portData.Options == nil {
-		return nil, nil, "", "", false
+		return nil, nil, 0, "", "", false
 	}
 
 	var ok bool
 	var remoteIPStr, localIPStr, psk, remoteName string
 	var remoteIP, localIP net.IP
 	var csum bool
+	var destinationPort int64
 
 	if remoteIPStr, ok = portData.Options["remote_ip"]; ok {
 		if remoteIPStr != "flow" {
@@ -523,7 +524,10 @@ func ParseTunnelInterfaceOptions(portData *OVSPortData) (net.IP, net.IP, string,
 		csum, _ = strconv.ParseBool(csumStr)
 	}
 	remoteName = portData.Options["remote_name"]
-	return remoteIP, localIP, psk, remoteName, csum
+	if destinationPortStr, ok := portData.Options["dst_port"]; ok {
+		destinationPort, _ = strconv.ParseInt(destinationPortStr, 10, 32)
+	}
+	return remoteIP, localIP, int32(destinationPort), psk, remoteName, csum
 }
 
 // CreateUplinkPort creates uplink port.


Test patch:
diff --git a/pkg/agent/controller/networkpolicy/reconciler_test.go b/pkg/agent/controller/networkpolicy/reconciler_test.go
index 0560113ba..9cfb32266 100644
--- a/pkg/agent/controller/networkpolicy/reconciler_test.go
+++ b/pkg/agent/controller/networkpolicy/reconciler_test.go
@@ -1256,10 +1256,10 @@ func TestReconcilerUpdate(t *testing.T) {
 				mockOFClient.EXPECT().UninstallPolicyRuleFlows(gomock.Any())
 			}
 			if len(tt.expectedAddedFrom) > 0 {
-				mockOFClient.EXPECT().AddPolicyRuleAddress(gomock.Any(), types.SrcAddress, gomock.InAnyOrder(tt.expectedAddedFrom), priority)
+				mockOFClient.EXPECT().AddPolicyRuleAddress(gomock.Any(), types.SrcAddress, gomock.InAnyOrder(tt.expectedAddedFrom), priority, false)
 			}
 			if len(tt.expectedAddedTo) > 0 {
-				mockOFClient.EXPECT().AddPolicyRuleAddress(gomock.Any(), types.DstAddress, gomock.InAnyOrder(tt.expectedAddedTo), priority)
+				mockOFClient.EXPECT().AddPolicyRuleAddress(gomock.Any(), types.DstAddress, gomock.InAnyOrder(tt.expectedAddedTo), priority, false)
 			}
 			if len(tt.expectedDeletedFrom) > 0 {
 				mockOFClient.EXPECT().DeletePolicyRuleAddress(gomock.Any(), types.SrcAddress, gomock.InAnyOrder(tt.expectedDeletedFrom), priority)
diff --git a/pkg/agent/multicast/mcast_discovery_test.go b/pkg/agent/multicast/mcast_discovery_test.go
index 620b69d7c..b23dff218 100644
--- a/pkg/agent/multicast/mcast_discovery_test.go
+++ b/pkg/agent/multicast/mcast_discovery_test.go
@@ -180,7 +180,7 @@ func generatePacketInForRemoteReport(t *testing.T, snooper *IGMPSnooper, groups
 }
 
 func createTunnelInterface(tunnelPort uint32, localNodeIP net.IP) *interfacestore.InterfaceConfig {
-	tunnelInterface := interfacestore.NewTunnelInterface("antrea-tun0", ovsconfig.GeneveTunnel, localNodeIP, false)
+	tunnelInterface := interfacestore.NewTunnelInterface("antrea-tun0", ovsconfig.GeneveTunnel, 6081, localNodeIP, false)
 	tunnelInterface.OVSPortConfig = &interfacestore.OVSPortConfig{OFPort: int32(tunnelPort)}
 	return tunnelInterface
 }
diff --git a/pkg/agent/openflow/client_test.go b/pkg/agent/openflow/client_test.go
index d698ab6fe..b3043a52b 100644
--- a/pkg/agent/openflow/client_test.go
+++ b/pkg/agent/openflow/client_test.go
@@ -437,7 +437,7 @@ func prepareTraceflowFlow(ctrl *gomock.Controller) *client {
 	c.bridge = ovsoftest.NewMockBridge(ctrl)
 
 	mFlow := ovsoftest.NewMockFlow(ctrl)
-	ctx := &conjMatchFlowContext{dropFlow: mFlow}
+	ctx := &conjMatchFlowContext{dropFlow: mFlow, dropFlowEnableLogging: false}
 	mFlow.EXPECT().FlowProtocol().Return(binding.Protocol("ip"))
 	mFlow.EXPECT().CopyToBuilder(priorityNormal+2, false).Return(EgressDefaultTable.ofTable.BuildFlow(priorityNormal + 2)).Times(1)
 	c.featureNetworkPolicy.globalConjMatchFlowCache["mockContext"] = ctx
diff --git a/pkg/agent/openflow/network_policy_test.go b/pkg/agent/openflow/network_policy_test.go
index 89eb37fea..614941811 100644
--- a/pkg/agent/openflow/network_policy_test.go
+++ b/pkg/agent/openflow/network_policy_test.go
@@ -105,7 +105,7 @@ func TestPolicyRuleConjunction(t *testing.T) {
 
 	var addedAddrs = parseAddresses([]string{"192.168.1.3", "192.168.1.30", "192.168.2.0/24", "103", "104"})
 	expectConjunctionsCount([]*expectConjunctionTimes{{5, ruleID1, clauseID, nClause}})
-	flowChanges1 := clause1.addAddrFlows(c.featureNetworkPolicy, types.SrcAddress, addedAddrs, nil)
+	flowChanges1 := clause1.addAddrFlows(c.featureNetworkPolicy, types.SrcAddress, addedAddrs, nil, false)
 	err := c.featureNetworkPolicy.applyConjunctiveMatchFlows(flowChanges1)
 	require.Nil(t, err, "Failed to invoke addAddrFlows")
 	checkFlowCount(t, len(addedAddrs))
@@ -130,7 +130,7 @@ func TestPolicyRuleConjunction(t *testing.T) {
 	var addedAddrs2 = parseAddresses([]string{"192.168.1.30", "192.168.1.50"})
 	expectConjunctionsCount([]*expectConjunctionTimes{{2, ruleID2, clauseID2, nClause}})
 	expectConjunctionsCount([]*expectConjunctionTimes{{1, ruleID1, clauseID, nClause}})
-	flowChanges3 := clause2.addAddrFlows(c.featureNetworkPolicy, types.SrcAddress, addedAddrs2, nil)
+	flowChanges3 := clause2.addAddrFlows(c.featureNetworkPolicy, types.SrcAddress, addedAddrs2, nil, false)
 	err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(flowChanges3)
 	require.Nil(t, err, "Failed to invoke addAddrFlows")
 	testAddr := NewIPAddress(net.ParseIP("192.168.1.30"))
@@ -146,7 +146,7 @@ func TestPolicyRuleConjunction(t *testing.T) {
 	nClause3 := uint8(1)
 	clause3 := conj3.newClause(clauseID3, nClause3, mockEgressRuleTable, mockEgressDefaultTable)
 	var addedAddrs3 = parseAddresses([]string{"192.168.1.30"})
-	flowChanges4 := clause3.addAddrFlows(c.featureNetworkPolicy, types.SrcAddress, addedAddrs3, nil)
+	flowChanges4 := clause3.addAddrFlows(c.featureNetworkPolicy, types.SrcAddress, addedAddrs3, nil, false)
 	err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(flowChanges4)
 	require.Nil(t, err, "Failed to invoke addAddrFlows")
 	checkConjMatchFlowActions(t, c, clause3, testAddr, types.SrcAddress, 2, 1)
@@ -643,7 +643,7 @@ func TestConjMatchFlowContextKeyConflict(t *testing.T) {
 		id: ruleID1,
 	}
 	clause1 := conj1.newClause(1, 3, mockEgressRuleTable, mockEgressDefaultTable)
-	flowChange1 := clause1.addAddrFlows(c.featureNetworkPolicy, types.DstAddress, parseAddresses([]string{ip.String()}), nil)
+	flowChange1 := clause1.addAddrFlows(c.featureNetworkPolicy, types.DstAddress, parseAddresses([]string{ip.String()}), nil, false)
 	err := c.featureNetworkPolicy.applyConjunctiveMatchFlows(flowChange1)
 	require.Nil(t, err, "no error expect in applyConjunctiveMatchFlows")
 
@@ -652,7 +652,7 @@ func TestConjMatchFlowContextKeyConflict(t *testing.T) {
 		id: ruleID2,
 	}
 	clause2 := conj2.newClause(1, 3, mockEgressRuleTable, mockEgressDefaultTable)
-	flowChange2 := clause2.addAddrFlows(c.featureNetworkPolicy, types.DstAddress, parseAddresses([]string{ipNet.String()}), nil)
+	flowChange2 := clause2.addAddrFlows(c.featureNetworkPolicy, types.DstAddress, parseAddresses([]string{ipNet.String()}), nil, false)
 	err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(flowChange2)
 	require.Nil(t, err, "no error expect in applyConjunctiveMatchFlows")
 	expectedMatchKey := fmt.Sprintf("table:%d,priority:%s,matchPair:%s", EgressRuleTable.GetID(), strconv.Itoa(int(priorityNormal)), singleMatchPair.KeyString())
diff --git a/pkg/agent/openflow/testing/mock_openflow.go b/pkg/agent/openflow/testing/mock_openflow.go
index cfb9be05f..d3e7d0ea6 100644
--- a/pkg/agent/openflow/testing/mock_openflow.go
+++ b/pkg/agent/openflow/testing/mock_openflow.go
@@ -71,17 +71,17 @@ func (mr *MockClientMockRecorder) AddAddressToDNSConjunction(arg0, arg1 interfac
 }
 
 // AddPolicyRuleAddress mocks base method
-func (m *MockClient) AddPolicyRuleAddress(arg0 uint32, arg1 types.AddressType, arg2 []types.Address, arg3 *uint16) error {
+func (m *MockClient) AddPolicyRuleAddress(arg0 uint32, arg1 types.AddressType, arg2 []types.Address, arg3 *uint16, arg4 bool) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AddPolicyRuleAddress", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "AddPolicyRuleAddress", arg0, arg1, arg2, arg3, arg4)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // AddPolicyRuleAddress indicates an expected call of AddPolicyRuleAddress
-func (mr *MockClientMockRecorder) AddPolicyRuleAddress(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+func (mr *MockClientMockRecorder) AddPolicyRuleAddress(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPolicyRuleAddress", reflect.TypeOf((*MockClient)(nil).AddPolicyRuleAddress), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPolicyRuleAddress", reflect.TypeOf((*MockClient)(nil).AddPolicyRuleAddress), arg0, arg1, arg2, arg3, arg4)
 }
 
 // BatchInstallPolicyRuleFlows mocks base method
diff --git a/pkg/controller/networkpolicy/networkpolicy_controller_test.go b/pkg/controller/networkpolicy/networkpolicy_controller_test.go
index b7b4f1ca1..76a85c8a0 100644
--- a/pkg/controller/networkpolicy/networkpolicy_controller_test.go
+++ b/pkg/controller/networkpolicy/networkpolicy_controller_test.go
@@ -158,6 +158,7 @@ func newControllerWithoutEventHandler(k8sObjects, crdObjects []runtime.Object) (
 	addressGroupStore := store.NewAddressGroupStore()
 	internalNetworkPolicyStore := store.NewNetworkPolicyStore()
 	internalGroupStore := store.NewGroupStore()
+	namespaceInformer := informerFactory.Core().V1().Namespaces()
 	networkPolicyInformer := informerFactory.Networking().V1().NetworkPolicies()
 	tierInformer := crdInformerFactory.Crd().V1alpha1().Tiers()
 	cnpInformer := crdInformerFactory.Crd().V1alpha1().ClusterNetworkPolicies()
@@ -167,6 +168,7 @@ func newControllerWithoutEventHandler(k8sObjects, crdObjects []runtime.Object) (
 	npController := &NetworkPolicyController{
 		kubeClient:                 client,
 		crdClient:                  crdClient,
+		namespaceLister:            namespaceInformer.Lister(),
 		networkPolicyInformer:      networkPolicyInformer,
 		networkPolicyLister:        networkPolicyInformer.Lister(),
 		networkPolicyListerSynced:  networkPolicyInformer.Informer().HasSynced,
@@ -2532,11 +2534,12 @@ func TestProcessNetworkPolicy(t *testing.T) {
 					UID:       "uidA",
 				},
 				Rules: []controlplane.NetworkPolicyRule{{
-					Direction: controlplane.DirectionIn,
-					From:      matchAllPeer,
-					Services:  nil,
-					Priority:  defaultRulePriority,
-					Action:    &defaultAction,
+					Direction:     controlplane.DirectionIn,
+					From:          matchAllPeer,
+					Services:      nil,
+					Priority:      defaultRulePriority,
+					Action:        &defaultAction,
+					EnableLogging: false,
 				}},
 				AppliedToGroups: []string{getNormalizedUID(antreatypes.NewGroupSelector("nsA", &metav1.LabelSelector{}, nil, nil, nil).NormalizedName)},
 			},
@@ -2626,8 +2629,9 @@ func TestProcessNetworkPolicy(t *testing.T) {
 								Port:     &int80,
 							},
 						},
-						Priority: defaultRulePriority,
-						Action:   &defaultAction,
+						Priority:      defaultRulePriority,
+						Action:        &defaultAction,
+						EnableLogging: false,
 					},
 					{
 						Direction: controlplane.DirectionOut,
@@ -2640,8 +2644,9 @@ func TestProcessNetworkPolicy(t *testing.T) {
 								Port:     &int81,
 							},
 						},
-						Priority: defaultRulePriority,
-						Action:   &defaultAction,
+						Priority:      defaultRulePriority,
+						Action:        &defaultAction,
+						EnableLogging: false,
 					},
 				},
 				AppliedToGroups: []string{getNormalizedUID(antreatypes.NewGroupSelector("nsA", &selectorA, nil, nil, nil).NormalizedName)},
@@ -2704,8 +2709,9 @@ func TestProcessNetworkPolicy(t *testing.T) {
 								Port:     &int80,
 							},
 						},
-						Priority: defaultRulePriority,
-						Action:   &defaultAction,
+						Priority:      defaultRulePriority,
+						Action:        &defaultAction,
+						EnableLogging: false,
 					},
 					{
 						Direction: controlplane.DirectionIn,
@@ -2718,8 +2724,9 @@ func TestProcessNetworkPolicy(t *testing.T) {
 								Port:     &int81,
 							},
 						},
-						Priority: defaultRulePriority,
-						Action:   &defaultAction,
+						Priority:      defaultRulePriority,
+						Action:        &defaultAction,
+						EnableLogging: false,
 					},
 				},
 				AppliedToGroups: []string{getNormalizedUID(antreatypes.NewGroupSelector("nsA", &selectorA, nil, nil, nil).NormalizedName)},
@@ -2747,6 +2754,69 @@ func TestProcessNetworkPolicy(t *testing.T) {
 	}
 }
 
+func TestProcessNetworkPolicyLogging(t *testing.T) {
+	tests := []struct {
+		name                    string
+		inputPolicy             *networkingv1.NetworkPolicy
+		expectedPolicy          *antreatypes.NetworkPolicy
+		expectedAppliedToGroups int
+		expectedAddressGroups   int
+	}{
+		{
+			name: "default-allow-ingress",
+			inputPolicy: &networkingv1.NetworkPolicy{
+				ObjectMeta: metav1.ObjectMeta{Namespace: "nsA", Name: "npA", UID: "uidA"},
+				Spec: networkingv1.NetworkPolicySpec{
+					PodSelector: metav1.LabelSelector{},
+					PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
+					Ingress:     []networkingv1.NetworkPolicyIngressRule{{}},
+				},
+			},
+			expectedPolicy: &antreatypes.NetworkPolicy{
+				UID:  "uidA",
+				Name: "uidA",
+				SourceRef: &controlplane.NetworkPolicyReference{
+					Type:      controlplane.K8sNetworkPolicy,
+					Namespace: "nsA",
+					Name:      "npA",
+					UID:       "uidA",
+				},
+				Rules: []controlplane.NetworkPolicyRule{{
+					Direction:     controlplane.DirectionIn,
+					From:          matchAllPeer,
+					Services:      nil,
+					Priority:      defaultRulePriority,
+					Action:        &defaultAction,
+					EnableLogging: true,
+				}},
+				AppliedToGroups: []string{getNormalizedUID(antreatypes.NewGroupSelector("nsA", &metav1.LabelSelector{}, nil, nil, nil).NormalizedName)},
+			},
+			expectedAppliedToGroups: 1,
+			expectedAddressGroups:   0,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, c := newController()
+			nsA := corev1.Namespace{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:        "nsA",
+					Annotations: map[string]string{"networkpolicy.antrea.io/enable-logging": "true"},
+				},
+			}
+			c.namespaceStore.Add(&nsA)
+
+			if actualPolicy := c.processNetworkPolicy(tt.inputPolicy); !reflect.DeepEqual(actualPolicy, tt.expectedPolicy) {
+				t.Errorf("processNetworkPolicy() got %v, want %v", actualPolicy, tt.expectedPolicy)
+			}
+
+			if actualAppliedToGroups := len(c.appliedToGroupStore.List()); actualAppliedToGroups != tt.expectedAppliedToGroups {
+				t.Errorf("len(appliedToGroupStore.List()) got %v, want %v", actualAppliedToGroups, tt.expectedAppliedToGroups)
+			}
+		})
+	}
+}
+
 func TestPodToGroupMember(t *testing.T) {
 	namedPod := getPod("", "", "", "", true)
 	unNamedPod := getPod("", "", "", "", false)
diff --git a/test/e2e/antreapolicy_test.go b/test/e2e/antreapolicy_test.go
index 9667d10af..36e548a3f 100644
--- a/test/e2e/antreapolicy_test.go
+++ b/test/e2e/antreapolicy_test.go
@@ -40,6 +40,7 @@ import (
 	crdv1alpha1 "antrea.io/antrea/pkg/apis/crd/v1alpha1"
 	crdv1alpha2 "antrea.io/antrea/pkg/apis/crd/v1alpha2"
 	crdv1alpha3 "antrea.io/antrea/pkg/apis/crd/v1alpha3"
+	"antrea.io/antrea/pkg/controller/networkpolicy"
 	"antrea.io/antrea/pkg/features"
 	. "antrea.io/antrea/test/e2e/utils"
 )
@@ -1946,7 +1947,7 @@ func testANPMultipleAppliedTo(t *testing.T, data *TestData, singleRule bool) {
 	failOnError(k8sUtils.DeleteANP(builder.Namespace, builder.Name), t)
 }
 
-// testAuditLoggingBasic tests that a audit log is generated when egress drop applied
+// testAuditLoggingBasic tests that audit logs are generated when egress drop applied
 func testAuditLoggingBasic(t *testing.T, data *TestData) {
 	builder := &ClusterNetworkPolicySpecBuilder{}
 	builder = builder.SetName("test-log-acnp-deny").
@@ -2034,6 +2035,99 @@ func testAuditLoggingBasic(t *testing.T, data *TestData) {
 	failOnError(k8sUtils.CleanACNPs(), t)
 }
 
+// testAuditLoggingEnableNP tests that audit logs are generated when K8s NP is applied
+// tests both Allow traffic by K8s NP and Drop traffic by implicit K8s policy drop
+func testAuditLoggingEnableNP(t *testing.T, data *TestData) {
+	data.updateNamespaceWithAnnotations(namespaces["x"], map[string]string{networkpolicy.EnableNPLoggingAnnotationKey: "true"})
+	// Add a K8s namespaced NetworkPolicy in ns x that allow ingress traffic from
+	// Pod x/b to x/a which default denies other ingress including from Pod x/c to x/a
+	k8sNPBuilder := &NetworkPolicySpecBuilder{}
+	k8sNPBuilder = k8sNPBuilder.SetName(namespaces["x"], "allow-x-b-to-x-a").
+		SetPodSelector(map[string]string{"pod": "a"}).
+		SetTypeIngress().
+		AddIngress(v1.ProtocolTCP, &p80, nil, nil, nil,
+			map[string]string{"pod": "b"}, nil, nil, nil)
+
+	knp, err := k8sUtils.CreateOrUpdateNetworkPolicy(k8sNPBuilder.Get())
+	failOnError(err, t)
+	failOnError(waitForResourceReady(t, timeout, knp), t)
+
+	// generate some traffic that will be dropped by implicit K8s policy drop
+	var wg sync.WaitGroup
+	oneProbe := func(ns1, pod1, ns2, pod2 string) {
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			k8sUtils.Probe(ns1, pod1, ns2, pod2, p80, ProtocolTCP)
+		}()
+	}
+	oneProbe(namespaces["x"], "b", namespaces["x"], "a")
+	oneProbe(namespaces["x"], "c", namespaces["x"], "a")
+	wg.Wait()
+
+	podXA, err := k8sUtils.GetPodByLabel(namespaces["x"], "a")
+	if err != nil {
+		t.Errorf("Failed to get Pod in Namespace x with label 'pod=a': %v", err)
+	}
+	// nodeName is guaranteed to be set at this stage, since the framework waits for all Pods to be in Running phase
+	nodeName := podXA.Spec.NodeName
+	antreaPodName, err := data.getAntreaPodOnNode(nodeName)
+	if err != nil {
+		t.Errorf("Error occurred when trying to get the Antrea Agent Pod running on Node %s: %v", nodeName, err)
+	}
+	cmd := []string{"cat", logDir + logfileName}
+
+	if err := wait.Poll(1*time.Second, 10*time.Second, func() (bool, error) {
+		stdout, stderr, err := data.RunCommandFromPod(antreaNamespace, antreaPodName, "antrea-agent", cmd)
+		if err != nil || stderr != "" {
+			// file may not exist yet
+			t.Logf("Error when printing the audit log file, err: %v, stderr: %v", err, stderr)
+			return false, nil
+		}
+		if !strings.Contains(stdout, "K8sNetworkPolicy") {
+			t.Logf("Audit log file does not contain entries for 'test-log-acnp-deny' yet")
+			return false, nil
+		}
+
+		var expectedNumEntries, actualNumEntries int
+		srcPods := []string{namespaces["x"] + "/b", namespaces["x"] + "/c"}
+		expectedLogPrefix := []string{"allow-x-b-to-x-a Allow [0-9]+ ", "K8sNetworkPolicy Drop -1 "}
+		destIPs, _ := podIPs[namespaces["x"]+"/a"]
+		for i := 0; i < len(srcPods); i++ {
+			srcIPs, _ := podIPs[srcPods[i]]
+			for _, srcIP := range srcIPs {
+				for _, destIP := range destIPs {
+					// only look for an entry in the audit log file if srcIP and
+					// dstIP are of the same family
+					if strings.Contains(srcIP, ".") != strings.Contains(destIP, ".") {
+						continue
+					}
+					expectedNumEntries += 1
+					// The audit log should contain log entry `... Drop <ofPriority> <x/a IP> <z/* IP> ...`
+					re := regexp.MustCompile(expectedLogPrefix[i] + srcIP + ` [0-9]+ ` + destIP + ` ` + strconv.Itoa(int(p80)))
+					if re.MatchString(stdout) {
+						actualNumEntries += 1
+					} else {
+						t.Logf("Audit log does not contain expected entry from %s (%s) to x/a (%s)", srcPods[i], srcIP, destIP)
+					}
+					break
+				}
+			}
+		}
+		if actualNumEntries != expectedNumEntries {
+			t.Logf("Missing entries in audit log with K8s NP: expected %d but found %d", expectedNumEntries, actualNumEntries)
+			return false, nil
+		}
+		return true, nil
+	}); err != nil {
+		t.Errorf("Error when polling audit log files for required entries: %v", err)
+	}
+	failOnError(k8sUtils.DeleteNetworkPolicy(namespaces["x"], "allow-x-b-to-x-a"), t)
+	data.UpdateNamespace(namespaces["x"], func(namespace *v1.Namespace) {
+		delete(namespace.Annotations, networkpolicy.EnableNPLoggingAnnotationKey)
+	})
+}
+
 func testAppliedToPerRule(t *testing.T) {
 	builder := &AntreaNetworkPolicySpecBuilder{}
 	builder = builder.SetName(namespaces["y"], "np1").SetPriority(1.0)
@@ -3556,6 +3650,7 @@ func TestAntreaPolicy(t *testing.T) {
 
 	t.Run("TestGroupAuditLogging", func(t *testing.T) {
 		t.Run("Case=AuditLoggingBasic", func(t *testing.T) { testAuditLoggingBasic(t, data) })
+		t.Run("Case=AuditLoggingEnableNP", func(t *testing.T) { testAuditLoggingEnableNP(t, data) })
 	})
 
 	t.Run("TestMulticastNP", func(t *testing.T) {
diff --git a/test/e2e/framework.go b/test/e2e/framework.go
index 40d8ee35c..e6a3f8627 100644
--- a/test/e2e/framework.go
+++ b/test/e2e/framework.go
@@ -600,11 +600,11 @@ func (data *TestData) CreateNamespace(namespace string, mutateFunc func(*corev1.
 		mutateFunc(ns)
 	}
 	if ns, err := data.clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}); err != nil {
-		// Ignore error if the namespace already exists
+		// Ignore error if the Namespace already exists
 		if !errors.IsAlreadyExists(err) {
 			return fmt.Errorf("error when creating '%s' Namespace: %v", namespace, err)
 		}
-		// When namespace already exists, check phase
+		// When Namespace already exists, check phase
 		if ns.Status.Phase == corev1.NamespaceTerminating {
 			return fmt.Errorf("error when creating '%s' Namespace: namespace exists but is in 'Terminating' phase", namespace)
 		}
@@ -612,8 +612,35 @@ func (data *TestData) CreateNamespace(namespace string, mutateFunc func(*corev1.
 	return nil
 }
 
-// createNamespaceWithAnnotations creates the namespace with Annotations.
+func (data *TestData) UpdateNamespace(namespace string, mutateFunc func(*corev1.Namespace)) error {
+	ns, _ := data.clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
+	if mutateFunc != nil {
+		mutateFunc(ns)
+	}
+	if ns, err := data.clientset.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{}); err != nil {
+		// Check Namespace phase
+		if ns.Status.Phase == corev1.NamespaceTerminating {
+			return fmt.Errorf("error when updating '%s' Namespace: namespace is in 'Terminating' phase", namespace)
+		}
+		return fmt.Errorf("error when updating '%s' Namespace: %v", namespace, err)
+	}
+	return nil
+}
+
+// createNamespaceWithAnnotations creates the Namespace with Annotations.
 func (data *TestData) createNamespaceWithAnnotations(namespace string, annotations map[string]string) error {
+	mutateFunc := data.generateNamespaceAnnotationsMutateFunc(annotations)
+	return data.CreateNamespace(namespace, mutateFunc)
+}
+
+// updateNamespaceWithAnnotations updates the given Namespace with Annotations.
+func (data *TestData) updateNamespaceWithAnnotations(namespace string, annotations map[string]string) error {
+	mutateFunc := data.generateNamespaceAnnotationsMutateFunc(annotations)
+	return data.UpdateNamespace(namespace, mutateFunc)
+}
+
+// generateAnnotationsMutateFunc generates a mutate function to add given Annotations to a Namespace.
+func (data *TestData) generateNamespaceAnnotationsMutateFunc(annotations map[string]string) func(*corev1.Namespace) {
 	var mutateFunc func(*corev1.Namespace)
 	if annotations != nil {
 		mutateFunc = func(namespace *corev1.Namespace) {
@@ -625,7 +652,7 @@ func (data *TestData) createNamespaceWithAnnotations(namespace string, annotatio
 			}
 		}
 	}
-	return data.CreateNamespace(namespace, mutateFunc)
+	return mutateFunc
 }
 
 // DeleteNamespace deletes the provided Namespace, and waits for deletion to actually complete if timeout>=0
diff --git a/test/integration/agent/openflow_test.go b/test/integration/agent/openflow_test.go
index 12cbd83be..34d2536f2 100644
--- a/test/integration/agent/openflow_test.go
+++ b/test/integration/agent/openflow_test.go
@@ -313,10 +313,10 @@ func TestReplayFlowsNetworkPolicyFlows(t *testing.T) {
 	err = c.InstallPolicyRuleFlows(rule)
 	require.Nil(t, err, "Failed to InstallPolicyRuleFlows")
 
-	err = c.AddPolicyRuleAddress(ruleID, types.SrcAddress, prepareIPNetAddresses([]string{"192.168.5.0/24", "192.169.1.0/24"}), nil)
+	err = c.AddPolicyRuleAddress(ruleID, types.SrcAddress, prepareIPNetAddresses([]string{"192.168.5.0/24", "192.169.1.0/24"}), nil, false)
 	require.Nil(t, err, "Failed to AddPolicyRuleAddress")
 	ofport := int32(100)
-	err = c.AddPolicyRuleAddress(ruleID, types.DstAddress, []types.Address{ofClient.NewOFPortAddress(ofport)}, nil)
+	err = c.AddPolicyRuleAddress(ruleID, types.DstAddress, []types.Address{ofClient.NewOFPortAddress(ofport)}, nil, false)
 	require.Nil(t, err, "Failed to AddPolicyRuleAddress")
 
 	testReplayFlows(t)
@@ -496,7 +496,7 @@ func TestNetworkPolicyFlows(t *testing.T) {
 	checkDeleteAddress(t, ingressRuleTable, priorityNormal, ruleID, addedFrom, types.SrcAddress)
 
 	ofport := int32(100)
-	err = c.AddPolicyRuleAddress(ruleID, types.DstAddress, []types.Address{ofClient.NewOFPortAddress(ofport)}, nil)
+	err = c.AddPolicyRuleAddress(ruleID, types.DstAddress, []types.Address{ofClient.NewOFPortAddress(ofport)}, nil, false)
 	require.Nil(t, err, "Failed to AddPolicyRuleAddress")
 
 	// Dump flows.
@@ -795,7 +795,7 @@ func checkDefaultDropFlows(t *testing.T, table string, priority int, addrType ty
 }
 
 func checkAddAddress(t *testing.T, ruleTable string, priority int, ruleID uint32, addedAddress []types.Address, addrType types.AddressType) {
-	err := c.AddPolicyRuleAddress(ruleID, addrType, addedAddress, nil)
+	err := c.AddPolicyRuleAddress(ruleID, addrType, addedAddress, nil, false)
 	require.Nil(t, err, "Failed to AddPolicyRuleAddress")
 
 	// dump flows
diff --git a/test/integration/ovs/ovs_client_test.go b/test/integration/ovs/ovs_client_test.go
index 74a4162ff..9907a91a5 100644
--- a/test/integration/ovs/ovs_client_test.go
+++ b/test/integration/ovs/ovs_client_test.go
@@ -287,6 +287,75 @@ func TestTunnelOptionCsum(t *testing.T) {
 	}
 }
 
+func TestTunnelOptionTunnelPort(t *testing.T) {
+	testCases := map[string]struct {
+		initialTunnelPort int32
+		updatedTunnelPort int32
+	}{
+		"initial zero, kept zero": {
+			initialTunnelPort: 0,
+			updatedTunnelPort: 0,
+		},
+		"initial zero, updated to 8472": {
+			initialTunnelPort: 0,
+			updatedTunnelPort: 8472,
+		},
+		"initial 8472, kept 8473": {
+			initialTunnelPort: 8472,
+			updatedTunnelPort: 8473,
+		},
+		"initial 8472, updated to zero": {
+			initialTunnelPort: 8472,
+			updatedTunnelPort: 0,
+		},
+	}
+	for name, testCase := range testCases {
+		t.Run(name, func(t *testing.T) {
+			data := &testData{}
+			data.setup(t)
+			defer data.teardown(t)
+
+			name := "vxlan0"
+			extraOptions := map[string]interface{}{}
+			if testCase.initialTunnelPort != 0 {
+				extraOptions["dst_port"] = strconv.Itoa(int(testCase.initialTunnelPort))
+			}
+			_, err := data.br.CreateTunnelPortExt(name, ovsconfig.VXLANTunnel, ofPortRequest, true, "", "", "", "", extraOptions, nil)
+			require.Nil(t, err, "Error when creating tunnel port")
+			options, err := data.br.GetInterfaceOptions(name)
+			require.Nil(t, err, "Error when getting interface options")
+			dstPort, exists := options["dst_port"]
+			if testCase.initialTunnelPort != 0 {
+				actualInitialTunnelPort, _ := strconv.ParseInt(dstPort, 10, 32)
+				require.Equal(t, testCase.initialTunnelPort, int32(actualInitialTunnelPort))
+			} else {
+				require.Equal(t, exists, false)
+			}
+
+			updatedOptions := map[string]interface{}{}
+			for k, v := range options {
+				updatedOptions[k] = v
+			}
+			if testCase.updatedTunnelPort != 0 {
+				updatedOptions["dst_port"] = strconv.Itoa(int(testCase.updatedTunnelPort))
+			} else if _, ok := updatedOptions["dst_port"]; ok {
+				delete(updatedOptions, "dst_port")
+			}
+			err = data.br.SetInterfaceOptions(name, updatedOptions)
+			require.Nil(t, err, "Error when setting interface options")
+			options, err = data.br.GetInterfaceOptions(name)
+			require.Nil(t, err, "Error when getting interface options")
+			dstPort, exists = options["dst_port"]
+			if testCase.updatedTunnelPort != 0 {
+				actualTunnelPort, _ := strconv.ParseInt(dstPort, 10, 32)
+				require.Equal(t, testCase.updatedTunnelPort, int32(actualTunnelPort))
+			} else {
+				require.Equal(t, exists, false)
+			}
+		})
+	}
+}
+
 func deleteAllPorts(t *testing.T, br *ovsconfig.OVSBridge) {
 	portList, err := br.GetPortUUIDList()
 	require.Nil(t, err, "Error when retrieving port list")