Repository: antrea-io/antrea
Pull request #5740 (2023-11-21 14:55:01)

Add Versioned API and Antctl for NetworkPolicyEvaluation (effective policy rule prediction)
- Added antctl support for querying effective policy rule by networkpolicyevaluation
  `antctl query networkpolicyevaluation -S ns1/pod1 -D ns2/pod2`
- Added versioned API for NetworkPolicy evaluation POST call
 `curl -d "@<test json file>" -H "Content-Type: application/json" -X POST <k8s-apiserver>:8001/apis/controlplane.antrea.io/v1beta2/networkpolicyevaluation`

Adds the above APIs and antctl queriers that returns the predicted effective NetworkPolicy rule, which affects traffic from `ns1/pod1` to `ns2/pod2`. The solution picks the highest priority rule that satisfies the query.

qiyueyao: Depends on #5989 

Patch:
diff --git a/.github/workflows/trivy_scan.yml b/.github/workflows/trivy_scan.yml
index e8955a509..e7dcb9a51 100644
--- a/.github/workflows/trivy_scan.yml
+++ b/.github/workflows/trivy_scan.yml
@@ -36,7 +36,7 @@ jobs:
         docker pull antrea/antrea-ubuntu:${{ steps.find-antrea-greatest-version.outputs.antrea_version }}
     - name: Run Trivy vulnerability scanner on latest Antrea Docker image
       if: ${{ always() && steps.pull.conclusion == 'success' }}
-      uses: aquasecurity/trivy-action@0.17.0
+      uses: aquasecurity/trivy-action@0.18.0
       # we cannot use .trivy.yml as we need to override some config parameters
       # and that is not supported by aquasecurity/trivy-action
       with:
@@ -51,7 +51,7 @@ jobs:
         output: 'trivy.latest.txt'
     - name: Run Trivy vulnerability scanner on Antrea Docker image for latest released version
       if: ${{ always() && steps.pull.conclusion == 'success' }}
-      uses: aquasecurity/trivy-action@0.17.0
+      uses: aquasecurity/trivy-action@0.18.0
       with:
         scan-type: 'image'
         image-ref: 'antrea/antrea-ubuntu:${{ steps.find-antrea-greatest-version.outputs.antrea_version }}'
diff --git a/.github/workflows/trivy_scan_before_release.yml b/.github/workflows/trivy_scan_before_release.yml
index ebcac0253..6103f5206 100644
--- a/.github/workflows/trivy_scan_before_release.yml
+++ b/.github/workflows/trivy_scan_before_release.yml
@@ -15,19 +15,19 @@ jobs:
       run: |
         ./hack/build-antrea-linux-all.sh --pull
     - name: Run Trivy vulnerability scanner on Antrea unified Docker image
-      uses: aquasecurity/trivy-action@0.17.0
+      uses: aquasecurity/trivy-action@0.18.0
       with:
         scan-type: 'image'
         image-ref: 'antrea/antrea-ubuntu:latest'
         trivy-config: '.trivy.yml'
     - name: Run Trivy vulnerability scanner on the antrea-agent Docker image
-      uses: aquasecurity/trivy-action@0.17.0
+      uses: aquasecurity/trivy-action@0.18.0
       with:
         scan-type: 'image'
         image-ref: 'antrea/antrea-agent-ubuntu:latest'
         trivy-config: '.trivy.yml'
     - name: Run Trivy vulnerability scanner on the antrea-controller Docker image
-      uses: aquasecurity/trivy-action@0.17.0
+      uses: aquasecurity/trivy-action@0.18.0
       with:
         scan-type: 'image'
         image-ref: 'antrea/antrea-controller-ubuntu:latest'
diff --git a/Makefile b/Makefile
index 91d47c718..9dc908dd6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,12 @@
 SHELL              := /bin/bash
 # go options
 GO                 ?= go
-LDFLAGS            :=
-GOFLAGS            :=
+# By default, disable debugging information (see https://pkg.go.dev/cmd/link) and trim embedded
+# paths.
+# To build binaries (standalone or embedded inside container images) with debugging information,
+# edit LDFLAGS and GOFLAGS.
+LDFLAGS            := -s -w
+GOFLAGS            := -trimpath
 # By default, disable cgo for all Go binaries.
 # For binaries meant to be published as release assets or copied to a different host, cgo should
 # always be disabled.
@@ -241,7 +245,7 @@ antctl: $(ANTCTL_BINARIES)
 
 .PHONY: antctl-release
 antctl-release:
-	$(GO) build -o $(BINDIR)/$(ANTCTL_BINARY_NAME) $(GOFLAGS) -ldflags '-s -w $(LDFLAGS)' antrea.io/antrea/cmd/antctl
+	$(GO) build -o $(BINDIR)/$(ANTCTL_BINARY_NAME) $(GOFLAGS) -ldflags '$(LDFLAGS)' antrea.io/antrea/cmd/antctl
 
 .PHONY: check-copyright
 check-copyright: 
diff --git a/README.md b/README.md
index 331de95b0..7a01286bf 100644
--- a/README.md
+++ b/README.md
@@ -106,10 +106,10 @@ Also check out [@ProjectAntrea](https://twitter.com/ProjectAntrea) on Twitter!
   enable fine-grained visibility into the communication among Kubernetes
   workloads. Theia provides visualization for Antrea network flows in Grafana
   dashboards, and recommends Network Policies to secure the workloads.
-* **Network Policies for virtual machines**: Antrea native policies can be
+* **Network Policies for virtual machines**: Antrea-native policies can be
   enforced on non-Kubernetes Nodes including VMs and baremetal servers. Project
   [Nephe](https://github.com/antrea-io/nephe) implements security policies for
-  VMs across clouds, leveraging Antrea native policies.
+  VMs across clouds, leveraging Antrea-native policies.
 * **Encryption**: Encryption of inter-Node Pod traffic with IPsec or WireGuard
   tunnels.
 * **Easy deployment**: Antrea is deployed by applying a single YAML manifest
diff --git a/build/charts/antrea/crds/traceflow.yaml b/build/charts/antrea/crds/traceflow.yaml
index 45cf9f0f2..90b139ecb 100644
--- a/build/charts/antrea/crds/traceflow.yaml
+++ b/build/charts/antrea/crds/traceflow.yaml
@@ -390,7 +390,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -507,7 +507,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
diff --git a/build/charts/antrea/templates/antctl/clusterrole.yaml b/build/charts/antrea/templates/antctl/clusterrole.yaml
index 707c193e5..10c44d96a 100644
--- a/build/charts/antrea/templates/antctl/clusterrole.yaml
+++ b/build/charts/antrea/templates/antctl/clusterrole.yaml
@@ -14,6 +14,12 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+      - controlplane.antrea.io
+    resources:
+      - networkpolicyevaluation
+    verbs:
+      - create
   - apiGroups:
       - stats.antrea.io
     resources:
diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml
index 155d20166..37e6d7209 100644
--- a/build/yamls/antrea-aks.yml
+++ b/build/yamls/antrea-aks.yml
@@ -5046,7 +5046,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -5163,7 +5163,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
@@ -6339,6 +6339,12 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+      - controlplane.antrea.io
+    resources:
+      - networkpolicyevaluation
+    verbs:
+      - create
   - apiGroups:
       - stats.antrea.io
     resources:
diff --git a/build/yamls/antrea-crds.yml b/build/yamls/antrea-crds.yml
index 7439bdf96..8add48f23 100644
--- a/build/yamls/antrea-crds.yml
+++ b/build/yamls/antrea-crds.yml
@@ -5019,7 +5019,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -5136,7 +5136,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml
index 3775ab5a6..042e024bd 100644
--- a/build/yamls/antrea-eks.yml
+++ b/build/yamls/antrea-eks.yml
@@ -5046,7 +5046,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -5163,7 +5163,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
@@ -6339,6 +6339,12 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+      - controlplane.antrea.io
+    resources:
+      - networkpolicyevaluation
+    verbs:
+      - create
   - apiGroups:
       - stats.antrea.io
     resources:
diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml
index d8be7ff47..1b99926c0 100644
--- a/build/yamls/antrea-gke.yml
+++ b/build/yamls/antrea-gke.yml
@@ -5046,7 +5046,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -5163,7 +5163,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
@@ -6339,6 +6339,12 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+      - controlplane.antrea.io
+    resources:
+      - networkpolicyevaluation
+    verbs:
+      - create
   - apiGroups:
       - stats.antrea.io
     resources:
diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml
index 5d77441ce..8c57461c3 100644
--- a/build/yamls/antrea-ipsec.yml
+++ b/build/yamls/antrea-ipsec.yml
@@ -5046,7 +5046,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -5163,7 +5163,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
@@ -6352,6 +6352,12 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+      - controlplane.antrea.io
+    resources:
+      - networkpolicyevaluation
+    verbs:
+      - create
   - apiGroups:
       - stats.antrea.io
     resources:
diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml
index 411ec9739..d7807bd60 100644
--- a/build/yamls/antrea.yml
+++ b/build/yamls/antrea.yml
@@ -5046,7 +5046,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                 liveTraffic:
                   type: boolean
                 droppedOnly:
@@ -5163,7 +5163,7 @@ spec:
                             flags:
                               type: integer
                               minimum: 0
-                              maximum: 7
+                              maximum: 255
                           type: object
                         udp:
                           properties:
@@ -6339,6 +6339,12 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+      - controlplane.antrea.io
+    resources:
+      - networkpolicyevaluation
+    verbs:
+      - create
   - apiGroups:
       - stats.antrea.io
     resources:
diff --git a/cmd/antrea-agent/options.go b/cmd/antrea-agent/options.go
index a01150e16..faaa19344 100644
--- a/cmd/antrea-agent/options.go
+++ b/cmd/antrea-agent/options.go
@@ -469,12 +469,7 @@ func (o *Options) setK8sNodeDefaultOptions() {
 	}
 
 	if o.config.NodePortLocal.Enable {
-		switch {
-		case o.config.NodePortLocal.PortRange != "":
-		case o.config.NPLPortRange != "":
-			klog.InfoS("The nplPortRange option is deprecated, please use nodePortLocal.portRange instead")
-			o.config.NodePortLocal.PortRange = o.config.NPLPortRange
-		default:
+		if o.config.NodePortLocal.PortRange == "" {
 			o.config.NodePortLocal.PortRange = defaultNPLPortRange
 		}
 	}
@@ -739,8 +734,8 @@ func (o *Options) validateSecondaryNetworkConfig() error {
 	if brConfig.BridgeName == "" {
 		return fmt.Errorf("bridge name is not provided for the secondary network OVS bridge")
 	}
-	if len(brConfig.PhysicalInterfaces) > 1 {
-		return fmt.Errorf("at most one physical interface can be specified for the secondary network OVS bridge")
+	if len(brConfig.PhysicalInterfaces) > 8 {
+		return fmt.Errorf("at most eight physical interfaces can be specified for the secondary network OVS bridge")
 	}
 
 	return nil
diff --git a/docs/antctl.md b/docs/antctl.md
index 96976844d..9ba8a851f 100644
--- a/docs/antctl.md
+++ b/docs/antctl.md
@@ -25,6 +25,7 @@ running in three different modes:
   - [controllerinfo and agentinfo commands](#controllerinfo-and-agentinfo-commands)
   - [NetworkPolicy commands](#networkpolicy-commands)
     - [Mapping endpoints to NetworkPolicies](#mapping-endpoints-to-networkpolicies)
+    - [Evaluating expected NetworkPolicy behavior](#evaluating-expected-networkpolicy-behavior)
   - [Dumping Pod network interface information](#dumping-pod-network-interface-information)
   - [Dumping OVS flows](#dumping-ovs-flows)
   - [OVS packet tracing](#ovs-packet-tracing)
@@ -263,6 +264,20 @@ Namespace.
 This command only works in "controller mode" and **as of now it can only be run
 from inside the Antrea Controller Pod, and not from out-of-cluster**.
 
+#### Evaluating expected NetworkPolicy behavior
+
+`antctl` supports evaluating all the existing Antrea-native NetworkPolicies,
+Kubernetes NetworkPolicies and AdminNetworkPolicies to predict the effective
+policy rule for traffic between source and destination Pods.
+
+```bash
+antctl query networkpolicyevaluation -S NAMESPACE/POD -D NAMESPACE/POD
+```
+
+If only Pod name is provided, the command will default to the "default" Namespace.
+
+This command only works in "controller mode".
+
 ### Dumping Pod network interface information
 
 `antctl` agent command `get podinterface` (or `get pi`) can dump network
diff --git a/docs/feature-gates.md b/docs/feature-gates.md
index 79008d604..20de5037f 100644
--- a/docs/feature-gates.md
+++ b/docs/feature-gates.md
@@ -173,7 +173,7 @@ This feature is currently only supported for Nodes running Linux. Windows suppor
 Stats API, which can be accessed by kubectl get commands, e.g. `kubectl get networkpolicystats`. The statistical data
 includes total number of sessions, packets, and bytes allowed or denied by a NetworkPolicy. It is collected
 asynchronously so there may be a delay of up to 1 minute for changes to be reflected in API responses. The feature
-supports K8s NetworkPolicies and Antrea native policies, the latter of which requires
+supports K8s NetworkPolicies and Antrea-native policies, the latter of which requires
 `AntreaPolicy` to be enabled. Usage examples:
 
 ```bash
diff --git a/docs/multicluster/user-guide.md b/docs/multicluster/user-guide.md
index 6673f6ec6..025dc198d 100644
--- a/docs/multicluster/user-guide.md
+++ b/docs/multicluster/user-guide.md
@@ -31,7 +31,7 @@
 
 Antrea Multi-cluster implements [Multi-cluster Service API](https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api),
 which allows users to create multi-cluster Services that can be accessed cross
-clusters in a ClusterSet. Antrea Multi-cluster also extends Antrea native
+clusters in a ClusterSet. Antrea Multi-cluster also extends Antrea-native
 NetworkPolicy to support Multi-cluster NetworkPolicy rules that apply to
 cross-cluster traffic, and ClusterNetworkPolicy replication that allows a
 ClusterSet admin to create ClusterNetworkPolicies which are replicated across
diff --git a/docs/node-port-local.md b/docs/node-port-local.md
index 89cf54c77..6a76cc296 100644
--- a/docs/node-port-local.md
+++ b/docs/node-port-local.md
@@ -38,9 +38,10 @@ From Antrea v1.14, NPL is GA.
 In addition to enabling the NodePortLocal feature gate (if needed), you need to
 ensure that the `nodePortLocal.enable` flag is set to true in the Antrea Agent
 configuration. The `nodePortLocal.portRange` parameter can also be set to change
-the range from which Node ports will be allocated. Otherwise, the default range
-of `61000-62000` will be used by default. When using the NodePortLocal feature,
-your `antrea-agent` ConfigMap should look like this:
+the range from which Node ports will be allocated. Otherwise, the range
+of `61000-62000` will be used by default on Linux, and the range `40000-41000` will
+be used on Windows. When using the NodePortLocal feature, your `antrea-agent` ConfigMap
+should look like this:
 
 ```yaml
 kind: ConfigMap
@@ -116,7 +117,7 @@ metadata:
   labels:
     app: nginx
   annotations:
-    nodeportlocal.antrea.io: '[{"podPort":8080,"nodeIP":"10.10.10.10","nodePort":61002,"protocol":"tcp","protocols":["tcp"]}]'
+    nodeportlocal.antrea.io: '[{"podPort":8080,"nodeIP":"10.10.10.10","nodePort":61002,"protocol":"tcp"}]'
 ```
 
 This annotation indicates that port 8080 of the Pod can be reached through port
@@ -132,11 +133,7 @@ NodePortLocal can only be used with Services of type `ClusterIP` or
 Services of type `NodePort` or `ExternalName`. The annotation also has no effect
 for Services with an empty or missing Selector.
 
-Starting from the Antrea v1.7 minor release, the `protocols` field in the
-annotation is deprecated. The array contains a single member, equal to the
-`protocol` field.
-The `protocols` field will be removed from Antrea for minor releases post March 2023,
-as per our deprecation policy.
+Starting from Antrea v2.0, the `protocols` field is removed.
 
 ### Usage pre Antrea v1.7
 
@@ -168,7 +165,7 @@ for a given podPort, and the allocated nodePort may be different for each one.
 Prior to the Antrea v1.4 minor release, the `nodePortLocal` option group in the
 Antrea Agent configuration did not exist. To enable the NodePortLocal feature,
 one simply needed to enable the feature gate, and the port range could be
-configured using the (now deprecated) `nplPortRange` parameter.
+configured using the (now removed) `nplPortRange` parameter.
 
 ### Usage pre Antrea v1.2
 
diff --git a/docs/secondary-network.md b/docs/secondary-network.md
index 6ed5c2126..31f7b24dd 100644
--- a/docs/secondary-network.md
+++ b/docs/secondary-network.md
@@ -68,8 +68,8 @@ data:
 ```
 
 At the moment, Antrea supports only a single OVS bridge for secondary networks,
-and supports only a single physical interface on the bridge. The physical
-interface cannot be the Node's management interface, otherwise the Node's
+and supports upto eight physical interfaces on the bridge. The physical
+interfaces cannot be the Node's management interface, otherwise the Node's
 management network connectivity can be broken after `antrea-agent` creates the
 OVS bridge and moves the management interface to the bridge.
 
@@ -165,3 +165,8 @@ spec:
  - name: toolbox
    image: antrea/toolbox:latest
 ```
+
+**At the moment, we do NOT support annotation update / removal: when the
+  annotation is added to the Pod for the first time (e.g., when creating the
+  Pod), we will configure the secondary network interfaces accordingly, and no
+  change is possible after that, until the Pod is deleted.**
diff --git a/docs/traceflow-guide.md b/docs/traceflow-guide.md
index 5cf8b5d47..7b555082f 100644
--- a/docs/traceflow-guide.md
+++ b/docs/traceflow-guide.md
@@ -140,7 +140,7 @@ will fail. But you can specify a different timeout value, by adding
 `timeout: <value-in-seconds>` to the Traceflow `spec`.
 
 In some cases, it might be useful to capture the packets dropped by
-NetworkPolicies (inc. K8s NetworkPolicies or Antrea native policies). You can
+NetworkPolicies (inc. K8s NetworkPolicies or Antrea-native policies). You can
 add `droppedOnly: true` to the live-traffic Traceflow `spec`, then the first
 packet that matches the Traceflow spec and is dropped by a NetworkPolicy will
 be captured and traced.
diff --git a/hack/update-codegen-dockerized.sh b/hack/update-codegen-dockerized.sh
index 4a2b488f4..9da35dc0e 100755
--- a/hack/update-codegen-dockerized.sh
+++ b/hack/update-codegen-dockerized.sh
@@ -60,7 +60,7 @@ MOCKGEN_TARGETS=(
   "pkg/agent/util/netlink Interface testing mock_netlink_linux.go"
   "pkg/agent/wireguard Interface testing mock_wireguard.go"
   "pkg/antctl AntctlClient ."
-  "pkg/controller/networkpolicy EndpointQuerier testing"
+  "pkg/controller/networkpolicy EndpointQuerier,PolicyRuleQuerier testing"
   "pkg/controller/querier ControllerQuerier testing"
   "pkg/flowaggregator/exporter Interface testing"
   "pkg/ipfix IPFIXExportingProcess,IPFIXRegistry,IPFIXCollectingProcess,IPFIXAggregationProcess testing"
diff --git a/pkg/agent/controller/networkpolicy/audit_logging.go b/pkg/agent/controller/networkpolicy/audit_logging.go
index 841d29862..3dd3a8269 100644
--- a/pkg/agent/controller/networkpolicy/audit_logging.go
+++ b/pkg/agent/controller/networkpolicy/audit_logging.go
@@ -281,7 +281,7 @@ func getNetworkPolicyInfo(pktIn *ofctrl.PacketIn, packet *binding.Packet, c *Con
 	ob.ofPriority = ofPriority
 	ob.ruleName = ruleName
 	ob.logLabel = logLabel
-	// Fill in placeholders for Antrea native policies without log labels,
+	// Fill in placeholders for Antrea-native policies without log labels,
 	// K8s NetworkPolicies without rule names or log labels.
 	fillLogInfoPlaceholders([]*string{&ob.ruleName, &ob.logLabel, &ob.ofPriority})
 	return nil
diff --git a/pkg/agent/nodeportlocal/k8s/npl_controller.go b/pkg/agent/nodeportlocal/k8s/npl_controller.go
index f0a17ab5d..592098e7a 100644
--- a/pkg/agent/nodeportlocal/k8s/npl_controller.go
+++ b/pkg/agent/nodeportlocal/k8s/npl_controller.go
@@ -512,11 +512,10 @@ func (c *NPLController) handleAddUpdatePod(key string, obj interface{}) error {
 		}
 		if _, ok := nplAnnotationsRequiredMap[portcache.NodePortProtoFormat(nodePort, protocol)]; !ok {
 			nplAnnotationsRequiredMap[portcache.NodePortProtoFormat(nodePort, protocol)] = types.NPLAnnotation{
-				PodPort:   port,
-				NodeIP:    pod.Status.HostIP,
-				NodePort:  nodePort,
-				Protocol:  protocol,
-				Protocols: []string{protocol},
+				PodPort:  port,
+				NodeIP:   pod.Status.HostIP,
+				NodePort: nodePort,
+				Protocol: protocol,
 			}
 		}
 	}
@@ -598,11 +597,10 @@ func (c *NPLController) waitForRulesInitialization() {
 				continue
 			}
 			allNPLPorts = append(allNPLPorts, rules.PodNodePort{
-				NodePort:  npl.NodePort,
-				PodPort:   npl.PodPort,
-				PodIP:     pod.Status.PodIP,
-				Protocol:  npl.Protocol,
-				Protocols: npl.Protocols,
+				NodePort: npl.NodePort,
+				PodPort:  npl.PodPort,
+				PodIP:    pod.Status.PodIP,
+				Protocol: npl.Protocol,
 			})
 		}
 	}
diff --git a/pkg/agent/nodeportlocal/portcache/port_table_others.go b/pkg/agent/nodeportlocal/portcache/port_table_others.go
index 5e9d7b890..30ec417e7 100644
--- a/pkg/agent/nodeportlocal/portcache/port_table_others.go
+++ b/pkg/agent/nodeportlocal/portcache/port_table_others.go
@@ -183,11 +183,10 @@ func (pt *PortTable) syncRules() error {
 			protocols = append(protocols, protocol.Protocol)
 		}
 		nplPorts = append(nplPorts, rules.PodNodePort{
-			NodePort:  npData.NodePort,
-			PodPort:   npData.PodPort,
-			PodIP:     npData.PodIP,
-			Protocol:  protocols[0],
-			Protocols: protocols,
+			NodePort: npData.NodePort,
+			PodPort:  npData.PodPort,
+			PodIP:    npData.PodIP,
+			Protocol: protocols[0],
 		})
 	}
 	if err := pt.PodPortRules.AddAllRules(nplPorts); err != nil {
diff --git a/pkg/agent/nodeportlocal/rules/iptable_rule.go b/pkg/agent/nodeportlocal/rules/iptable_rule.go
index e65d7a128..767ad3c09 100644
--- a/pkg/agent/nodeportlocal/rules/iptable_rule.go
+++ b/pkg/agent/nodeportlocal/rules/iptable_rule.go
@@ -103,11 +103,9 @@ func (ipt *iptablesRules) AddAllRules(nplList []PodNodePort) error {
 	writeLine(iptablesData, "*nat")
 	writeLine(iptablesData, iptables.MakeChainLine(NodePortLocalChain))
 	for _, nplData := range nplList {
-		for _, protocol := range nplData.Protocols {
-			destination := nplData.PodIP + ":" + fmt.Sprint(nplData.PodPort)
-			rule := buildRuleForPod(nplData.NodePort, destination, protocol)
-			writeLine(iptablesData, append([]string{"-A", NodePortLocalChain}, rule...)...)
-		}
+		destination := nplData.PodIP + ":" + fmt.Sprint(nplData.PodPort)
+		rule := buildRuleForPod(nplData.NodePort, destination, nplData.Protocol)
+		writeLine(iptablesData, append([]string{"-A", NodePortLocalChain}, rule...)...)
 	}
 	writeLine(iptablesData, "COMMIT")
 	if err := ipt.table.Restore(iptablesData.String(), false, false); err != nil {
diff --git a/pkg/agent/nodeportlocal/rules/types.go b/pkg/agent/nodeportlocal/rules/types.go
index 21b89e5eb..29768957d 100644
--- a/pkg/agent/nodeportlocal/rules/types.go
+++ b/pkg/agent/nodeportlocal/rules/types.go
@@ -16,9 +16,8 @@ package rules
 
 // PodNodePort contains the Node Port, Pod IP, Pod Port and Protocols for NodePortLocal.
 type PodNodePort struct {
-	NodePort  int
-	PodPort   int
-	PodIP     string
-	Protocol  string
-	Protocols []string
+	NodePort int
+	PodPort  int
+	PodIP    string
+	Protocol string
 }
diff --git a/pkg/agent/nodeportlocal/types/types.go b/pkg/agent/nodeportlocal/types/types.go
index b876242b3..9cce1e9c5 100644
--- a/pkg/agent/nodeportlocal/types/types.go
+++ b/pkg/agent/nodeportlocal/types/types.go
@@ -21,9 +21,8 @@ const (
 
 // NPLAnnotation is the structure used for setting NodePortLocal annotation on the Pods.
 type NPLAnnotation struct {
-	PodPort   int      `json:"podPort"`
-	NodeIP    string   `json:"nodeIP"`
-	NodePort  int      `json:"nodePort"`
-	Protocol  string   `json:"protocol"`
-	Protocols []string `json:"protocols"` // deprecated, array with a single member which is equal to the Protocol field
+	PodPort  int    `json:"podPort"`
+	NodeIP   string `json:"nodeIP"`
+	NodePort int    `json:"nodePort"`
+	Protocol string `json:"protocol"`
 }
diff --git a/pkg/agent/secondarynetwork/init.go b/pkg/agent/secondarynetwork/init.go
index abc3351c0..7333b6e58 100644
--- a/pkg/agent/secondarynetwork/init.go
+++ b/pkg/agent/secondarynetwork/init.go
@@ -34,7 +34,7 @@ import (
 )
 
 var (
-	// Funcs which will be orridden with mock funcs in tests.
+	// Funcs which will be overridden with mock funcs in tests.
 	interfaceByNameFn = net.InterfaceByName
 	newOVSBridgeFn    = ovsconfig.NewOVSBridge
 )
@@ -82,9 +82,7 @@ func createOVSBridge(bridges []agentconfig.OVSBridgeConfig, ovsdb *ovsdb.OVSDB)
 	// Only one OVS bridge is supported.
 	bridgeConfig := bridges[0]
 
-	phyInterface := ""
-	if len(bridgeConfig.PhysicalInterfaces) > 0 {
-		phyInterface = bridgeConfig.PhysicalInterfaces[0]
+	for _, phyInterface := range bridgeConfig.PhysicalInterfaces {
 		if _, err := interfaceByNameFn(phyInterface); err != nil {
 			return nil, fmt.Errorf("failed to get interface %s: %v", phyInterface, err)
 		}
@@ -96,21 +94,17 @@ func createOVSBridge(bridges []agentconfig.OVSBridgeConfig, ovsdb *ovsdb.OVSDB)
 	}
 	klog.InfoS("OVS bridge created", "bridge", bridgeConfig.BridgeName)
 
-	if phyInterface == "" {
-		return ovsBridgeClient, nil
-	}
-
-	if _, err := ovsBridgeClient.GetOFPort(phyInterface, false); err == nil {
-		klog.V(2).InfoS("Physical interface already connected to OVS bridge, skip the configuration", "device", phyInterface, "bridge", bridgeConfig.BridgeName)
-		return ovsBridgeClient, nil
-	}
+	for i, phyInterface := range bridgeConfig.PhysicalInterfaces {
+		if _, err := ovsBridgeClient.GetOFPort(phyInterface, false); err == nil {
+			klog.V(2).InfoS("Physical interface already connected to OVS bridge, skip the configuration", "device", phyInterface, "bridge", bridgeConfig.BridgeName)
+			continue
+		}
 
-	_, err := ovsBridgeClient.CreateUplinkPort(phyInterface, 0, map[string]interface{}{interfacestore.AntreaInterfaceTypeKey: interfacestore.AntreaUplink})
-	if err != nil {
-		return nil, fmt.Errorf("failed to create OVS uplink port %s: %v", phyInterface, err)
+		if _, err := ovsBridgeClient.CreateUplinkPort(phyInterface, int32(i), map[string]interface{}{interfacestore.AntreaInterfaceTypeKey: interfacestore.AntreaUplink}); err != nil {
+			return nil, fmt.Errorf("failed to create OVS uplink port %s: %v", phyInterface, err)
+		}
+		klog.InfoS("Physical interface added to OVS bridge", "device", phyInterface, "bridge", bridgeConfig.BridgeName)
 	}
-	klog.InfoS("Physical interface added to OVS bridge", "device", phyInterface, "bridge", bridgeConfig.BridgeName)
-
 	return ovsBridgeClient, nil
 }
 
diff --git a/pkg/agent/secondarynetwork/podwatch/sriov.go b/pkg/agent/secondarynetwork/podwatch/sriov.go
index adbb613a2..ba7c34902 100644
--- a/pkg/agent/secondarynetwork/podwatch/sriov.go
+++ b/pkg/agent/secondarynetwork/podwatch/sriov.go
@@ -33,7 +33,6 @@ import (
 	podresourcesv1alpha1 "k8s.io/kubelet/pkg/apis/podresources/v1alpha1"
 
 	cnipodcache "antrea.io/antrea/pkg/agent/secondarynetwork/cnipodcache"
-	"antrea.io/antrea/pkg/agent/util"
 )
 
 const (
@@ -68,7 +67,7 @@ func getPodContainerDeviceIDs(podName string, podNamespace string) ([]string, er
 		path.Join(kubeletPodResourcesPath, kubeletSocket),
 		grpc.WithTransportCredentials(grpcinsecure.NewCredentials()),
 		grpc.WithContextDialer(func(ctx context.Context, addr string) (conn net.Conn, e error) {
-			return util.DialLocalSocket(addr)
+			return net.Dial("unix", addr)
 		}),
 	)
 	if err != nil {
diff --git a/pkg/agent/util/net.go b/pkg/agent/util/net.go
index 4e684b3b3..099242d3c 100644
--- a/pkg/agent/util/net.go
+++ b/pkg/agent/util/net.go
@@ -135,10 +135,6 @@ func listenUnix(address string) (net.Listener, error) {
 	return net.Listen("unix", address)
 }
 
-func dialUnix(address string) (net.Conn, error) {
-	return net.Dial("unix", address)
-}
-
 // GetIPNetDeviceFromIP returns local IPs/masks and associated device from IP, and ignores the interfaces which have
 // names in the ignoredInterfaces.
 func GetIPNetDeviceFromIP(localIPs *ip.DualStackIPs, ignoredInterfaces sets.Set[string]) (v4IPNet *net.IPNet, v6IPNet *net.IPNet, iface *net.Interface, err error) {
diff --git a/pkg/agent/util/net_linux.go b/pkg/agent/util/net_linux.go
index 80459d737..db1c5d8de 100644
--- a/pkg/agent/util/net_linux.go
+++ b/pkg/agent/util/net_linux.go
@@ -230,11 +230,6 @@ func ListenLocalSocket(address string) (net.Listener, error) {
 	return listener, nil
 }
 
-// DialLocalSocket connects to a Unix domain socket.
-func DialLocalSocket(address string) (net.Conn, error) {
-	return dialUnix(address)
-}
-
 // SetAdapterMACAddress set specified MAC address on interface.
 func SetAdapterMACAddress(adapterName string, macConfig *net.HardwareAddr) error {
 	link, err := netlinkUtil.LinkByName(adapterName)
diff --git a/pkg/agent/util/net_windows.go b/pkg/agent/util/net_windows.go
index 39b4a62cd..1848c2e9a 100644
--- a/pkg/agent/util/net_windows.go
+++ b/pkg/agent/util/net_windows.go
@@ -654,16 +654,6 @@ func ListenLocalSocket(address string) (net.Listener, error) {
 	return listenUnix(address)
 }
 
-// DialLocalSocket connects to a Unix domain socket or a Windows named pipe.
-// - If the specified address starts with "\\.\pipe\",  connect to a Windows named pipe path.
-// - Else connect to a Unix domain socket.
-func DialLocalSocket(address string) (net.Conn, error) {
-	if strings.HasPrefix(address, namedPipePrefix) {
-		return winio.DialPipe(address, nil)
-	}
-	return dialUnix(address)
-}
-
 func HostInterfaceExists(ifaceName string) bool {
 	_, err := getAdapterInAllCompartmentsByName(ifaceName)
 	if err != nil {
diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go
index c653b01ce..ad597f215 100644
--- a/pkg/antctl/antctl.go
+++ b/pkg/antctl/antctl.go
@@ -24,6 +24,7 @@ import (
 	"antrea.io/antrea/pkg/agent/apiserver/handlers/podinterface"
 	"antrea.io/antrea/pkg/agent/apiserver/handlers/serviceexternalip"
 	fallbackversion "antrea.io/antrea/pkg/antctl/fallback/version"
+	"antrea.io/antrea/pkg/antctl/parameter"
 	"antrea.io/antrea/pkg/antctl/raw/featuregates"
 	"antrea.io/antrea/pkg/antctl/raw/multicluster"
 	"antrea.io/antrea/pkg/antctl/raw/proxy"
@@ -512,6 +513,37 @@ $ antctl get podmulticaststats pod -n namespace`,
 			},
 			transformedResponse: reflect.TypeOf(endpointserver.EndpointQueryResponse{}),
 		},
+		{
+			use:     "networkpolicyevaluation",
+			aliases: []string{"networkpoliciesevaluation", "networkpolicyeval", "networkpolicieseval", "netpoleval"},
+			short:   "Analyze effective NetworkPolicy rules.",
+			long:    "Analyze network policies in the cluster and return the rule expected to be effective on the source and destination endpoints provided.",
+			example: `  Query effective NetworkPolicy rule between two Pods
+  $ antctl query networkpolicyevaluation -S ns1/pod1 -D ns2/pod2
+`,
+			commandGroup: query,
+			controllerEndpoint: &endpoint{
+				resourceEndpoint: &resourceEndpoint{
+					groupVersionResource: &cpv1beta.NetworkPolicyEvaluationVersionResource,
+					params: []flagInfo{
+						{
+							name:      "source",
+							usage:     "Source endpoint, specified by <Namespace>/<name>.",
+							shorthand: "S",
+						},
+						{
+							name:      "destination",
+							usage:     "Destination endpoint, specified by <Namespace>/<name>.",
+							shorthand: "D",
+						},
+					},
+					parameterTransform: parameter.NewNetworkPolicyEvaluation,
+					restMethod:         restPost,
+				},
+				addonTransform: networkpolicy.EvaluationTransform,
+			},
+			transformedResponse: reflect.TypeOf(networkpolicy.EvaluationResponse{}),
+		},
 		{
 			use:   "flowrecords",
 			short: "Print the matching flow records in the flow aggregator",
diff --git a/pkg/antctl/client.go b/pkg/antctl/client.go
index 31f544185..410d0db38 100644
--- a/pkg/antctl/client.go
+++ b/pkg/antctl/client.go
@@ -161,22 +161,38 @@ func (c *client) resourceRequest(e *resourceEndpoint, opt *requestOption) (io.Re
 	// If timeout is zero, there will be no timeout.
 	restClient.Client.Timeout = opt.timeout
 
-	resGetter := restClient.Get().
+	var restRequest *rest.Request
+	if e.restMethod == restGet {
+		restRequest = restClient.Get()
+	} else if e.restMethod == restPost {
+		restRequest = restClient.Post()
+	}
+
+	restRequest = restRequest.
 		NamespaceIfScoped(opt.args["namespace"], e.namespaced).
 		Resource(e.groupVersionResource.Resource)
 
 	if len(e.resourceName) != 0 {
-		resGetter = resGetter.Name(e.resourceName)
+		restRequest = restRequest.Name(e.resourceName)
 	} else if name, ok := opt.args["name"]; ok {
-		resGetter = resGetter.Name(name)
+		restRequest = restRequest.Name(name)
 	}
 
 	for arg, val := range opt.args {
 		if arg != "name" && arg != "namespace" {
-			resGetter = resGetter.Param(arg, val)
+			restRequest = restRequest.Param(arg, val)
 		}
 	}
-	result := resGetter.Do(context.TODO())
+
+	if e.parameterTransform != nil {
+		obj, err := e.parameterTransform(opt.args)
+		if err != nil {
+			return nil, err
+		}
+		restRequest = restRequest.Body(obj)
+	}
+
+	result := restRequest.Do(context.TODO())
 	if result.Error() != nil {
 		return nil, generateMessage(opt.commandDefinition, opt.args, true /* isResourceRequest */, result.Error())
 	}
diff --git a/pkg/antctl/command_definition.go b/pkg/antctl/command_definition.go
index 633a99839..2382f3208 100644
--- a/pkg/antctl/command_definition.go
+++ b/pkg/antctl/command_definition.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	k8sruntime "k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/klog/v2"
 
@@ -104,6 +105,9 @@ type resourceEndpoint struct {
 	resourceName         string
 	namespaced           bool
 	supportSorting       bool
+	params               []flagInfo
+	parameterTransform   func(args map[string]string) (k8sruntime.Object, error)
+	restMethod           restMethod
 }
 
 func (e *resourceEndpoint) OutputType() OutputType {
@@ -134,6 +138,7 @@ func (e *resourceEndpoint) flags() []flagInfo {
 	if e.supportSorting {
 		flags = append(flags, getSortByFlag())
 	}
+	flags = append(flags, e.params...)
 	return flags
 }
 
@@ -145,6 +150,13 @@ func getSortByFlag() flagInfo {
 	}
 }
 
+type restMethod uint
+
+const (
+	restGet restMethod = iota
+	restPost
+)
+
 type nonResourceEndpoint struct {
 	path       string
 	params     []flagInfo
@@ -434,6 +446,7 @@ func (cd *commandDefinition) output(resp io.Reader, writer io.Writer, ft formatt
 			if cd.controllerEndpoint.nonResourceEndpoint != nil && cd.controllerEndpoint.nonResourceEndpoint.path == "/endpoint" {
 				return output.TableOutputForQueryEndpoint(obj, writer)
 			}
+			return output.TableOutputForGetCommands(obj, writer)
 		} else {
 			return output.TableOutput(obj, writer)
 		}
@@ -442,7 +455,6 @@ func (cd *commandDefinition) output(resp io.Reader, writer io.Writer, ft formatt
 	default:
 		return fmt.Errorf("unsupported format type: %v", ft)
 	}
-	return nil
 }
 
 func (cd *commandDefinition) collectFlags(cmd *cobra.Command, args []string) (map[string]string, error) {
diff --git a/pkg/antctl/parameter/parameter.go b/pkg/antctl/parameter/parameter.go
new file mode 100644
index 000000000..88c9631a6
--- /dev/null
+++ b/pkg/antctl/parameter/parameter.go
@@ -0,0 +1,57 @@
+// Copyright 2024 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 parameter
+
+import (
+	"fmt"
+	"strings"
+
+	"k8s.io/apimachinery/pkg/runtime"
+
+	cpv1beta "antrea.io/antrea/pkg/apis/controlplane/v1beta2"
+)
+
+// parsePeer parses Namespace/Pod name, empty string is returned if the argument is not of a
+// valid Namespace/Pod reference (missing pod name or invalid format). Namespace will be set
+// as default if missing, string without separator will be considered as pod name.
+func parsePeer(str string) (string, string) {
+	parts := strings.Split(str, "/")
+	ns, pod := "", ""
+	if len(parts) == 1 {
+		ns, pod = "default", parts[0]
+	} else if len(parts) == 2 {
+		ns, pod = parts[0], parts[1]
+	}
+	return ns, pod
+}
+
+func NewNetworkPolicyEvaluation(args map[string]string) (runtime.Object, error) {
+	var ns1, pod1, ns2, pod2 string
+	if val, ok := args["source"]; ok {
+		ns1, pod1 = parsePeer(val)
+	}
+	if val, ok := args["destination"]; ok {
+		ns2, pod2 = parsePeer(val)
+	}
+	if pod1 == "" || pod2 == "" {
+		return nil, fmt.Errorf("missing entities for NetworkPolicyEvaluation request: %v", args)
+	}
+	return &cpv1beta.NetworkPolicyEvaluation{
+		Request: &cpv1beta.NetworkPolicyEvaluationRequest{
+			Source:      cpv1beta.Entity{Pod: &cpv1beta.PodReference{Namespace: ns1, Name: pod1}},
+			Destination: cpv1beta.Entity{Pod: &cpv1beta.PodReference{Namespace: ns2, Name: pod2}},
+		},
+	}, nil
+}
diff --git a/pkg/antctl/transform/networkpolicy/transform.go b/pkg/antctl/transform/networkpolicy/transform.go
index b290a7b19..f9f3a089e 100644
--- a/pkg/antctl/transform/networkpolicy/transform.go
+++ b/pkg/antctl/transform/networkpolicy/transform.go
@@ -15,6 +15,7 @@
 package networkpolicy
 
 import (
+	"encoding/json"
 	"io"
 	"reflect"
 	"sort"
@@ -161,3 +162,38 @@ func (r Response) GetTableRow(maxColumnLength int) []string {
 func (r Response) SortRows() bool {
 	return false
 }
+
+type EvaluationResponse struct {
+	*cpv1beta.NetworkPolicyEvaluation
+}
+
+func EvaluationTransform(reader io.Reader, _ bool, _ map[string]string) (interface{}, error) {
+	var eval cpv1beta.NetworkPolicyEvaluation
+	if err := json.NewDecoder(reader).Decode(&eval); err != nil {
+		return nil, err
+	}
+	return EvaluationResponse{&eval}, nil
+}
+
+var _ common.TableOutput = new(EvaluationResponse)
+
+func (r EvaluationResponse) GetTableHeader() []string {
+	return []string{"NAME", "NAMESPACE", "POLICY-TYPE", "RULE-INDEX", "DIRECTION"}
+}
+
+func (r EvaluationResponse) GetTableRow(_ int) []string {
+	if r.NetworkPolicyEvaluation != nil && r.Response != nil {
+		return []string{
+			r.Response.NetworkPolicy.Name,
+			r.Response.NetworkPolicy.Namespace,
+			string(r.Response.NetworkPolicy.Type),
+			common.Int32ToString(r.Response.RuleIndex),
+			string(r.Response.Rule.Direction),
+		}
+	}
+	return make([]string, 5)
+}
+
+func (r EvaluationResponse) SortRows() bool {
+	return false
+}
diff --git a/pkg/apis/controlplane/register.go b/pkg/apis/controlplane/register.go
index 42a27ddea..10da97734 100644
--- a/pkg/apis/controlplane/register.go
+++ b/pkg/apis/controlplane/register.go
@@ -54,6 +54,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
 		&NetworkPolicy{},
 		&NetworkPolicyList{},
 		&NetworkPolicyStatus{},
+		&NetworkPolicyEvaluation{},
 		&NodeStatsSummary{},
 		&ClusterGroupMembers{},
 		&GroupMembers{},
diff --git a/pkg/apis/controlplane/types.go b/pkg/apis/controlplane/types.go
index fdfa76244..d5c601287 100644
--- a/pkg/apis/controlplane/types.go
+++ b/pkg/apis/controlplane/types.go
@@ -448,6 +448,42 @@ type NetworkPolicyNodeStatus struct {
 	Message string
 }
 
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// NetworkPolicyEvaluation contains the request and response for a NetworkPolicy evaluation.
+type NetworkPolicyEvaluation struct {
+	metav1.TypeMeta
+	Request  *NetworkPolicyEvaluationRequest
+	Response *NetworkPolicyEvaluationResponse
+}
+
+// Entity contains Namespace and Pod name as a request parameter.
+type Entity struct {
+	Pod *PodReference
+}
+
+// NetworkPolicyEvaluationRequest is the request body of NetworkPolicy evaluation.
+type NetworkPolicyEvaluationRequest struct {
+	Source      Entity
+	Destination Entity
+}
+
+// RuleRef contains basic information for the rule.
+type RuleRef struct {
+	Direction Direction
+	Name      string
+	Action    *crdv1beta1.RuleAction
+}
+
+// NetworkPolicyEvaluationResponse is the response of NetworkPolicy evaluation.
+type NetworkPolicyEvaluationResponse struct {
+	// The reference of the effective NetworkPolicy.
+	NetworkPolicy NetworkPolicyReference
+	RuleIndex     int32
+	// The content of the effective rule.
+	Rule RuleRef
+}
+
 type GroupReference struct {
 	// Namespace of the Group. Empty for ClusterGroup.
 	Namespace string
diff --git a/pkg/apis/controlplane/v1beta2/generated.pb.go b/pkg/apis/controlplane/v1beta2/generated.pb.go
index 3247f1e1f..19cb2e92d 100644
--- a/pkg/apis/controlplane/v1beta2/generated.pb.go
+++ b/pkg/apis/controlplane/v1beta2/generated.pb.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -411,10 +411,38 @@ func (m *EgressGroupPatch) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_EgressGroupPatch proto.InternalMessageInfo
 
+func (m *Entity) Reset()      { *m = Entity{} }
+func (*Entity) ProtoMessage() {}
+func (*Entity) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fbaa7d016762fa1d, []int{13}
+}
+func (m *Entity) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *Entity) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	b = b[:cap(b)]
+	n, err := m.MarshalToSizedBuffer(b)
+	if err != nil {
+		return nil, err
+	}
+	return b[:n], nil
+}
+func (m *Entity) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Entity.Merge(m, src)
+}
+func (m *Entity) XXX_Size() int {
+	return m.Size()
+}
+func (m *Entity) XXX_DiscardUnknown() {
+	xxx_messageInfo_Entity.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Entity proto.InternalMessageInfo
+
 func (m *ExternalEntityReference) Reset()      { *m = ExternalEntityReference{} }
 func (*ExternalEntityReference) ProtoMessage() {}
 func (*ExternalEntityReference) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{13}
+	return fileDescriptor_fbaa7d016762fa1d, []int{14}
 }
 func (m *ExternalEntityReference) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -442,7 +470,7 @@ var xxx_messageInfo_ExternalEntityReference proto.InternalMessageInfo
 func (m *GroupAssociation) Reset()      { *m = GroupAssociation{} }
 func (*GroupAssociation) ProtoMessage() {}
 func (*GroupAssociation) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{14}
+	return fileDescriptor_fbaa7d016762fa1d, []int{15}
 }
 func (m *GroupAssociation) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -470,7 +498,7 @@ var xxx_messageInfo_GroupAssociation proto.InternalMessageInfo
 func (m *GroupMember) Reset()      { *m = GroupMember{} }
 func (*GroupMember) ProtoMessage() {}
 func (*GroupMember) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{15}
+	return fileDescriptor_fbaa7d016762fa1d, []int{16}
 }
 func (m *GroupMember) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -498,7 +526,7 @@ var xxx_messageInfo_GroupMember proto.InternalMessageInfo
 func (m *GroupMembers) Reset()      { *m = GroupMembers{} }
 func (*GroupMembers) ProtoMessage() {}
 func (*GroupMembers) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{16}
+	return fileDescriptor_fbaa7d016762fa1d, []int{17}
 }
 func (m *GroupMembers) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -526,7 +554,7 @@ var xxx_messageInfo_GroupMembers proto.InternalMessageInfo
 func (m *GroupReference) Reset()      { *m = GroupReference{} }
 func (*GroupReference) ProtoMessage() {}
 func (*GroupReference) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{17}
+	return fileDescriptor_fbaa7d016762fa1d, []int{18}
 }
 func (m *GroupReference) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -554,7 +582,7 @@ var xxx_messageInfo_GroupReference proto.InternalMessageInfo
 func (m *HTTPProtocol) Reset()      { *m = HTTPProtocol{} }
 func (*HTTPProtocol) ProtoMessage() {}
 func (*HTTPProtocol) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{18}
+	return fileDescriptor_fbaa7d016762fa1d, []int{19}
 }
 func (m *HTTPProtocol) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -582,7 +610,7 @@ var xxx_messageInfo_HTTPProtocol proto.InternalMessageInfo
 func (m *IPBlock) Reset()      { *m = IPBlock{} }
 func (*IPBlock) ProtoMessage() {}
 func (*IPBlock) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{19}
+	return fileDescriptor_fbaa7d016762fa1d, []int{20}
 }
 func (m *IPBlock) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -610,7 +638,7 @@ var xxx_messageInfo_IPBlock proto.InternalMessageInfo
 func (m *IPGroupAssociation) Reset()      { *m = IPGroupAssociation{} }
 func (*IPGroupAssociation) ProtoMessage() {}
 func (*IPGroupAssociation) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{20}
+	return fileDescriptor_fbaa7d016762fa1d, []int{21}
 }
 func (m *IPGroupAssociation) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -638,7 +666,7 @@ var xxx_messageInfo_IPGroupAssociation proto.InternalMessageInfo
 func (m *IPNet) Reset()      { *m = IPNet{} }
 func (*IPNet) ProtoMessage() {}
 func (*IPNet) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{21}
+	return fileDescriptor_fbaa7d016762fa1d, []int{22}
 }
 func (m *IPNet) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -666,7 +694,7 @@ var xxx_messageInfo_IPNet proto.InternalMessageInfo
 func (m *L7Protocol) Reset()      { *m = L7Protocol{} }
 func (*L7Protocol) ProtoMessage() {}
 func (*L7Protocol) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{22}
+	return fileDescriptor_fbaa7d016762fa1d, []int{23}
 }
 func (m *L7Protocol) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -694,7 +722,7 @@ var xxx_messageInfo_L7Protocol proto.InternalMessageInfo
 func (m *MulticastGroupInfo) Reset()      { *m = MulticastGroupInfo{} }
 func (*MulticastGroupInfo) ProtoMessage() {}
 func (*MulticastGroupInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{23}
+	return fileDescriptor_fbaa7d016762fa1d, []int{24}
 }
 func (m *MulticastGroupInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -722,7 +750,7 @@ var xxx_messageInfo_MulticastGroupInfo proto.InternalMessageInfo
 func (m *NamedPort) Reset()      { *m = NamedPort{} }
 func (*NamedPort) ProtoMessage() {}
 func (*NamedPort) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{24}
+	return fileDescriptor_fbaa7d016762fa1d, []int{25}
 }
 func (m *NamedPort) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -750,7 +778,7 @@ var xxx_messageInfo_NamedPort proto.InternalMessageInfo
 func (m *NetworkPolicy) Reset()      { *m = NetworkPolicy{} }
 func (*NetworkPolicy) ProtoMessage() {}
 func (*NetworkPolicy) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{25}
+	return fileDescriptor_fbaa7d016762fa1d, []int{26}
 }
 func (m *NetworkPolicy) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -775,10 +803,94 @@ func (m *NetworkPolicy) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_NetworkPolicy proto.InternalMessageInfo
 
+func (m *NetworkPolicyEvaluation) Reset()      { *m = NetworkPolicyEvaluation{} }
+func (*NetworkPolicyEvaluation) ProtoMessage() {}
+func (*NetworkPolicyEvaluation) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fbaa7d016762fa1d, []int{27}
+}
+func (m *NetworkPolicyEvaluation) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *NetworkPolicyEvaluation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	b = b[:cap(b)]
+	n, err := m.MarshalToSizedBuffer(b)
+	if err != nil {
+		return nil, err
+	}
+	return b[:n], nil
+}
+func (m *NetworkPolicyEvaluation) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_NetworkPolicyEvaluation.Merge(m, src)
+}
+func (m *NetworkPolicyEvaluation) XXX_Size() int {
+	return m.Size()
+}
+func (m *NetworkPolicyEvaluation) XXX_DiscardUnknown() {
+	xxx_messageInfo_NetworkPolicyEvaluation.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NetworkPolicyEvaluation proto.InternalMessageInfo
+
+func (m *NetworkPolicyEvaluationRequest) Reset()      { *m = NetworkPolicyEvaluationRequest{} }
+func (*NetworkPolicyEvaluationRequest) ProtoMessage() {}
+func (*NetworkPolicyEvaluationRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fbaa7d016762fa1d, []int{28}
+}
+func (m *NetworkPolicyEvaluationRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *NetworkPolicyEvaluationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	b = b[:cap(b)]
+	n, err := m.MarshalToSizedBuffer(b)
+	if err != nil {
+		return nil, err
+	}
+	return b[:n], nil
+}
+func (m *NetworkPolicyEvaluationRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_NetworkPolicyEvaluationRequest.Merge(m, src)
+}
+func (m *NetworkPolicyEvaluationRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *NetworkPolicyEvaluationRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_NetworkPolicyEvaluationRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NetworkPolicyEvaluationRequest proto.InternalMessageInfo
+
+func (m *NetworkPolicyEvaluationResponse) Reset()      { *m = NetworkPolicyEvaluationResponse{} }
+func (*NetworkPolicyEvaluationResponse) ProtoMessage() {}
+func (*NetworkPolicyEvaluationResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fbaa7d016762fa1d, []int{29}
+}
+func (m *NetworkPolicyEvaluationResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *NetworkPolicyEvaluationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	b = b[:cap(b)]
+	n, err := m.MarshalToSizedBuffer(b)
+	if err != nil {
+		return nil, err
+	}
+	return b[:n], nil
+}
+func (m *NetworkPolicyEvaluationResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_NetworkPolicyEvaluationResponse.Merge(m, src)
+}
+func (m *NetworkPolicyEvaluationResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *NetworkPolicyEvaluationResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_NetworkPolicyEvaluationResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NetworkPolicyEvaluationResponse proto.InternalMessageInfo
+
 func (m *NetworkPolicyList) Reset()      { *m = NetworkPolicyList{} }
 func (*NetworkPolicyList) ProtoMessage() {}
 func (*NetworkPolicyList) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{26}
+	return fileDescriptor_fbaa7d016762fa1d, []int{30}
 }
 func (m *NetworkPolicyList) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -806,7 +918,7 @@ var xxx_messageInfo_NetworkPolicyList proto.InternalMessageInfo
 func (m *NetworkPolicyNodeStatus) Reset()      { *m = NetworkPolicyNodeStatus{} }
 func (*NetworkPolicyNodeStatus) ProtoMessage() {}
 func (*NetworkPolicyNodeStatus) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{27}
+	return fileDescriptor_fbaa7d016762fa1d, []int{31}
 }
 func (m *NetworkPolicyNodeStatus) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -834,7 +946,7 @@ var xxx_messageInfo_NetworkPolicyNodeStatus proto.InternalMessageInfo
 func (m *NetworkPolicyPeer) Reset()      { *m = NetworkPolicyPeer{} }
 func (*NetworkPolicyPeer) ProtoMessage() {}
 func (*NetworkPolicyPeer) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{28}
+	return fileDescriptor_fbaa7d016762fa1d, []int{32}
 }
 func (m *NetworkPolicyPeer) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -862,7 +974,7 @@ var xxx_messageInfo_NetworkPolicyPeer proto.InternalMessageInfo
 func (m *NetworkPolicyReference) Reset()      { *m = NetworkPolicyReference{} }
 func (*NetworkPolicyReference) ProtoMessage() {}
 func (*NetworkPolicyReference) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{29}
+	return fileDescriptor_fbaa7d016762fa1d, []int{33}
 }
 func (m *NetworkPolicyReference) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -890,7 +1002,7 @@ var xxx_messageInfo_NetworkPolicyReference proto.InternalMessageInfo
 func (m *NetworkPolicyRule) Reset()      { *m = NetworkPolicyRule{} }
 func (*NetworkPolicyRule) ProtoMessage() {}
 func (*NetworkPolicyRule) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{30}
+	return fileDescriptor_fbaa7d016762fa1d, []int{34}
 }
 func (m *NetworkPolicyRule) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -918,7 +1030,7 @@ var xxx_messageInfo_NetworkPolicyRule proto.InternalMessageInfo
 func (m *NetworkPolicyStats) Reset()      { *m = NetworkPolicyStats{} }
 func (*NetworkPolicyStats) ProtoMessage() {}
 func (*NetworkPolicyStats) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{31}
+	return fileDescriptor_fbaa7d016762fa1d, []int{35}
 }
 func (m *NetworkPolicyStats) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -946,7 +1058,7 @@ var xxx_messageInfo_NetworkPolicyStats proto.InternalMessageInfo
 func (m *NetworkPolicyStatus) Reset()      { *m = NetworkPolicyStatus{} }
 func (*NetworkPolicyStatus) ProtoMessage() {}
 func (*NetworkPolicyStatus) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{32}
+	return fileDescriptor_fbaa7d016762fa1d, []int{36}
 }
 func (m *NetworkPolicyStatus) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -974,7 +1086,7 @@ var xxx_messageInfo_NetworkPolicyStatus proto.InternalMessageInfo
 func (m *NodeReference) Reset()      { *m = NodeReference{} }
 func (*NodeReference) ProtoMessage() {}
 func (*NodeReference) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{33}
+	return fileDescriptor_fbaa7d016762fa1d, []int{37}
 }
 func (m *NodeReference) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1002,7 +1114,7 @@ var xxx_messageInfo_NodeReference proto.InternalMessageInfo
 func (m *NodeStatsSummary) Reset()      { *m = NodeStatsSummary{} }
 func (*NodeStatsSummary) ProtoMessage() {}
 func (*NodeStatsSummary) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{34}
+	return fileDescriptor_fbaa7d016762fa1d, []int{38}
 }
 func (m *NodeStatsSummary) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1030,7 +1142,7 @@ var xxx_messageInfo_NodeStatsSummary proto.InternalMessageInfo
 func (m *PaginationGetOptions) Reset()      { *m = PaginationGetOptions{} }
 func (*PaginationGetOptions) ProtoMessage() {}
 func (*PaginationGetOptions) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{35}
+	return fileDescriptor_fbaa7d016762fa1d, []int{39}
 }
 func (m *PaginationGetOptions) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1058,7 +1170,7 @@ var xxx_messageInfo_PaginationGetOptions proto.InternalMessageInfo
 func (m *PodReference) Reset()      { *m = PodReference{} }
 func (*PodReference) ProtoMessage() {}
 func (*PodReference) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{36}
+	return fileDescriptor_fbaa7d016762fa1d, []int{40}
 }
 func (m *PodReference) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1083,10 +1195,38 @@ func (m *PodReference) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_PodReference proto.InternalMessageInfo
 
+func (m *RuleRef) Reset()      { *m = RuleRef{} }
+func (*RuleRef) ProtoMessage() {}
+func (*RuleRef) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fbaa7d016762fa1d, []int{41}
+}
+func (m *RuleRef) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *RuleRef) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	b = b[:cap(b)]
+	n, err := m.MarshalToSizedBuffer(b)
+	if err != nil {
+		return nil, err
+	}
+	return b[:n], nil
+}
+func (m *RuleRef) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_RuleRef.Merge(m, src)
+}
+func (m *RuleRef) XXX_Size() int {
+	return m.Size()
+}
+func (m *RuleRef) XXX_DiscardUnknown() {
+	xxx_messageInfo_RuleRef.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RuleRef proto.InternalMessageInfo
+
 func (m *Service) Reset()      { *m = Service{} }
 func (*Service) ProtoMessage() {}
 func (*Service) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{37}
+	return fileDescriptor_fbaa7d016762fa1d, []int{42}
 }
 func (m *Service) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1114,7 +1254,7 @@ var xxx_messageInfo_Service proto.InternalMessageInfo
 func (m *ServiceReference) Reset()      { *m = ServiceReference{} }
 func (*ServiceReference) ProtoMessage() {}
 func (*ServiceReference) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{38}
+	return fileDescriptor_fbaa7d016762fa1d, []int{43}
 }
 func (m *ServiceReference) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1142,7 +1282,7 @@ var xxx_messageInfo_ServiceReference proto.InternalMessageInfo
 func (m *SupportBundleCollection) Reset()      { *m = SupportBundleCollection{} }
 func (*SupportBundleCollection) ProtoMessage() {}
 func (*SupportBundleCollection) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{39}
+	return fileDescriptor_fbaa7d016762fa1d, []int{44}
 }
 func (m *SupportBundleCollection) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1170,7 +1310,7 @@ var xxx_messageInfo_SupportBundleCollection proto.InternalMessageInfo
 func (m *SupportBundleCollectionList) Reset()      { *m = SupportBundleCollectionList{} }
 func (*SupportBundleCollectionList) ProtoMessage() {}
 func (*SupportBundleCollectionList) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{40}
+	return fileDescriptor_fbaa7d016762fa1d, []int{45}
 }
 func (m *SupportBundleCollectionList) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1198,7 +1338,7 @@ var xxx_messageInfo_SupportBundleCollectionList proto.InternalMessageInfo
 func (m *SupportBundleCollectionNodeStatus) Reset()      { *m = SupportBundleCollectionNodeStatus{} }
 func (*SupportBundleCollectionNodeStatus) ProtoMessage() {}
 func (*SupportBundleCollectionNodeStatus) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{41}
+	return fileDescriptor_fbaa7d016762fa1d, []int{46}
 }
 func (m *SupportBundleCollectionNodeStatus) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1226,7 +1366,7 @@ var xxx_messageInfo_SupportBundleCollectionNodeStatus proto.InternalMessageInfo
 func (m *SupportBundleCollectionStatus) Reset()      { *m = SupportBundleCollectionStatus{} }
 func (*SupportBundleCollectionStatus) ProtoMessage() {}
 func (*SupportBundleCollectionStatus) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{42}
+	return fileDescriptor_fbaa7d016762fa1d, []int{47}
 }
 func (m *SupportBundleCollectionStatus) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1254,7 +1394,7 @@ var xxx_messageInfo_SupportBundleCollectionStatus proto.InternalMessageInfo
 func (m *TLSProtocol) Reset()      { *m = TLSProtocol{} }
 func (*TLSProtocol) ProtoMessage() {}
 func (*TLSProtocol) Descriptor() ([]byte, []int) {
-	return fileDescriptor_fbaa7d016762fa1d, []int{43}
+	return fileDescriptor_fbaa7d016762fa1d, []int{48}
 }
 func (m *TLSProtocol) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1293,6 +1433,7 @@ func init() {
 	proto.RegisterType((*EgressGroup)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.EgressGroup")
 	proto.RegisterType((*EgressGroupList)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.EgressGroupList")
 	proto.RegisterType((*EgressGroupPatch)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.EgressGroupPatch")
+	proto.RegisterType((*Entity)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.Entity")
 	proto.RegisterType((*ExternalEntityReference)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.ExternalEntityReference")
 	proto.RegisterType((*GroupAssociation)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.GroupAssociation")
 	proto.RegisterType((*GroupMember)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.GroupMember")
@@ -1306,6 +1447,9 @@ func init() {
 	proto.RegisterType((*MulticastGroupInfo)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.MulticastGroupInfo")
 	proto.RegisterType((*NamedPort)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NamedPort")
 	proto.RegisterType((*NetworkPolicy)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicy")
+	proto.RegisterType((*NetworkPolicyEvaluation)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicyEvaluation")
+	proto.RegisterType((*NetworkPolicyEvaluationRequest)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicyEvaluationRequest")
+	proto.RegisterType((*NetworkPolicyEvaluationResponse)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicyEvaluationResponse")
 	proto.RegisterType((*NetworkPolicyList)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicyList")
 	proto.RegisterType((*NetworkPolicyNodeStatus)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicyNodeStatus")
 	proto.RegisterType((*NetworkPolicyPeer)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NetworkPolicyPeer")
@@ -1317,6 +1461,7 @@ func init() {
 	proto.RegisterType((*NodeStatsSummary)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.NodeStatsSummary")
 	proto.RegisterType((*PaginationGetOptions)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.PaginationGetOptions")
 	proto.RegisterType((*PodReference)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.PodReference")
+	proto.RegisterType((*RuleRef)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.RuleRef")
 	proto.RegisterType((*Service)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.Service")
 	proto.RegisterType((*ServiceReference)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.ServiceReference")
 	proto.RegisterType((*SupportBundleCollection)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.SupportBundleCollection")
@@ -1331,186 +1476,198 @@ func init() {
 }
 
 var fileDescriptor_fbaa7d016762fa1d = []byte{
-	// 2862 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x1b, 0x4b, 0x6f, 0x24, 0xc5,
-	0x79, 0xdb, 0x33, 0x63, 0x7b, 0xbe, 0x19, 0x7b, 0xbd, 0x65, 0x60, 0x1d, 0x60, 0xed, 0xa5, 0x49,
-	0xd0, 0x26, 0x22, 0x33, 0xd8, 0x01, 0x76, 0xc3, 0x4b, 0xf1, 0x78, 0xbd, 0x66, 0x82, 0x6d, 0x86,
-	0x9a, 0x01, 0x24, 0x08, 0x84, 0x76, 0x77, 0xcd, 0xb8, 0xb3, 0x3d, 0x5d, 0x4d, 0x75, 0x8d, 0x59,
-	0x73, 0x88, 0x88, 0x48, 0x0e, 0xe4, 0x45, 0x94, 0x4b, 0xc4, 0x2d, 0xb7, 0x5c, 0xf2, 0x0b, 0x38,
-	0x85, 0x43, 0x24, 0x8e, 0x44, 0x51, 0x14, 0x4e, 0x56, 0xd6, 0x51, 0x88, 0x38, 0xe4, 0x92, 0x5b,
-	0x36, 0x8a, 0x14, 0x55, 0x75, 0xf5, 0x73, 0x66, 0xd6, 0x3b, 0xb6, 0xd7, 0x91, 0xc2, 0x9e, 0x3c,
-	0xfd, 0xbd, 0xab, 0xea, 0xfb, 0xea, 0x7b, 0x74, 0x1b, 0x9e, 0x31, 0x5c, 0xce, 0x88, 0x51, 0xb1,
-	0x69, 0x35, 0xf8, 0x55, 0xf5, 0xae, 0x76, 0xaa, 0x86, 0x67, 0xfb, 0x55, 0x93, 0xba, 0x9c, 0x51,
-	0xc7, 0x73, 0x0c, 0x97, 0x54, 0x77, 0x16, 0xb7, 0x08, 0x37, 0x96, 0xaa, 0x1d, 0xe2, 0x12, 0x66,
-	0x70, 0x62, 0x55, 0x3c, 0x46, 0x39, 0x45, 0x95, 0x80, 0xeb, 0xbb, 0x36, 0x55, 0xbf, 0x2a, 0xde,
-	0xd5, 0x4e, 0x45, 0xf0, 0x57, 0x92, 0xfc, 0x15, 0xc5, 0x7f, 0xef, 0xa5, 0xe1, 0xfa, 0x7c, 0x6e,
-	0x70, 0xbf, 0xba, 0xb3, 0x68, 0x38, 0xde, 0xb6, 0xb1, 0x98, 0xd5, 0x74, 0xef, 0xd7, 0x3b, 0x36,
-	0xdf, 0xee, 0x6d, 0x55, 0x4c, 0xda, 0xad, 0x76, 0x68, 0x87, 0x56, 0x25, 0x78, 0xab, 0xd7, 0x96,
-	0x4f, 0xf2, 0x41, 0xfe, 0x52, 0xe4, 0x8f, 0x5e, 0xbd, 0xe4, 0x4b, 0x2d, 0x9e, 0xdd, 0x35, 0xcc,
-	0x6d, 0xdb, 0x25, 0x6c, 0x37, 0xd6, 0xd5, 0x25, 0xdc, 0xa8, 0xee, 0xf4, 0x2b, 0xa9, 0x0e, 0xe3,
-	0x62, 0x3d, 0x97, 0xdb, 0x5d, 0xd2, 0xc7, 0xf0, 0xf8, 0x41, 0x0c, 0xbe, 0xb9, 0x4d, 0xba, 0x46,
-	0x1f, 0xdf, 0x37, 0x86, 0xf1, 0xf5, 0xb8, 0xed, 0x54, 0x6d, 0x97, 0xfb, 0x9c, 0x65, 0x99, 0xf4,
-	0xbf, 0x6b, 0x50, 0x5e, 0xb6, 0x2c, 0x46, 0x7c, 0x7f, 0x8d, 0xd1, 0x9e, 0x87, 0xde, 0x80, 0x49,
-	0xb1, 0x12, 0xcb, 0xe0, 0xc6, 0x9c, 0x76, 0x5e, 0xbb, 0x50, 0x5a, 0x7a, 0xa4, 0x12, 0x08, 0xae,
-	0x24, 0x05, 0xc7, 0x67, 0x22, 0xa8, 0x2b, 0x3b, 0x8b, 0x95, 0xe7, 0xb7, 0xbe, 0x47, 0x4c, 0xbe,
-	0x41, 0xb8, 0x51, 0x43, 0x1f, 0xef, 0x2d, 0x9c, 0xda, 0xdf, 0x5b, 0x80, 0x18, 0x86, 0x23, 0xa9,
-	0xa8, 0x07, 0xe5, 0x8e, 0x50, 0xb5, 0x41, 0xba, 0x5b, 0x84, 0xf9, 0x73, 0x63, 0xe7, 0x73, 0x17,
-	0x4a, 0x4b, 0x4f, 0x8e, 0x78, 0xec, 0x95, 0xb5, 0x58, 0x46, 0xed, 0x2e, 0xa5, 0xb0, 0x9c, 0x00,
-	0xfa, 0x38, 0xa5, 0x46, 0xff, 0xa3, 0x06, 0x33, 0xc9, 0x95, 0xae, 0xdb, 0x3e, 0x47, 0xdf, 0xe9,
-	0x5b, 0x6d, 0xe5, 0xd6, 0x56, 0x2b, 0xb8, 0xe5, 0x5a, 0x67, 0x94, 0xea, 0xc9, 0x10, 0x92, 0x58,
-	0xa9, 0x01, 0x05, 0x9b, 0x93, 0x6e, 0xb8, 0xc4, 0xa7, 0x46, 0x5d, 0x62, 0xd2, 0xdc, 0xda, 0x94,
-	0x52, 0x54, 0xa8, 0x0b, 0x91, 0x38, 0x90, 0xac, 0xbf, 0x97, 0x83, 0x33, 0x49, 0xb2, 0x86, 0xc1,
-	0xcd, 0xed, 0x13, 0x38, 0xc4, 0x1f, 0x6a, 0x70, 0xc6, 0xb0, 0x2c, 0x62, 0xad, 0x1d, 0xf3, 0x51,
-	0x7e, 0x49, 0xa9, 0x15, 0xab, 0x4a, 0x4b, 0xc7, 0xfd, 0x0a, 0xd1, 0x8f, 0x35, 0x98, 0x65, 0xa4,
-	0x4b, 0x77, 0x32, 0x86, 0xe4, 0x8e, 0x6e, 0xc8, 0x7d, 0xca, 0x90, 0x59, 0xdc, 0x2f, 0x1f, 0x0f,
-	0x52, 0xaa, 0x7f, 0xae, 0xc1, 0xf4, 0xb2, 0xe7, 0x39, 0x36, 0xb1, 0x5a, 0xf4, 0xff, 0x3c, 0x9a,
-	0xfe, 0xac, 0x01, 0x4a, 0xaf, 0xf5, 0x04, 0xe2, 0xc9, 0x4c, 0xc7, 0xd3, 0x33, 0x23, 0xc7, 0x53,
-	0xca, 0xe0, 0x21, 0x11, 0xf5, 0x93, 0x1c, 0xcc, 0xa6, 0x09, 0xef, 0xc4, 0xd4, 0xff, 0x2e, 0xa6,
-	0xde, 0x84, 0xd9, 0x9a, 0xe1, 0xdb, 0xe6, 0x72, 0x8f, 0x6f, 0x13, 0x97, 0xdb, 0xa6, 0xc1, 0x6d,
-	0xea, 0xa2, 0x87, 0x61, 0xb2, 0xe7, 0x13, 0xe6, 0x1a, 0x5d, 0x22, 0x0f, 0xa3, 0x18, 0xfb, 0xcd,
-	0x8b, 0x0a, 0x8e, 0x23, 0x0a, 0x41, 0xed, 0x19, 0xbe, 0xff, 0x16, 0x65, 0xd6, 0xdc, 0x58, 0x9a,
-	0xba, 0xa1, 0xe0, 0x38, 0xa2, 0xd0, 0x17, 0x61, 0xa6, 0xd6, 0x73, 0x2d, 0x87, 0x5c, 0xb1, 0x1d,
-	0xd2, 0x24, 0x6c, 0x87, 0x30, 0x74, 0x0e, 0x72, 0x3d, 0xe6, 0x28, 0x55, 0x25, 0xc5, 0x9c, 0x7b,
-	0x11, 0xaf, 0x63, 0x01, 0xd7, 0xdf, 0x1f, 0x83, 0x73, 0x01, 0x4f, 0x40, 0x2f, 0xac, 0x5d, 0xa1,
-	0x6e, 0xdb, 0xee, 0xf4, 0x58, 0x60, 0xf0, 0x63, 0x50, 0xda, 0x22, 0x06, 0x23, 0xac, 0x45, 0xaf,
-	0x12, 0x57, 0x09, 0x9a, 0x55, 0x82, 0x4a, 0xb5, 0x18, 0x85, 0x93, 0x74, 0xe8, 0x21, 0x18, 0x37,
-	0x3c, 0xfb, 0x39, 0xb2, 0xab, 0xec, 0x9e, 0x56, 0x1c, 0xe3, 0xcb, 0x8d, 0xfa, 0x73, 0x64, 0x17,
-	0x2b, 0x2c, 0xfa, 0xb9, 0x06, 0xb3, 0x5b, 0xfd, 0xfb, 0x34, 0x97, 0x93, 0x8e, 0xba, 0x32, 0xea,
-	0x99, 0x0d, 0xd8, 0xf2, 0xda, 0x59, 0x71, 0x6e, 0x03, 0x10, 0x78, 0x90, 0x62, 0xfd, 0xd7, 0x79,
-	0x98, 0x5d, 0x71, 0x7a, 0x3e, 0x27, 0x2c, 0xe5, 0x5c, 0xb7, 0x3f, 0x8a, 0x7e, 0xa0, 0xc1, 0x0c,
-	0x69, 0xb7, 0x89, 0xc9, 0xed, 0x1d, 0x72, 0x8c, 0x41, 0x34, 0xa7, 0xb4, 0xce, 0xac, 0x66, 0x84,
-	0xe3, 0x3e, 0x75, 0xe8, 0xfb, 0x70, 0x26, 0x82, 0xd5, 0x1b, 0x35, 0x87, 0x9a, 0x57, 0xc3, 0xf8,
-	0x79, 0x6c, 0x54, 0x1b, 0xea, 0x8d, 0x4d, 0xc2, 0xe3, 0x10, 0x5e, 0xcd, 0xca, 0xc5, 0xfd, 0xaa,
-	0xd0, 0x25, 0x28, 0x73, 0xca, 0x0d, 0x27, 0x5c, 0x7e, 0xfe, 0xbc, 0x76, 0x21, 0x17, 0xdf, 0xeb,
-	0xad, 0x04, 0x0e, 0xa7, 0x28, 0xd1, 0x12, 0x80, 0x7c, 0x6e, 0x18, 0x1d, 0xe2, 0xcf, 0x15, 0x24,
-	0x5f, 0xb4, 0xdf, 0xad, 0x08, 0x83, 0x13, 0x54, 0xc2, 0xb7, 0xcd, 0x1e, 0x63, 0xc4, 0xe5, 0xe2,
-	0x79, 0x6e, 0x5c, 0x32, 0x45, 0xbe, 0xbd, 0x12, 0xa3, 0x70, 0x92, 0x4e, 0xff, 0x4c, 0x83, 0xd2,
-	0x6a, 0xe7, 0x0b, 0x50, 0x79, 0xfe, 0x41, 0x83, 0xd3, 0x89, 0x85, 0x9e, 0x40, 0xa2, 0x7c, 0x23,
-	0x9d, 0x28, 0x47, 0x5e, 0x61, 0xc2, 0xda, 0x21, 0x59, 0xf2, 0xa7, 0x39, 0x98, 0x49, 0x50, 0x05,
-	0x29, 0xd2, 0x02, 0xa0, 0xd1, 0xbe, 0x1f, 0xeb, 0x19, 0x26, 0xe4, 0xde, 0x49, 0x93, 0x03, 0xd2,
-	0xa4, 0x03, 0x67, 0x57, 0xaf, 0x71, 0x91, 0xee, 0x9c, 0x55, 0x97, 0xdb, 0x7c, 0x17, 0x93, 0x36,
-	0x61, 0xc4, 0x35, 0x09, 0x3a, 0x0f, 0xf9, 0x44, 0x9a, 0x2c, 0x2b, 0xd1, 0xf9, 0x4d, 0x91, 0x22,
-	0x25, 0x06, 0x55, 0xa1, 0x28, 0xfe, 0xfa, 0x9e, 0x61, 0x12, 0x95, 0x67, 0xce, 0x28, 0xb2, 0xe2,
-	0x66, 0x88, 0xc0, 0x31, 0x8d, 0xfe, 0x6f, 0x0d, 0x66, 0xa4, 0xfa, 0x65, 0xdf, 0xa7, 0xa6, 0x1d,
-	0x64, 0xb8, 0x13, 0xa9, 0x8f, 0x66, 0x0c, 0xa5, 0x51, 0xad, 0xff, 0xd0, 0xa5, 0xa0, 0xe4, 0x8e,
-	0x36, 0x29, 0xbe, 0xdc, 0x97, 0x33, 0xf2, 0x71, 0x9f, 0x46, 0xfd, 0xc3, 0x3c, 0x94, 0x12, 0x9b,
-	0x8f, 0x5e, 0x86, 0x9c, 0x47, 0x2d, 0xb5, 0xe6, 0x91, 0x7b, 0xbc, 0x06, 0xb5, 0x62, 0x33, 0x26,
-	0x44, 0x55, 0x21, 0x20, 0x42, 0x22, 0x7a, 0x57, 0x83, 0x69, 0x92, 0x3a, 0x55, 0x79, 0x3a, 0xa5,
-	0xa5, 0xb5, 0x91, 0xe3, 0x79, 0xb0, 0x6f, 0xd4, 0xd0, 0xfe, 0xde, 0xc2, 0x74, 0x06, 0x99, 0x51,
-	0x89, 0x1e, 0x82, 0x9c, 0xed, 0x05, 0x6e, 0x5d, 0xae, 0xdd, 0x25, 0x0c, 0xac, 0x37, 0xfc, 0x1b,
-	0x7b, 0x0b, 0xc5, 0x7a, 0x43, 0x35, 0x9e, 0x58, 0x10, 0xa0, 0xd7, 0xa1, 0xe0, 0x51, 0xc6, 0x45,
-	0xb2, 0x11, 0x27, 0xf2, 0xcd, 0x51, 0x6d, 0x14, 0x9e, 0x66, 0x35, 0x28, 0xe3, 0xf1, 0x8d, 0x23,
-	0x9e, 0x7c, 0x1c, 0x88, 0x45, 0xaf, 0x42, 0xde, 0xa5, 0x16, 0x91, 0x39, 0xa9, 0xb4, 0xf4, 0xf4,
-	0xc8, 0xe2, 0xa9, 0x45, 0xe2, 0x85, 0x4f, 0xca, 0x10, 0x10, 0x20, 0x29, 0x14, 0x75, 0x60, 0xc2,
-	0x27, 0x6c, 0xc7, 0x36, 0x83, 0xf4, 0x55, 0x5a, 0xfa, 0xd6, 0xa8, 0xf2, 0x9b, 0x01, 0x7b, 0xac,
-	0xa2, 0xb4, 0xbf, 0xb7, 0x30, 0x11, 0x42, 0x43, 0xe9, 0xfa, 0x07, 0x79, 0x28, 0xdf, 0x29, 0x88,
-	0xee, 0x14, 0x44, 0x83, 0x0a, 0xa2, 0xdf, 0x68, 0x30, 0x9d, 0xbe, 0x97, 0xd2, 0x57, 0xb3, 0x76,
-	0xf0, 0xd5, 0x1c, 0xdd, 0xf6, 0x63, 0x43, 0x6f, 0xfb, 0x1a, 0xe4, 0x7a, 0xb6, 0x25, 0x3b, 0x83,
-	0x62, 0xed, 0x91, 0xa8, 0x95, 0xa9, 0x5f, 0xbe, 0xb1, 0xb7, 0xf0, 0xc0, 0xb0, 0x11, 0x22, 0xdf,
-	0xf5, 0x88, 0x5f, 0x79, 0xb1, 0x7e, 0x19, 0x0b, 0x66, 0xfd, 0x6d, 0x28, 0x3f, 0xdb, 0x6a, 0x35,
-	0x1a, 0x8c, 0x72, 0x6a, 0x52, 0x47, 0x68, 0xdd, 0xa6, 0x3e, 0xcf, 0xe6, 0x98, 0x67, 0xa9, 0xcf,
-	0xb1, 0xc4, 0x88, 0x46, 0xa6, 0x4b, 0xf8, 0x36, 0xb5, 0xb2, 0x8d, 0xcc, 0x86, 0x84, 0x62, 0x85,
-	0x15, 0x92, 0x3c, 0x83, 0x6f, 0x2b, 0xf3, 0x22, 0x49, 0x0d, 0x83, 0x6f, 0x63, 0x89, 0xd1, 0x3f,
-	0xd2, 0x60, 0x42, 0x9d, 0x2b, 0x7a, 0x19, 0xf2, 0xa6, 0x6d, 0x31, 0x15, 0x38, 0x87, 0xf4, 0xa4,
-	0x48, 0xc9, 0x4a, 0xfd, 0x32, 0xc6, 0x52, 0x20, 0x7a, 0x0d, 0xc6, 0xc9, 0x35, 0x93, 0x78, 0x5c,
-	0x05, 0xca, 0x21, 0x45, 0x47, 0xab, 0x5c, 0x95, 0xc2, 0xb0, 0x12, 0xaa, 0xff, 0x47, 0x03, 0x54,
-	0x6f, 0x7c, 0x71, 0x53, 0x68, 0x1b, 0x0a, 0x72, 0x83, 0xd0, 0x83, 0x30, 0x66, 0x7b, 0x72, 0xad,
-	0xe5, 0xda, 0xec, 0xfe, 0xde, 0xc2, 0x58, 0xbd, 0x91, 0x4e, 0x2d, 0x63, 0xb6, 0x27, 0x82, 0xd7,
-	0x63, 0xa4, 0x6d, 0x5f, 0x5b, 0x27, 0x6e, 0x87, 0x6f, 0x4b, 0x0f, 0x2a, 0xc4, 0xc1, 0xdb, 0x48,
-	0xe0, 0x70, 0x8a, 0x52, 0xff, 0x9d, 0x06, 0xb0, 0x7e, 0x31, 0x72, 0xd3, 0x57, 0x20, 0xbf, 0xcd,
-	0xb9, 0x77, 0xd8, 0x54, 0x9d, 0x74, 0xf9, 0x20, 0x83, 0x08, 0x08, 0x96, 0x32, 0xd1, 0x4b, 0x90,
-	0xe3, 0x8e, 0xaf, 0x12, 0xf4, 0xc8, 0xf7, 0x6a, 0x6b, 0xbd, 0x19, 0x49, 0x96, 0x45, 0x40, 0x6b,
-	0xbd, 0x89, 0x85, 0x40, 0xfd, 0x03, 0x0d, 0xd0, 0x46, 0xcf, 0x11, 0x8d, 0xb5, 0xcf, 0xe5, 0xf6,
-	0xd5, 0xdd, 0x36, 0x45, 0x0f, 0x42, 0x41, 0xf6, 0x18, 0x2a, 0xe4, 0xa2, 0x94, 0x19, 0x1c, 0x4a,
-	0x80, 0x43, 0xaf, 0x43, 0xde, 0xa3, 0xd6, 0xa1, 0xc7, 0xcf, 0xa9, 0xd2, 0x24, 0x0e, 0x45, 0x6a,
-	0xf9, 0x58, 0xca, 0xd5, 0xdf, 0xd3, 0xa0, 0x18, 0xa5, 0x6d, 0x19, 0xba, 0x94, 0x05, 0x97, 0x40,
-	0x21, 0x49, 0xcf, 0x38, 0x96, 0x98, 0x5b, 0xb8, 0x9c, 0x2e, 0xc1, 0xa4, 0xa7, 0xf6, 0x41, 0x5d,
-	0x01, 0xf7, 0x47, 0x93, 0x1a, 0x05, 0xbf, 0x91, 0xf8, 0x8d, 0x23, 0x6a, 0xfd, 0x1f, 0x39, 0x98,
-	0xda, 0x24, 0xfc, 0x2d, 0xca, 0xae, 0x36, 0xa8, 0x63, 0x9b, 0xbb, 0x27, 0x10, 0x4d, 0x6d, 0x28,
-	0xb0, 0x9e, 0x43, 0xc2, 0x0d, 0x5e, 0x1e, 0xb9, 0x26, 0x49, 0xda, 0x8b, 0x7b, 0x0e, 0x89, 0xcf,
-	0x51, 0x3c, 0xf9, 0x38, 0x10, 0x8f, 0x9e, 0x86, 0xd3, 0x46, 0x6a, 0x22, 0x19, 0xe4, 0xce, 0xa2,
-	0x0c, 0x99, 0xd3, 0xe9, 0x61, 0xa5, 0x8f, 0xb3, 0xb4, 0xe8, 0x82, 0xd8, 0x54, 0x9b, 0x32, 0x51,
-	0x40, 0x8a, 0xc4, 0xa7, 0xd5, 0xca, 0xc1, 0x86, 0x06, 0x30, 0x1c, 0x61, 0xd1, 0xa3, 0x50, 0xe6,
-	0x36, 0x61, 0x21, 0x46, 0xa6, 0xbb, 0x42, 0x6d, 0x46, 0xa6, 0xc8, 0x04, 0x1c, 0xa7, 0xa8, 0x90,
-	0x0f, 0x45, 0x9f, 0xf6, 0x98, 0x2c, 0x7e, 0x54, 0xf9, 0x74, 0xe5, 0x68, 0x5b, 0x11, 0x79, 0xdd,
-	0x94, 0x48, 0x74, 0xcd, 0x50, 0x38, 0x8e, 0xf5, 0xe8, 0x7f, 0xd2, 0xe0, 0x4c, 0x8a, 0xe9, 0x04,
-	0xda, 0xea, 0xad, 0x74, 0x5b, 0xfd, 0xf4, 0x91, 0x16, 0x39, 0xa4, 0xb1, 0xfe, 0xa7, 0x06, 0x67,
-	0x53, 0x74, 0xa2, 0x4a, 0x6d, 0x72, 0x83, 0xf7, 0x7c, 0xf4, 0x30, 0x4c, 0x8a, 0x6a, 0x75, 0x73,
-	0xc0, 0xd4, 0x73, 0x53, 0xc1, 0x71, 0x44, 0x21, 0x2a, 0x17, 0xf5, 0xb6, 0xcf, 0xa6, 0xae, 0x8c,
-	0xb9, 0x44, 0xe5, 0xb2, 0x16, 0x61, 0x70, 0x82, 0x0a, 0x7d, 0x1b, 0x10, 0x23, 0x86, 0x63, 0xbf,
-	0x2d, 0x1f, 0xaf, 0x18, 0xb6, 0xd3, 0x63, 0x44, 0x46, 0xe2, 0x64, 0xed, 0x5e, 0xc5, 0x8b, 0x70,
-	0x1f, 0x05, 0x1e, 0xc0, 0x85, 0xbe, 0x0a, 0x13, 0x5d, 0xe2, 0xfb, 0xa2, 0x02, 0xca, 0x4b, 0x63,
-	0x4f, 0x2b, 0x01, 0x13, 0x1b, 0x01, 0x18, 0x87, 0x78, 0xf9, 0x16, 0x2b, 0xb5, 0xe8, 0x06, 0x21,
-	0x0c, 0x5d, 0x84, 0x29, 0x23, 0xf1, 0x6a, 0xcb, 0x9f, 0xd3, 0xa4, 0xd3, 0x9f, 0xd9, 0xdf, 0x5b,
-	0x98, 0x4a, 0xbe, 0xf3, 0xf2, 0x71, 0x9a, 0x0e, 0x11, 0x98, 0xb4, 0x3d, 0x55, 0x64, 0x06, 0x47,
-	0x75, 0x71, 0xf4, 0xfc, 0x2d, 0xf9, 0xe3, 0x0d, 0x8e, 0xaa, 0xcb, 0x48, 0x34, 0x5a, 0x80, 0x42,
-	0xfb, 0x4d, 0xcb, 0x0d, 0x83, 0xb1, 0x28, 0xce, 0xf2, 0xca, 0x0b, 0x97, 0x37, 0x7d, 0x1c, 0xc0,
-	0x11, 0x17, 0xb5, 0xa3, 0x6a, 0x01, 0xc2, 0xbe, 0xe8, 0xe8, 0x8d, 0x45, 0xa2, 0xfa, 0x0c, 0x65,
-	0xe3, 0x84, 0x1e, 0x71, 0x5b, 0x38, 0xc6, 0x16, 0x71, 0xea, 0x16, 0x11, 0x1d, 0x9c, 0x2d, 0xcb,
-	0xd6, 0xdc, 0x85, 0xa9, 0xe0, 0xb6, 0x58, 0x4f, 0xa3, 0x70, 0x96, 0x56, 0xff, 0x4c, 0x83, 0x7b,
-	0x06, 0x47, 0x23, 0x7a, 0x0c, 0xf2, 0xa2, 0x10, 0x54, 0xbe, 0xf7, 0x40, 0x78, 0x7f, 0xb7, 0x76,
-	0x3d, 0x72, 0x63, 0x6f, 0x21, 0x7d, 0x82, 0x02, 0x88, 0x25, 0xf9, 0xc8, 0xf3, 0x85, 0x28, 0x4f,
-	0xe4, 0x0e, 0x2a, 0x62, 0xf3, 0x47, 0x29, 0x62, 0x3f, 0x1a, 0xcf, 0x38, 0x9d, 0xb8, 0x73, 0xd1,
-	0x53, 0x50, 0xb4, 0x6c, 0x26, 0xda, 0x07, 0x1a, 0x8e, 0xe9, 0xe7, 0x43, 0x63, 0x2f, 0x87, 0x88,
-	0x1b, 0xc9, 0x07, 0x1c, 0x33, 0x20, 0x13, 0xf2, 0x6d, 0x46, 0xbb, 0xaa, 0x0c, 0x38, 0x5a, 0x42,
-	0x10, 0x31, 0x10, 0x2f, 0xfe, 0x0a, 0xa3, 0x5d, 0x2c, 0x85, 0xa3, 0xd7, 0x60, 0x8c, 0x53, 0x35,
-	0xda, 0x3f, 0x06, 0x15, 0xa0, 0x54, 0x8c, 0xb5, 0x28, 0x1e, 0xe3, 0x54, 0x44, 0x8f, 0x9f, 0xf6,
-	0xd9, 0x8b, 0x87, 0xf4, 0xd9, 0x38, 0x7a, 0x22, 0x47, 0x8d, 0x44, 0xcb, 0x97, 0x32, 0x99, 0x3c,
-	0x13, 0xa7, 0xfa, 0xbe, 0xcc, 0xf4, 0x12, 0x8c, 0x1b, 0xc1, 0x99, 0x8c, 0xcb, 0x33, 0x79, 0x46,
-	0xbe, 0x04, 0x09, 0x0f, 0xe3, 0x91, 0x9b, 0x7c, 0x72, 0xc2, 0x2c, 0xf5, 0xa5, 0xc9, 0x62, 0x45,
-	0x1c, 0x70, 0xc0, 0x83, 0x95, 0x34, 0xf4, 0x24, 0x4c, 0x11, 0xd7, 0xd8, 0x72, 0xc8, 0x3a, 0xed,
-	0x74, 0x6c, 0xb7, 0x33, 0x37, 0x21, 0xef, 0xba, 0xbb, 0x95, 0x29, 0x53, 0xab, 0x49, 0x24, 0x4e,
-	0xd3, 0x0e, 0xca, 0xcb, 0x93, 0x23, 0xe4, 0xe5, 0xd0, 0xcd, 0x8b, 0x43, 0xdd, 0xfc, 0x4d, 0x28,
-	0x39, 0x51, 0xf9, 0xea, 0xcf, 0x81, 0x3c, 0x8d, 0x27, 0x46, 0x3d, 0x8d, 0xb8, 0x02, 0x8e, 0x9b,
-	0xd0, 0x18, 0xe6, 0xe3, 0xa4, 0x0e, 0x71, 0x2c, 0x0e, 0xed, 0xc8, 0x5b, 0x62, 0xae, 0x94, 0xce,
-	0x31, 0xeb, 0x0a, 0x8e, 0x23, 0x0a, 0xfd, 0xfd, 0x1c, 0xa0, 0x94, 0x47, 0x89, 0x4c, 0xe5, 0xa3,
-	0x77, 0x35, 0x98, 0x72, 0x93, 0x60, 0x95, 0x8c, 0x8f, 0xab, 0x2c, 0x88, 0x8e, 0x27, 0x8d, 0x4f,
-	0xeb, 0x44, 0x1e, 0x94, 0x39, 0x33, 0xda, 0x6d, 0xdb, 0x94, 0x56, 0xa9, 0xa0, 0x7c, 0xfc, 0x26,
-	0x36, 0xc8, 0xef, 0x85, 0x2a, 0xe1, 0xf7, 0x42, 0x95, 0x56, 0x82, 0x3b, 0x31, 0x2c, 0x48, 0x40,
-	0x71, 0x4a, 0x03, 0x7a, 0x47, 0x83, 0x19, 0x51, 0xb2, 0x25, 0x49, 0xd4, 0x98, 0xe3, 0x89, 0x5b,
-	0x57, 0x8b, 0x33, 0x12, 0xe2, 0xd6, 0x2a, 0x8b, 0xc1, 0x7d, 0xda, 0xf4, 0xbf, 0x69, 0x30, 0xdb,
-	0x77, 0x22, 0xbd, 0x93, 0x98, 0x33, 0x39, 0x50, 0x10, 0xb5, 0x47, 0x98, 0x72, 0xd7, 0x8e, 0x74,
-	0xd6, 0x71, 0xd5, 0x13, 0xd7, 0x49, 0x02, 0xe6, 0xe3, 0x40, 0x89, 0xbe, 0x08, 0x53, 0xa9, 0x91,
-	0xde, 0xc1, 0x73, 0x6e, 0xfd, 0xc3, 0x02, 0xcc, 0x84, 0x72, 0xfd, 0x66, 0xaf, 0xdb, 0x35, 0xd8,
-	0x49, 0x74, 0x09, 0x3f, 0xd2, 0xe0, 0x74, 0xd2, 0x31, 0xed, 0x68, 0x8b, 0x6a, 0x47, 0xda, 0xa2,
-	0xc0, 0x37, 0xce, 0x2a, 0xdd, 0xa7, 0x37, 0xd3, 0x2a, 0x70, 0x56, 0x27, 0xfa, 0xad, 0x06, 0xf7,
-	0x07, 0x5a, 0xd4, 0x8b, 0xd9, 0x0c, 0x87, 0x72, 0xd4, 0xe3, 0x30, 0xea, 0xcb, 0xca, 0xa8, 0xfb,
-	0x97, 0x6f, 0xa2, 0x0f, 0xdf, 0xd4, 0x1a, 0xf4, 0x2b, 0x0d, 0xee, 0x0e, 0x08, 0xb2, 0x76, 0xe6,
-	0x8f, 0xcd, 0xce, 0x73, 0xca, 0xce, 0xbb, 0x97, 0x07, 0x29, 0xc2, 0x83, 0xf5, 0x8b, 0x7e, 0xa7,
-	0x1b, 0x76, 0xe4, 0xb2, 0xb4, 0x3a, 0x84, 0x31, 0xfd, 0x2d, 0x7d, 0x5c, 0x13, 0x45, 0x38, 0x1c,
-	0xeb, 0xd1, 0x5f, 0x83, 0xbb, 0x1a, 0x46, 0xc7, 0x76, 0x65, 0x89, 0xbd, 0x46, 0xf8, 0xf3, 0x9e,
-	0xf8, 0xe1, 0x07, 0x03, 0xb3, 0x4e, 0xe0, 0xf6, 0xb9, 0xe4, 0xc0, 0xac, 0x43, 0xb0, 0xc4, 0xa0,
-	0x07, 0xa1, 0xe0, 0xd8, 0x5d, 0x9b, 0xab, 0x16, 0x20, 0x0a, 0xa7, 0x75, 0x01, 0xc4, 0x01, 0x4e,
-	0x37, 0xa0, 0x9c, 0x6c, 0xf7, 0x6f, 0xc7, 0x5b, 0xa3, 0xdf, 0xe7, 0x20, 0x9c, 0x87, 0xa3, 0x47,
-	0x13, 0x7d, 0x7e, 0xa0, 0x62, 0xee, 0xe0, 0x1e, 0x1f, 0x6d, 0xaa, 0x09, 0xc3, 0xd8, 0x01, 0x71,
-	0xda, 0xe3, 0xb6, 0x53, 0x09, 0x3e, 0x78, 0xac, 0xd4, 0x5d, 0xfe, 0x3c, 0x6b, 0x72, 0x66, 0xbb,
-	0x9d, 0x60, 0x66, 0x93, 0x98, 0x47, 0x7c, 0x05, 0x26, 0x88, 0x2b, 0x87, 0x17, 0xb2, 0x9a, 0x2a,
-	0x04, 0x33, 0xfb, 0xd5, 0x00, 0x84, 0x43, 0x9c, 0xe8, 0x9f, 0x6d, 0xb3, 0xeb, 0x89, 0x8a, 0x56,
-	0x56, 0x9c, 0x85, 0xa0, 0x7f, 0xae, 0xaf, 0x6c, 0x34, 0x64, 0x95, 0x1b, 0x61, 0x43, 0xca, 0x95,
-	0xf0, 0x3d, 0x45, 0x82, 0x52, 0xc0, 0x70, 0x84, 0x95, 0x94, 0x1d, 0x25, 0x73, 0x3c, 0x41, 0xb9,
-	0x16, 0xc9, 0x54, 0x58, 0x74, 0x49, 0xbd, 0xb4, 0x56, 0x1d, 0x8f, 0x2c, 0x50, 0x8a, 0x99, 0xf7,
-	0xce, 0xe1, 0xb4, 0x2c, 0x45, 0x29, 0x96, 0xe7, 0x33, 0x53, 0x2e, 0x6f, 0x32, 0x5e, 0x5e, 0x33,
-	0x00, 0xe1, 0x10, 0x87, 0x2a, 0x00, 0x3e, 0x33, 0xd5, 0xaa, 0x65, 0x31, 0x52, 0xa8, 0x4d, 0x8b,
-	0xdb, 0xac, 0x19, 0x41, 0x71, 0x82, 0x42, 0x27, 0x30, 0x93, 0xed, 0x49, 0x6e, 0x87, 0xbb, 0xbc,
-	0x9f, 0x87, 0xb3, 0xcd, 0x9e, 0x27, 0x0e, 0x2a, 0xf8, 0xb4, 0x66, 0x85, 0x3a, 0x8e, 0x2a, 0xb3,
-	0x6f, 0xff, 0xa5, 0xfd, 0x2a, 0x14, 0xc9, 0x35, 0xcf, 0x66, 0xc4, 0x5a, 0x0e, 0xfd, 0xed, 0x6b,
-	0xb7, 0xa6, 0xa2, 0x65, 0x77, 0x49, 0xbc, 0xb4, 0xd5, 0x50, 0x08, 0x8e, 0xe5, 0x89, 0xbd, 0xf0,
-	0x6d, 0xd7, 0x24, 0x82, 0x54, 0x35, 0x39, 0x11, 0x43, 0x33, 0x44, 0xe0, 0x98, 0x46, 0x34, 0x92,
-	0xed, 0xe8, 0x63, 0x24, 0xe9, 0x83, 0x87, 0x68, 0x24, 0xb3, 0x1f, 0x35, 0xc5, 0x3b, 0x10, 0xc3,
-	0x70, 0x42, 0x0f, 0xfa, 0x99, 0x06, 0xd3, 0x46, 0xfa, 0x7b, 0xa2, 0xe0, 0xe5, 0xdb, 0xc6, 0xe1,
-	0x54, 0x0f, 0xf9, 0x36, 0xaa, 0x76, 0x8f, 0xb2, 0x63, 0x3a, 0xf3, 0x61, 0x51, 0x46, 0xb9, 0xfe,
-	0xb9, 0x06, 0xf7, 0x0d, 0xf1, 0x88, 0x13, 0x18, 0xfe, 0x38, 0xe9, 0xe1, 0xcf, 0xc8, 0xe5, 0xcd,
-	0x10, 0xcb, 0x87, 0x8c, 0x81, 0x7e, 0x39, 0x06, 0x0f, 0x0c, 0xe1, 0x38, 0xf4, 0x40, 0xe8, 0x49,
-	0x98, 0x0a, 0x7f, 0x27, 0xc3, 0x30, 0x2e, 0xa6, 0x93, 0x48, 0x9c, 0xa6, 0x0d, 0x55, 0xc9, 0x0b,
-	0x2b, 0xd7, 0xaf, 0x2a, 0xb8, 0xb4, 0x42, 0x0a, 0xe1, 0xe1, 0x26, 0xed, 0x7a, 0x0e, 0xe1, 0x24,
-	0xe8, 0xd2, 0x27, 0x63, 0x0f, 0x5f, 0x09, 0x11, 0x38, 0xa6, 0x11, 0x49, 0x8a, 0x30, 0x46, 0x99,
-	0xf4, 0xb0, 0xc4, 0x3c, 0x7b, 0x55, 0x00, 0x71, 0x80, 0xd3, 0xff, 0xa5, 0xc1, 0xb9, 0x21, 0x9b,
-	0x72, 0x62, 0x55, 0xee, 0x4e, 0xba, 0xca, 0x7d, 0xe1, 0x98, 0xdc, 0xe0, 0xc0, 0x7a, 0xf7, 0x61,
-	0x28, 0x25, 0x5e, 0x12, 0xa0, 0x73, 0x90, 0xf3, 0x5d, 0x3b, 0xfb, 0x41, 0x62, 0x73, 0xb3, 0x8e,
-	0x05, 0xbc, 0xd6, 0xfa, 0xf8, 0xfa, 0xfc, 0xa9, 0x4f, 0xae, 0xcf, 0x9f, 0xfa, 0xf4, 0xfa, 0xfc,
-	0xa9, 0x77, 0xf6, 0xe7, 0xb5, 0x8f, 0xf7, 0xe7, 0xb5, 0x4f, 0xf6, 0xe7, 0xb5, 0x4f, 0xf7, 0xe7,
-	0xb5, 0xbf, 0xec, 0xcf, 0x6b, 0xbf, 0xf8, 0xeb, 0xfc, 0xa9, 0x57, 0x2a, 0xa3, 0xfd, 0xa7, 0xc6,
-	0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x02, 0x8d, 0x03, 0xda, 0x31, 0x00, 0x00,
+	// 3050 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3b, 0x4d, 0x6c, 0x24, 0x47,
+	0xd5, 0xdb, 0xf3, 0x63, 0x7b, 0xde, 0xd8, 0x5e, 0x6f, 0x39, 0xc9, 0xfa, 0x4b, 0xb2, 0xf6, 0xa6,
+	0xf3, 0x7d, 0xd1, 0x7e, 0x28, 0x8c, 0xb3, 0x26, 0xc9, 0x2e, 0xf9, 0x13, 0x1e, 0xaf, 0xd7, 0x19,
+	0x62, 0x7b, 0x27, 0x65, 0x27, 0x11, 0x09, 0x09, 0x69, 0x77, 0xd7, 0x8c, 0x9b, 0xed, 0xe9, 0xee,
+	0xad, 0xaa, 0x76, 0xd6, 0x39, 0xa0, 0x20, 0xe0, 0x10, 0x02, 0x04, 0x71, 0x41, 0xb9, 0x71, 0xe3,
+	0xc2, 0x8d, 0x5b, 0x4e, 0xe4, 0x80, 0x94, 0x63, 0x10, 0x42, 0xe4, 0x64, 0x11, 0x23, 0x82, 0x72,
+	0x40, 0x48, 0xdc, 0x58, 0x84, 0x84, 0xea, 0xa7, 0xff, 0x66, 0x3c, 0xeb, 0x1d, 0xdb, 0x6b, 0x10,
+	0xd9, 0x93, 0xa7, 0xdf, 0x7b, 0xf5, 0xde, 0xab, 0x7a, 0xef, 0xd5, 0xfb, 0xe9, 0x36, 0x3c, 0x63,
+	0xf9, 0x9c, 0x12, 0xab, 0xe6, 0x06, 0xb3, 0xea, 0xd7, 0x6c, 0x78, 0xb5, 0x3d, 0x6b, 0x85, 0x2e,
+	0x9b, 0xb5, 0x03, 0x9f, 0xd3, 0xc0, 0x0b, 0x3d, 0xcb, 0x27, 0xb3, 0x5b, 0xe7, 0x37, 0x08, 0xb7,
+	0xe6, 0x66, 0xdb, 0xc4, 0x27, 0xd4, 0xe2, 0xc4, 0xa9, 0x85, 0x34, 0xe0, 0x01, 0xaa, 0xa9, 0x55,
+	0xdf, 0x70, 0x03, 0xfd, 0xab, 0x16, 0x5e, 0x6d, 0xd7, 0xc4, 0xfa, 0x5a, 0x76, 0x7d, 0x4d, 0xaf,
+	0xbf, 0xf7, 0x62, 0x7f, 0x79, 0x8c, 0x5b, 0x9c, 0xcd, 0x6e, 0x9d, 0xb7, 0xbc, 0x70, 0xd3, 0x3a,
+	0xdf, 0x2d, 0xe9, 0xde, 0x2f, 0xb6, 0x5d, 0xbe, 0x19, 0x6d, 0xd4, 0xec, 0xa0, 0x33, 0xdb, 0x0e,
+	0xda, 0xc1, 0xac, 0x04, 0x6f, 0x44, 0x2d, 0xf9, 0x24, 0x1f, 0xe4, 0x2f, 0x4d, 0xfe, 0xe8, 0xd5,
+	0x8b, 0x4c, 0x4a, 0x09, 0xdd, 0x8e, 0x65, 0x6f, 0xba, 0x3e, 0xa1, 0xdb, 0xa9, 0xac, 0x0e, 0xe1,
+	0xd6, 0xec, 0x56, 0xaf, 0x90, 0xd9, 0x7e, 0xab, 0x68, 0xe4, 0x73, 0xb7, 0x43, 0x7a, 0x16, 0x3c,
+	0xbe, 0xdf, 0x02, 0x66, 0x6f, 0x92, 0x8e, 0xd5, 0xb3, 0xee, 0x4b, 0xfd, 0xd6, 0x45, 0xdc, 0xf5,
+	0x66, 0x5d, 0x9f, 0x33, 0x4e, 0xbb, 0x17, 0x99, 0x7f, 0x36, 0x60, 0x74, 0xde, 0x71, 0x28, 0x61,
+	0x6c, 0x89, 0x06, 0x51, 0x88, 0x5e, 0x87, 0x11, 0xb1, 0x13, 0xc7, 0xe2, 0xd6, 0x94, 0x71, 0xd6,
+	0x38, 0x57, 0x9d, 0x7b, 0xa4, 0xa6, 0x18, 0xd7, 0xb2, 0x8c, 0x53, 0x9b, 0x08, 0xea, 0xda, 0xd6,
+	0xf9, 0xda, 0x95, 0x8d, 0x6f, 0x12, 0x9b, 0xaf, 0x10, 0x6e, 0xd5, 0xd1, 0x87, 0x3b, 0x33, 0x27,
+	0x76, 0x77, 0x66, 0x20, 0x85, 0xe1, 0x84, 0x2b, 0x8a, 0x60, 0xb4, 0x2d, 0x44, 0xad, 0x90, 0xce,
+	0x06, 0xa1, 0x6c, 0xaa, 0x70, 0xb6, 0x78, 0xae, 0x3a, 0xf7, 0xe4, 0x80, 0x66, 0xaf, 0x2d, 0xa5,
+	0x3c, 0xea, 0x77, 0x69, 0x81, 0xa3, 0x19, 0x20, 0xc3, 0x39, 0x31, 0xe6, 0x6f, 0x0d, 0x98, 0xc8,
+	0xee, 0x74, 0xd9, 0x65, 0x1c, 0x7d, 0xbd, 0x67, 0xb7, 0xb5, 0x5b, 0xdb, 0xad, 0x58, 0x2d, 0xf7,
+	0x3a, 0xa1, 0x45, 0x8f, 0xc4, 0x90, 0xcc, 0x4e, 0x2d, 0x28, 0xbb, 0x9c, 0x74, 0xe2, 0x2d, 0x3e,
+	0x35, 0xe8, 0x16, 0xb3, 0xea, 0xd6, 0xc7, 0xb4, 0xa0, 0x72, 0x43, 0xb0, 0xc4, 0x8a, 0xb3, 0xf9,
+	0x76, 0x11, 0x4e, 0x65, 0xc9, 0x9a, 0x16, 0xb7, 0x37, 0x8f, 0xc1, 0x88, 0xdf, 0x35, 0xe0, 0x94,
+	0xe5, 0x38, 0xc4, 0x59, 0x3a, 0x62, 0x53, 0xfe, 0x8f, 0x16, 0x2b, 0x76, 0x95, 0xe7, 0x8e, 0x7b,
+	0x05, 0xa2, 0xef, 0x1b, 0x30, 0x49, 0x49, 0x27, 0xd8, 0xea, 0x52, 0xa4, 0x78, 0x78, 0x45, 0xee,
+	0xd3, 0x8a, 0x4c, 0xe2, 0x5e, 0xfe, 0x78, 0x2f, 0xa1, 0xe6, 0x67, 0x06, 0x8c, 0xcf, 0x87, 0xa1,
+	0xe7, 0x12, 0x67, 0x3d, 0xf8, 0x2f, 0x8f, 0xa6, 0xdf, 0x1b, 0x80, 0xf2, 0x7b, 0x3d, 0x86, 0x78,
+	0xb2, 0xf3, 0xf1, 0xf4, 0xcc, 0xc0, 0xf1, 0x94, 0x53, 0xb8, 0x4f, 0x44, 0xbd, 0x53, 0x84, 0xc9,
+	0x3c, 0xe1, 0x9d, 0x98, 0xfa, 0xf7, 0xc5, 0xd4, 0x35, 0x98, 0xac, 0x5b, 0xcc, 0xb5, 0xe7, 0x23,
+	0xbe, 0x49, 0x7c, 0xee, 0xda, 0x16, 0x77, 0x03, 0x1f, 0x3d, 0x0c, 0x23, 0x11, 0x23, 0xd4, 0xb7,
+	0x3a, 0x44, 0x1a, 0xa3, 0x92, 0xfa, 0xcd, 0x0b, 0x1a, 0x8e, 0x13, 0x0a, 0x41, 0x1d, 0x5a, 0x8c,
+	0xbd, 0x11, 0x50, 0x67, 0xaa, 0x90, 0xa7, 0x6e, 0x6a, 0x38, 0x4e, 0x28, 0xcc, 0xf3, 0x30, 0x51,
+	0x8f, 0x7c, 0xc7, 0x23, 0x97, 0x5d, 0x8f, 0xac, 0x11, 0xba, 0x45, 0x28, 0x3a, 0x03, 0xc5, 0x88,
+	0x7a, 0x5a, 0x54, 0x55, 0x2f, 0x2e, 0xbe, 0x80, 0x97, 0xb1, 0x80, 0x9b, 0xef, 0x16, 0xe0, 0x8c,
+	0x5a, 0xa3, 0xe8, 0x85, 0xb6, 0x0b, 0x81, 0xdf, 0x72, 0xdb, 0x11, 0x55, 0x0a, 0x3f, 0x06, 0xd5,
+	0x0d, 0x62, 0x51, 0x42, 0xd7, 0x83, 0xab, 0xc4, 0xd7, 0x8c, 0x26, 0x35, 0xa3, 0x6a, 0x3d, 0x45,
+	0xe1, 0x2c, 0x1d, 0x7a, 0x08, 0x86, 0xac, 0xd0, 0x7d, 0x8e, 0x6c, 0x6b, 0xbd, 0xc7, 0xf5, 0x8a,
+	0xa1, 0xf9, 0x66, 0xe3, 0x39, 0xb2, 0x8d, 0x35, 0x16, 0xfd, 0xc8, 0x80, 0xc9, 0x8d, 0xde, 0x73,
+	0x9a, 0x2a, 0x4a, 0x47, 0x5d, 0x18, 0xd4, 0x66, 0x7b, 0x1c, 0x79, 0xfd, 0xb4, 0xb0, 0xdb, 0x1e,
+	0x08, 0xbc, 0x97, 0x60, 0xf3, 0x67, 0x25, 0x98, 0x5c, 0xf0, 0x22, 0xc6, 0x09, 0xcd, 0x39, 0xd7,
+	0xed, 0x8f, 0xa2, 0x6f, 0x1b, 0x30, 0x41, 0x5a, 0x2d, 0x62, 0x73, 0x77, 0x8b, 0x1c, 0x61, 0x10,
+	0x4d, 0x69, 0xa9, 0x13, 0x8b, 0x5d, 0xcc, 0x71, 0x8f, 0x38, 0xf4, 0x2d, 0x38, 0x95, 0xc0, 0x1a,
+	0xcd, 0xba, 0x17, 0xd8, 0x57, 0xe3, 0xf8, 0x79, 0x6c, 0x50, 0x1d, 0x1a, 0xcd, 0x55, 0xc2, 0xd3,
+	0x10, 0x5e, 0xec, 0xe6, 0x8b, 0x7b, 0x45, 0xa1, 0x8b, 0x30, 0xca, 0x03, 0x6e, 0x79, 0xf1, 0xf6,
+	0x4b, 0x67, 0x8d, 0x73, 0xc5, 0xf4, 0x5e, 0x5f, 0xcf, 0xe0, 0x70, 0x8e, 0x12, 0xcd, 0x01, 0xc8,
+	0xe7, 0xa6, 0xd5, 0x26, 0x6c, 0xaa, 0x2c, 0xd7, 0x25, 0xe7, 0xbd, 0x9e, 0x60, 0x70, 0x86, 0x4a,
+	0xf8, 0xb6, 0x1d, 0x51, 0x4a, 0x7c, 0x2e, 0x9e, 0xa7, 0x86, 0xe4, 0xa2, 0xc4, 0xb7, 0x17, 0x52,
+	0x14, 0xce, 0xd2, 0x99, 0x9f, 0x1a, 0x50, 0x5d, 0x6c, 0x7f, 0x0e, 0x2a, 0xcf, 0xdf, 0x18, 0x70,
+	0x32, 0xb3, 0xd1, 0x63, 0x48, 0x94, 0xaf, 0xe7, 0x13, 0xe5, 0xc0, 0x3b, 0xcc, 0x68, 0xdb, 0x27,
+	0x4b, 0xfe, 0xa0, 0x08, 0x13, 0x19, 0x2a, 0x95, 0x22, 0x1d, 0x80, 0x20, 0x39, 0xf7, 0x23, 0xb5,
+	0x61, 0x86, 0xef, 0x9d, 0x34, 0xb9, 0x47, 0x9a, 0xb4, 0x60, 0x68, 0xd1, 0xe7, 0x2e, 0xdf, 0x46,
+	0x2f, 0x41, 0x31, 0x0c, 0x1c, 0x7d, 0xf8, 0x03, 0x77, 0x1c, 0xcd, 0xc0, 0xc1, 0xa4, 0x45, 0x28,
+	0xf1, 0x6d, 0x52, 0x1f, 0x16, 0x39, 0x4e, 0x40, 0x04, 0x47, 0xd3, 0x83, 0xd3, 0x8b, 0xd7, 0xb9,
+	0xc8, 0xa8, 0x9e, 0x12, 0x95, 0x10, 0xa2, 0xb3, 0x50, 0xca, 0x64, 0xe2, 0x51, 0xad, 0x7d, 0x69,
+	0x55, 0x64, 0x61, 0x89, 0x41, 0xb3, 0x50, 0x11, 0x7f, 0x59, 0x68, 0xd9, 0x44, 0xa7, 0xb2, 0x53,
+	0x9a, 0xac, 0xb2, 0x1a, 0x23, 0x70, 0x4a, 0x63, 0xfe, 0xc3, 0x80, 0x09, 0xb9, 0xc3, 0x79, 0xc6,
+	0x02, 0xdb, 0x55, 0x49, 0xf4, 0x58, 0x4a, 0xb0, 0x09, 0x4b, 0x4b, 0xd4, 0x47, 0x7c, 0xe0, 0x6a,
+	0x53, 0xae, 0x4e, 0x4f, 0x33, 0xc9, 0x1f, 0xf3, 0x5d, 0xfc, 0x71, 0x8f, 0x44, 0xf3, 0xfd, 0x12,
+	0x54, 0x33, 0xf6, 0xbd, 0x6d, 0x46, 0x45, 0xdf, 0x31, 0x60, 0x9c, 0xe4, 0xac, 0x2a, 0xad, 0x53,
+	0x9d, 0x5b, 0x1a, 0xf8, 0xca, 0xd8, 0xdb, 0x37, 0xea, 0x68, 0x77, 0x67, 0x66, 0xbc, 0x0b, 0xd9,
+	0x25, 0x12, 0x3d, 0x04, 0x45, 0x37, 0x54, 0x91, 0x33, 0x5a, 0xbf, 0x4b, 0x28, 0xd8, 0x68, 0xb2,
+	0x1b, 0x3b, 0x33, 0x95, 0x46, 0x53, 0xf7, 0xb6, 0x58, 0x10, 0xa0, 0xd7, 0xa0, 0x1c, 0x06, 0x94,
+	0x8b, 0x7c, 0x26, 0x2c, 0xf2, 0xe5, 0x41, 0x75, 0x14, 0x9e, 0xe6, 0x34, 0x03, 0xca, 0xd3, 0x4b,
+	0x4d, 0x3c, 0x31, 0xac, 0xd8, 0xa2, 0x57, 0xa0, 0xe4, 0x07, 0x0e, 0x91, 0x69, 0xaf, 0x3a, 0xf7,
+	0xf4, 0xc0, 0xec, 0x03, 0x87, 0xa4, 0x1b, 0x1f, 0x91, 0x21, 0x20, 0x40, 0x92, 0x29, 0x6a, 0xc3,
+	0x30, 0x23, 0x74, 0xcb, 0xb5, 0x55, 0x86, 0xac, 0xce, 0x7d, 0x65, 0x50, 0xfe, 0x6b, 0x6a, 0x79,
+	0x2a, 0xa2, 0xba, 0xbb, 0x33, 0x33, 0x1c, 0x43, 0x63, 0xee, 0xe6, 0x7b, 0x25, 0x18, 0xbd, 0x53,
+	0x73, 0xdd, 0xa9, 0xb9, 0xf6, 0xaa, 0xb9, 0x7e, 0x6e, 0xc0, 0x78, 0xfe, 0x5e, 0xca, 0x5f, 0xcd,
+	0xc6, 0xfe, 0x57, 0x73, 0x72, 0xdb, 0x17, 0xfa, 0xde, 0xf6, 0x75, 0x28, 0x46, 0xae, 0x23, 0x9b,
+	0x8f, 0x4a, 0xfd, 0x91, 0xa4, 0x5b, 0x6a, 0x5c, 0xba, 0xb1, 0x33, 0xf3, 0x40, 0xbf, 0x29, 0x25,
+	0xdf, 0x0e, 0x09, 0xab, 0xbd, 0xd0, 0xb8, 0x84, 0xc5, 0x62, 0xf3, 0x4d, 0x18, 0x7d, 0x76, 0x7d,
+	0xbd, 0xd9, 0xa4, 0x01, 0x0f, 0xec, 0xc0, 0x13, 0x52, 0x37, 0x03, 0xc6, 0xbb, 0x73, 0xcc, 0xb3,
+	0x01, 0xe3, 0x58, 0x62, 0x44, 0xaf, 0xd4, 0x21, 0x7c, 0x33, 0x70, 0xba, 0x7b, 0xa5, 0x15, 0x09,
+	0xc5, 0x1a, 0x2b, 0x38, 0x85, 0x16, 0xdf, 0xd4, 0xea, 0x25, 0x9c, 0x9a, 0x16, 0xdf, 0xc4, 0x12,
+	0x63, 0x7e, 0x60, 0xc0, 0xb0, 0xb6, 0x2b, 0x7a, 0x09, 0x4a, 0xb6, 0xeb, 0x50, 0x1d, 0x38, 0x07,
+	0xf4, 0xa4, 0x44, 0xc8, 0x42, 0xe3, 0x12, 0xc6, 0x92, 0x21, 0x7a, 0x15, 0x86, 0xc8, 0x75, 0x9b,
+	0x84, 0x5c, 0x07, 0xca, 0x01, 0x59, 0x27, 0xbb, 0x5c, 0x94, 0xcc, 0xb0, 0x66, 0x6a, 0xfe, 0xd3,
+	0x00, 0xd4, 0x68, 0x7e, 0x7e, 0x53, 0x68, 0x0b, 0xca, 0xf2, 0x80, 0xd0, 0x83, 0x50, 0x70, 0x43,
+	0xb9, 0xd7, 0xd1, 0xfa, 0xe4, 0xee, 0xce, 0x4c, 0xa1, 0xd1, 0xcc, 0xa7, 0x96, 0x82, 0x1b, 0x8a,
+	0xe0, 0x0d, 0x29, 0x69, 0xb9, 0xd7, 0x97, 0x89, 0xdf, 0xe6, 0x9b, 0xd2, 0x83, 0xca, 0x69, 0xf0,
+	0x36, 0x33, 0x38, 0x9c, 0xa3, 0x34, 0x7f, 0x65, 0x00, 0x2c, 0x5f, 0x48, 0xdc, 0xf4, 0x65, 0x28,
+	0x6d, 0x72, 0x1e, 0x1e, 0x34, 0x55, 0x67, 0x5d, 0x5e, 0x65, 0x10, 0x01, 0xc1, 0x92, 0x27, 0x7a,
+	0x11, 0x8a, 0xdc, 0x63, 0x3a, 0x41, 0x0f, 0x7c, 0xaf, 0xae, 0x2f, 0xaf, 0x25, 0x9c, 0x65, 0x11,
+	0xb0, 0xbe, 0xbc, 0x86, 0x05, 0x43, 0xf3, 0x3d, 0x03, 0xd0, 0x4a, 0xe4, 0x89, 0xde, 0x9d, 0x71,
+	0x79, 0x7c, 0x0d, 0xbf, 0x15, 0xa0, 0x07, 0xa1, 0x2c, 0xdb, 0x18, 0x1d, 0x72, 0x49, 0xca, 0x54,
+	0x46, 0x51, 0x38, 0xf4, 0x1a, 0x94, 0xc2, 0xc0, 0x39, 0xf0, 0x84, 0x3b, 0x57, 0x9a, 0xa4, 0xa1,
+	0x18, 0x38, 0x0c, 0x4b, 0xbe, 0xe6, 0xdb, 0x06, 0x54, 0x92, 0xb4, 0x2d, 0x43, 0x37, 0xa0, 0xea,
+	0x12, 0x28, 0x67, 0xe9, 0x29, 0xc7, 0x12, 0x73, 0x0b, 0x97, 0xd3, 0x45, 0x18, 0x09, 0xf5, 0x39,
+	0xe8, 0x2b, 0xe0, 0xfe, 0x64, 0x18, 0xa4, 0xe1, 0x37, 0x32, 0xbf, 0x71, 0x42, 0x6d, 0xfe, 0xa5,
+	0x08, 0x63, 0xab, 0x84, 0xbf, 0x11, 0xd0, 0xab, 0xcd, 0xc0, 0x73, 0xed, 0xed, 0x63, 0x88, 0xa6,
+	0x16, 0x94, 0x69, 0xe4, 0x91, 0xf8, 0x80, 0xe7, 0x07, 0xae, 0x49, 0xb2, 0xfa, 0xe2, 0xc8, 0x23,
+	0xa9, 0x1d, 0xc5, 0x13, 0xc3, 0x8a, 0x3d, 0x7a, 0x1a, 0x4e, 0x5a, 0xb9, 0xa1, 0xa7, 0xca, 0x9d,
+	0x15, 0x19, 0x32, 0x27, 0xf3, 0xf3, 0x50, 0x86, 0xbb, 0x69, 0xd1, 0x39, 0x71, 0xa8, 0x6e, 0x40,
+	0x45, 0x01, 0x29, 0x12, 0x9f, 0x51, 0x1f, 0x55, 0x07, 0xaa, 0x60, 0x38, 0xc1, 0xa2, 0x47, 0x61,
+	0x94, 0xbb, 0x84, 0xc6, 0x18, 0x99, 0xee, 0xca, 0xf5, 0x09, 0x99, 0x22, 0x33, 0x70, 0x9c, 0xa3,
+	0x42, 0x0c, 0x2a, 0x2c, 0x88, 0xa8, 0x2c, 0x7e, 0x74, 0xf9, 0x74, 0xf9, 0x70, 0x47, 0x91, 0x78,
+	0xdd, 0x98, 0x48, 0x74, 0x6b, 0x31, 0x73, 0x9c, 0xca, 0x31, 0xdf, 0x29, 0xc0, 0xe9, 0xdc, 0xa2,
+	0xc5, 0x2d, 0xcb, 0x8b, 0xd4, 0x3d, 0x1a, 0xc1, 0x30, 0x25, 0xd7, 0x22, 0xa2, 0x33, 0x52, 0x75,
+	0x6e, 0xf5, 0x50, 0xea, 0xa4, 0x9c, 0xb1, 0xe2, 0xaa, 0x6a, 0x3b, 0xfd, 0x80, 0x63, 0x59, 0x68,
+	0x1b, 0x46, 0x28, 0x61, 0x61, 0xe0, 0x33, 0xa2, 0xef, 0x81, 0x2b, 0x47, 0x26, 0x57, 0xb1, 0x55,
+	0x86, 0x8b, 0x9f, 0x70, 0x22, 0xce, 0xfc, 0xab, 0x01, 0xd3, 0x37, 0xd7, 0x19, 0xbd, 0x06, 0x43,
+	0xea, 0xf4, 0xf4, 0x99, 0x3c, 0x3e, 0x70, 0x13, 0x21, 0xfb, 0x81, 0x34, 0xa7, 0x69, 0xb3, 0x68,
+	0xae, 0xa8, 0x03, 0x55, 0x87, 0x30, 0xee, 0xfa, 0x6a, 0xb8, 0x59, 0x38, 0x94, 0x90, 0xa4, 0x58,
+	0xba, 0x94, 0xb2, 0xc4, 0x59, 0xfe, 0xe6, 0x2f, 0x0b, 0x30, 0xb3, 0xcf, 0x69, 0x89, 0x06, 0x6a,
+	0xcc, 0xcf, 0xd2, 0xe8, 0xad, 0x1f, 0x95, 0x77, 0xde, 0xad, 0xb5, 0xcc, 0x5f, 0x3c, 0x38, 0x2f,
+	0x53, 0xd4, 0x70, 0x22, 0x8c, 0x1b, 0xbe, 0x43, 0xae, 0xeb, 0xdc, 0x95, 0xd4, 0x70, 0x38, 0x46,
+	0xe0, 0x94, 0x06, 0x7d, 0x0d, 0x4a, 0xe2, 0x41, 0xcf, 0x87, 0x2f, 0x0c, 0xaa, 0xac, 0xe0, 0x89,
+	0x49, 0x2b, 0xbd, 0x5f, 0x25, 0x40, 0xb2, 0x34, 0x7f, 0x67, 0xc0, 0xa9, 0x9c, 0xb2, 0xc7, 0x30,
+	0xef, 0xda, 0xc8, 0xcf, 0xbb, 0x9e, 0x3e, 0xd4, 0xe1, 0xf7, 0x99, 0x78, 0xfd, 0xcd, 0xe8, 0xba,
+	0x0d, 0x44, 0x6f, 0xb7, 0xc6, 0x2d, 0x1e, 0x31, 0xf4, 0x30, 0x8c, 0x88, 0x1e, 0x6f, 0x75, 0x8f,
+	0xd7, 0x11, 0xab, 0x1a, 0x8e, 0x13, 0x0a, 0x51, 0xef, 0xeb, 0xd7, 0xf0, 0xb1, 0x17, 0x67, 0xea,
+	0xfd, 0xa5, 0x04, 0x83, 0x33, 0x54, 0xe8, 0xab, 0x80, 0x28, 0xb1, 0x3c, 0xf7, 0x4d, 0xf9, 0x78,
+	0xd9, 0x72, 0xbd, 0x88, 0x2a, 0xf3, 0x8d, 0xd4, 0xef, 0xd5, 0x6b, 0x11, 0xee, 0xa1, 0xc0, 0x7b,
+	0xac, 0x42, 0xff, 0x0f, 0xc3, 0x1d, 0xc2, 0x98, 0xe8, 0x1b, 0x4a, 0x52, 0xd9, 0x93, 0x9a, 0xc1,
+	0xf0, 0x8a, 0x02, 0xe3, 0x18, 0x2f, 0x5f, 0x2f, 0xe7, 0x36, 0xdd, 0x24, 0x84, 0xa2, 0x0b, 0x30,
+	0x66, 0x65, 0xde, 0x39, 0xb3, 0x29, 0x43, 0xa6, 0x8a, 0x53, 0xc2, 0x4f, 0xb3, 0x2f, 0xa3, 0x19,
+	0xce, 0xd3, 0x21, 0x02, 0x23, 0x6e, 0xa8, 0x5b, 0x33, 0x65, 0xaa, 0x0b, 0x83, 0x57, 0xbd, 0x72,
+	0x7d, 0x7a, 0xc0, 0x49, 0x4f, 0x96, 0xb0, 0x46, 0x33, 0x50, 0x6e, 0x5d, 0x73, 0xfc, 0x38, 0x85,
+	0x55, 0x84, 0x2d, 0x2f, 0x3f, 0x7f, 0x69, 0x95, 0x61, 0x05, 0x47, 0x5c, 0x74, 0x5c, 0xba, 0x71,
+	0x8e, 0xa7, 0x09, 0x87, 0x6f, 0xc7, 0x33, 0x3d, 0x5b, 0xcc, 0x1b, 0x67, 0xe4, 0x88, 0x1c, 0xeb,
+	0x59, 0x1b, 0xc4, 0x6b, 0x38, 0x44, 0x5c, 0x41, 0xae, 0x6c, 0xf6, 0x8a, 0xe7, 0xc6, 0x54, 0x8e,
+	0x5d, 0xce, 0xa3, 0x70, 0x37, 0xad, 0xf9, 0xa9, 0x01, 0xf7, 0xec, 0x7d, 0x4b, 0xa0, 0xc7, 0xa0,
+	0x24, 0xda, 0x27, 0xed, 0x7b, 0x0f, 0xc4, 0x51, 0xb9, 0xbe, 0x1d, 0x92, 0x1b, 0x3b, 0x33, 0x79,
+	0x0b, 0x0a, 0x20, 0x96, 0xe4, 0x03, 0x4f, 0xe5, 0x92, 0xea, 0xaa, 0xb8, 0x5f, 0xeb, 0x57, 0x3a,
+	0x4c, 0xeb, 0xf7, 0xc1, 0x50, 0x97, 0xd3, 0x89, 0xdb, 0x05, 0x3d, 0x05, 0x15, 0xc7, 0xa5, 0xa2,
+	0xe9, 0x0e, 0xe2, 0xf7, 0x67, 0xd3, 0xb1, 0xb2, 0x97, 0x62, 0xc4, 0x8d, 0xec, 0x03, 0x4e, 0x17,
+	0x20, 0x1b, 0x4a, 0x2d, 0x1a, 0x74, 0x74, 0xce, 0x38, 0x5c, 0x19, 0x25, 0x62, 0x20, 0xdd, 0xfc,
+	0x65, 0x1a, 0x74, 0xb0, 0x64, 0x8e, 0x5e, 0x85, 0x02, 0x0f, 0xf4, 0x9d, 0x7a, 0x04, 0x22, 0x40,
+	0x8b, 0x28, 0xac, 0x07, 0xb8, 0xc0, 0x03, 0x11, 0x3d, 0x2c, 0xef, 0xb3, 0x17, 0x0e, 0xe8, 0xb3,
+	0x69, 0xf4, 0x24, 0x8e, 0x9a, 0xb0, 0x96, 0x6f, 0x4b, 0xbb, 0xaa, 0xb3, 0xb4, 0x40, 0xee, 0xa9,
+	0xe7, 0x5e, 0x84, 0x21, 0x4b, 0xd9, 0x64, 0x48, 0xda, 0xe4, 0x19, 0xf9, 0x76, 0x32, 0x36, 0xc6,
+	0x23, 0x37, 0xf9, 0x16, 0x8c, 0x3a, 0xfa, 0x13, 0xb0, 0xf3, 0x32, 0x9f, 0xa8, 0x35, 0x58, 0x73,
+	0x43, 0x4f, 0xc2, 0x18, 0xf1, 0xad, 0x0d, 0x8f, 0x2c, 0x07, 0xed, 0xb6, 0xeb, 0xb7, 0xa7, 0x86,
+	0xe5, 0x5d, 0x97, 0xe4, 0xc3, 0xc5, 0x2c, 0x12, 0xe7, 0x69, 0xf7, 0xaa, 0x66, 0x47, 0x06, 0xa8,
+	0x66, 0x63, 0x37, 0xaf, 0xf4, 0x75, 0xf3, 0x6b, 0x50, 0xf5, 0x92, 0xa6, 0x8f, 0x4d, 0x81, 0xb4,
+	0xc6, 0x13, 0x83, 0x5a, 0x23, 0xed, 0x1b, 0xd3, 0x6a, 0x24, 0x85, 0x31, 0x9c, 0x95, 0x21, 0xcc,
+	0xe2, 0x05, 0x6d, 0x79, 0x4b, 0x4c, 0x55, 0xf3, 0x39, 0x66, 0x59, 0xc3, 0x71, 0x42, 0x61, 0xbe,
+	0x5b, 0x04, 0x94, 0xf3, 0x28, 0x91, 0xa9, 0xd8, 0x7f, 0x48, 0xb9, 0x12, 0xc2, 0x28, 0xa7, 0x56,
+	0xab, 0xe5, 0xda, 0x52, 0xab, 0x5b, 0x28, 0xe4, 0xe4, 0x87, 0x7c, 0xb5, 0xf8, 0x43, 0xbe, 0xda,
+	0x7a, 0x66, 0x75, 0x66, 0xc4, 0x96, 0x81, 0xe2, 0x9c, 0x04, 0xf4, 0x96, 0x01, 0x13, 0xa2, 0x3a,
+	0xc9, 0x92, 0xe8, 0xe1, 0xe0, 0x13, 0xb7, 0x2e, 0x16, 0x77, 0x71, 0x48, 0x07, 0x12, 0xdd, 0x18,
+	0xdc, 0x23, 0xcd, 0xfc, 0x93, 0x01, 0x93, 0x3d, 0x16, 0x89, 0x8e, 0x63, 0x3a, 0xeb, 0x41, 0x59,
+	0xd4, 0x1e, 0x71, 0xca, 0x5d, 0x3a, 0x94, 0xad, 0xd3, 0xaa, 0x27, 0xad, 0x93, 0x04, 0x8c, 0x61,
+	0x25, 0xc4, 0x3c, 0x0f, 0x63, 0xb9, 0x41, 0xf8, 0xfe, 0x6f, 0x87, 0xcc, 0xf7, 0xcb, 0x30, 0x11,
+	0xf3, 0x65, 0x6b, 0x51, 0xa7, 0x63, 0xd1, 0xe3, 0xe8, 0xad, 0xbf, 0x67, 0xc0, 0xc9, 0xac, 0x63,
+	0xba, 0xc9, 0x11, 0xd5, 0x0f, 0x75, 0x44, 0xca, 0x37, 0x4e, 0x6b, 0xd9, 0x27, 0x57, 0xf3, 0x22,
+	0x70, 0xb7, 0x4c, 0xf4, 0x0b, 0x03, 0xee, 0x57, 0x52, 0xf4, 0x17, 0x13, 0x5d, 0x2b, 0xb4, 0xa3,
+	0x1e, 0x85, 0x52, 0xff, 0xab, 0x95, 0xba, 0x7f, 0xfe, 0x26, 0xf2, 0xf0, 0x4d, 0xb5, 0x41, 0x3f,
+	0x35, 0xe0, 0x6e, 0x45, 0xd0, 0xad, 0x67, 0xe9, 0xc8, 0xf4, 0x3c, 0xa3, 0xf5, 0xbc, 0x7b, 0x7e,
+	0x2f, 0x41, 0x78, 0x6f, 0xf9, 0x88, 0x41, 0xa5, 0x13, 0xcf, 0xb1, 0x64, 0x69, 0x75, 0x00, 0x65,
+	0x7a, 0x07, 0x61, 0x69, 0x4d, 0x94, 0xe0, 0x70, 0x2a, 0xc7, 0x7c, 0x15, 0xee, 0x6a, 0x5a, 0x6d,
+	0xdd, 0x33, 0x2e, 0x11, 0x7e, 0x25, 0x14, 0x3f, 0x98, 0x1a, 0x33, 0xb7, 0x95, 0xdb, 0x17, 0xb3,
+	0x63, 0xe6, 0x36, 0xc1, 0x12, 0x83, 0x1e, 0x84, 0xb2, 0xe7, 0x76, 0x5c, 0xae, 0x5b, 0x80, 0x24,
+	0x9c, 0x96, 0x05, 0x10, 0x2b, 0x9c, 0x69, 0xc1, 0x68, 0x76, 0x48, 0x76, 0x3b, 0xde, 0xb5, 0x7e,
+	0x60, 0xc0, 0xb0, 0xee, 0xe8, 0x0e, 0x59, 0x65, 0xed, 0x3f, 0x7d, 0x4b, 0xcb, 0x85, 0xe2, 0x51,
+	0x96, 0x0b, 0xe6, 0xaf, 0x8b, 0x10, 0xbf, 0x09, 0x43, 0x8f, 0x66, 0x26, 0x7c, 0x6a, 0x0b, 0x53,
+	0xfb, 0x4f, 0xf7, 0xd0, 0xaa, 0x9e, 0x2d, 0x16, 0xf6, 0xb9, 0x6b, 0x22, 0xee, 0x7a, 0x35, 0xf5,
+	0x35, 0x75, 0xad, 0xe1, 0xf3, 0x2b, 0x74, 0x8d, 0x53, 0xd7, 0x6f, 0xab, 0x69, 0x6d, 0x66, 0x12,
+	0xf9, 0x7f, 0x30, 0x4c, 0x7c, 0x39, 0xb6, 0x94, 0x5b, 0x2d, 0xab, 0x89, 0xce, 0xa2, 0x02, 0xe1,
+	0x18, 0x87, 0xce, 0xc1, 0x88, 0x6b, 0x77, 0x42, 0x51, 0x95, 0xcb, 0xaa, 0xb9, 0xac, 0x06, 0x30,
+	0x8d, 0x85, 0x95, 0xa6, 0xac, 0xd4, 0x13, 0x6c, 0x4c, 0xb9, 0x10, 0xbf, 0xa1, 0xcc, 0x50, 0x0a,
+	0x18, 0x4e, 0xb0, 0x92, 0xb2, 0xad, 0x79, 0x0e, 0x65, 0x28, 0x97, 0x12, 0x9e, 0x1a, 0x8b, 0x2e,
+	0xea, 0x2f, 0x62, 0x74, 0xd7, 0x26, 0x8b, 0xac, 0x4a, 0xd7, 0x47, 0x2d, 0xf1, 0x9c, 0x3c, 0x47,
+	0x29, 0xb6, 0xc7, 0xa8, 0x2d, 0xb7, 0x37, 0x92, 0x6e, 0x6f, 0x4d, 0x81, 0x70, 0x8c, 0x43, 0x35,
+	0x00, 0x46, 0x6d, 0xbd, 0x6b, 0x59, 0x50, 0x95, 0xeb, 0xe3, 0xe2, 0x46, 0x5e, 0x4b, 0xa0, 0x38,
+	0x43, 0x61, 0x12, 0x98, 0xe8, 0xee, 0xab, 0x6e, 0x87, 0xcb, 0xbf, 0x5b, 0x82, 0xd3, 0x6b, 0x51,
+	0x28, 0x0c, 0xa5, 0xbe, 0xdb, 0x5b, 0x08, 0x3c, 0x4f, 0x3b, 0xf1, 0xed, 0x4f, 0x3c, 0xaf, 0x40,
+	0x85, 0x5c, 0x0f, 0x5d, 0x4a, 0x9c, 0xf9, 0xd8, 0xdf, 0xbe, 0x70, 0x6b, 0x22, 0xd6, 0xdd, 0x0e,
+	0x49, 0xb7, 0xb6, 0x18, 0x33, 0xc1, 0x29, 0x3f, 0x71, 0x16, 0xcc, 0xf5, 0x6d, 0x22, 0x48, 0x75,
+	0x90, 0x25, 0x0b, 0xd6, 0x62, 0x04, 0x4e, 0x69, 0x44, 0x33, 0xdc, 0x4a, 0xbe, 0x74, 0x94, 0x3e,
+	0x78, 0x80, 0x66, 0xb8, 0xfb, 0x8b, 0xc9, 0xf4, 0x04, 0x52, 0x18, 0xce, 0xc8, 0x41, 0x3f, 0x34,
+	0x60, 0xdc, 0xca, 0x7f, 0xac, 0xa8, 0x5e, 0xbb, 0xaf, 0x1c, 0x4c, 0x74, 0x9f, 0x0f, 0x2f, 0xeb,
+	0xf7, 0x68, 0x3d, 0xc6, 0xbb, 0xbe, 0x5a, 0xec, 0x12, 0x6e, 0x7e, 0x66, 0xc0, 0x7d, 0x7d, 0x3c,
+	0xe2, 0x18, 0x06, 0x58, 0x5e, 0x7e, 0x80, 0x35, 0x70, 0x89, 0xd6, 0x47, 0xf3, 0x3e, 0xa3, 0xac,
+	0x9f, 0x14, 0xe0, 0x81, 0x3e, 0x2b, 0x0e, 0x3c, 0xd4, 0x7a, 0x12, 0xc6, 0xe2, 0xdf, 0xd9, 0x30,
+	0x4c, 0x1b, 0x82, 0x2c, 0x12, 0xe7, 0x69, 0x63, 0x51, 0xf2, 0xc2, 0x2a, 0xf6, 0x8a, 0x52, 0x97,
+	0x56, 0x4c, 0x21, 0x3c, 0xdc, 0x0e, 0x3a, 0xa1, 0x47, 0x38, 0x51, 0x93, 0x86, 0x91, 0xd4, 0xc3,
+	0x17, 0x62, 0x04, 0x4e, 0x69, 0x44, 0xa2, 0x25, 0x94, 0x06, 0x54, 0x7a, 0x58, 0xe6, 0x4d, 0xd6,
+	0xa2, 0x00, 0x62, 0x85, 0x33, 0xff, 0x6e, 0xc0, 0x99, 0x3e, 0x87, 0x72, 0x6c, 0x95, 0xfa, 0x56,
+	0xbe, 0x52, 0x7f, 0xfe, 0x88, 0xdc, 0x60, 0xdf, 0x9a, 0xfd, 0x61, 0xa8, 0x66, 0x5e, 0x0f, 0xa2,
+	0x33, 0x50, 0x64, 0xbe, 0xdb, 0xfd, 0xb5, 0xf3, 0xda, 0x6a, 0x03, 0x0b, 0x78, 0x7d, 0xfd, 0xc3,
+	0x4f, 0xa6, 0x4f, 0x7c, 0xf4, 0xc9, 0xf4, 0x89, 0x8f, 0x3f, 0x99, 0x3e, 0xf1, 0xd6, 0xee, 0xb4,
+	0xf1, 0xe1, 0xee, 0xb4, 0xf1, 0xd1, 0xee, 0xb4, 0xf1, 0xf1, 0xee, 0xb4, 0xf1, 0x87, 0xdd, 0x69,
+	0xe3, 0xc7, 0x7f, 0x9c, 0x3e, 0xf1, 0x72, 0x6d, 0xb0, 0x7f, 0x03, 0xfb, 0x57, 0x00, 0x00, 0x00,
+	0xff, 0xff, 0x93, 0xce, 0x0d, 0x5e, 0x37, 0x36, 0x00, 0x00,
 }
 
 func (m *AddressGroup) Marshal() (dAtA []byte, err error) {
@@ -2154,6 +2311,41 @@ func (m *EgressGroupPatch) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *Entity) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *Entity) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Entity) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Pod != nil {
+		{
+			size, err := m.Pod.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintGenerated(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *ExternalEntityReference) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -2805,7 +2997,7 @@ func (m *NetworkPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
-func (m *NetworkPolicyList) Marshal() (dAtA []byte, err error) {
+func (m *NetworkPolicyEvaluation) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -2815,44 +3007,44 @@ func (m *NetworkPolicyList) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *NetworkPolicyList) MarshalTo(dAtA []byte) (int, error) {
+func (m *NetworkPolicyEvaluation) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *NetworkPolicyList) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *NetworkPolicyEvaluation) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
 	_ = l
-	if len(m.Items) > 0 {
-		for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- {
-			{
-				size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i])
-				if err != nil {
-					return 0, err
-				}
-				i -= size
-				i = encodeVarintGenerated(dAtA, i, uint64(size))
+	if m.Response != nil {
+		{
+			size, err := m.Response.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
 			}
-			i--
-			dAtA[i] = 0x12
+			i -= size
+			i = encodeVarintGenerated(dAtA, i, uint64(size))
 		}
+		i--
+		dAtA[i] = 0x12
 	}
-	{
-		size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i])
-		if err != nil {
-			return 0, err
+	if m.Request != nil {
+		{
+			size, err := m.Request.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintGenerated(dAtA, i, uint64(size))
 		}
-		i -= size
-		i = encodeVarintGenerated(dAtA, i, uint64(size))
+		i--
+		dAtA[i] = 0xa
 	}
-	i--
-	dAtA[i] = 0xa
 	return len(dAtA) - i, nil
 }
 
-func (m *NetworkPolicyNodeStatus) Marshal() (dAtA []byte, err error) {
+func (m *NetworkPolicyEvaluationRequest) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -2862,41 +3054,40 @@ func (m *NetworkPolicyNodeStatus) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *NetworkPolicyNodeStatus) MarshalTo(dAtA []byte) (int, error) {
+func (m *NetworkPolicyEvaluationRequest) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *NetworkPolicyNodeStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *NetworkPolicyEvaluationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
 	_ = l
-	i -= len(m.Message)
-	copy(dAtA[i:], m.Message)
-	i = encodeVarintGenerated(dAtA, i, uint64(len(m.Message)))
-	i--
-	dAtA[i] = 0x22
-	i--
-	if m.RealizationFailure {
-		dAtA[i] = 1
-	} else {
-		dAtA[i] = 0
+	{
+		size, err := m.Destination.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenerated(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x18
-	i = encodeVarintGenerated(dAtA, i, uint64(m.Generation))
-	i--
-	dAtA[i] = 0x10
-	i -= len(m.NodeName)
-	copy(dAtA[i:], m.NodeName)
-	i = encodeVarintGenerated(dAtA, i, uint64(len(m.NodeName)))
+	dAtA[i] = 0x12
+	{
+		size, err := m.Source.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenerated(dAtA, i, uint64(size))
+	}
 	i--
 	dAtA[i] = 0xa
 	return len(dAtA) - i, nil
 }
 
-func (m *NetworkPolicyPeer) Marshal() (dAtA []byte, err error) {
+func (m *NetworkPolicyEvaluationResponse) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -2906,21 +3097,158 @@ func (m *NetworkPolicyPeer) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *NetworkPolicyPeer) MarshalTo(dAtA []byte) (int, error) {
+func (m *NetworkPolicyEvaluationResponse) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *NetworkPolicyPeer) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *NetworkPolicyEvaluationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
 	_ = l
-	if len(m.LabelIdentities) > 0 {
-		for iNdEx := len(m.LabelIdentities) - 1; iNdEx >= 0; iNdEx-- {
-			i = encodeVarintGenerated(dAtA, i, uint64(m.LabelIdentities[iNdEx]))
-			i--
-			dAtA[i] = 0x28
+	{
+		size, err := m.Rule.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenerated(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	i = encodeVarintGenerated(dAtA, i, uint64(m.RuleIndex))
+	i--
+	dAtA[i] = 0x10
+	{
+		size, err := m.NetworkPolicy.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenerated(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *NetworkPolicyList) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *NetworkPolicyList) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *NetworkPolicyList) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Items) > 0 {
+		for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintGenerated(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x12
+		}
+	}
+	{
+		size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenerated(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *NetworkPolicyNodeStatus) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *NetworkPolicyNodeStatus) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *NetworkPolicyNodeStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	i -= len(m.Message)
+	copy(dAtA[i:], m.Message)
+	i = encodeVarintGenerated(dAtA, i, uint64(len(m.Message)))
+	i--
+	dAtA[i] = 0x22
+	i--
+	if m.RealizationFailure {
+		dAtA[i] = 1
+	} else {
+		dAtA[i] = 0
+	}
+	i--
+	dAtA[i] = 0x18
+	i = encodeVarintGenerated(dAtA, i, uint64(m.Generation))
+	i--
+	dAtA[i] = 0x10
+	i -= len(m.NodeName)
+	copy(dAtA[i:], m.NodeName)
+	i = encodeVarintGenerated(dAtA, i, uint64(len(m.NodeName)))
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *NetworkPolicyPeer) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *NetworkPolicyPeer) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *NetworkPolicyPeer) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.LabelIdentities) > 0 {
+		for iNdEx := len(m.LabelIdentities) - 1; iNdEx >= 0; iNdEx-- {
+			i = encodeVarintGenerated(dAtA, i, uint64(m.LabelIdentities[iNdEx]))
+			i--
+			dAtA[i] = 0x28
 		}
 	}
 	if len(m.ToServices) > 0 {
@@ -3411,6 +3739,46 @@ func (m *PodReference) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *RuleRef) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *RuleRef) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *RuleRef) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Action != nil {
+		i -= len(*m.Action)
+		copy(dAtA[i:], *m.Action)
+		i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Action)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	i -= len(m.Name)
+	copy(dAtA[i:], m.Name)
+	i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name)))
+	i--
+	dAtA[i] = 0x12
+	i -= len(m.Direction)
+	copy(dAtA[i:], m.Direction)
+	i = encodeVarintGenerated(dAtA, i, uint64(len(m.Direction)))
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
 func (m *Service) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -4011,6 +4379,19 @@ func (m *EgressGroupPatch) Size() (n int) {
 	return n
 }
 
+func (m *Entity) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Pod != nil {
+		l = m.Pod.Size()
+		n += 1 + l + sovGenerated(uint64(l))
+	}
+	return n
+}
+
 func (m *ExternalEntityReference) Size() (n int) {
 	if m == nil {
 		return 0
@@ -4263,6 +4644,50 @@ func (m *NetworkPolicy) Size() (n int) {
 	return n
 }
 
+func (m *NetworkPolicyEvaluation) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Request != nil {
+		l = m.Request.Size()
+		n += 1 + l + sovGenerated(uint64(l))
+	}
+	if m.Response != nil {
+		l = m.Response.Size()
+		n += 1 + l + sovGenerated(uint64(l))
+	}
+	return n
+}
+
+func (m *NetworkPolicyEvaluationRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Source.Size()
+	n += 1 + l + sovGenerated(uint64(l))
+	l = m.Destination.Size()
+	n += 1 + l + sovGenerated(uint64(l))
+	return n
+}
+
+func (m *NetworkPolicyEvaluationResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.NetworkPolicy.Size()
+	n += 1 + l + sovGenerated(uint64(l))
+	n += 1 + sovGenerated(uint64(m.RuleIndex))
+	l = m.Rule.Size()
+	n += 1 + l + sovGenerated(uint64(l))
+	return n
+}
+
 func (m *NetworkPolicyList) Size() (n int) {
 	if m == nil {
 		return 0
@@ -4499,6 +4924,23 @@ func (m *PodReference) Size() (n int) {
 	return n
 }
 
+func (m *RuleRef) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Direction)
+	n += 1 + l + sovGenerated(uint64(l))
+	l = len(m.Name)
+	n += 1 + l + sovGenerated(uint64(l))
+	if m.Action != nil {
+		l = len(*m.Action)
+		n += 1 + l + sovGenerated(uint64(l))
+	}
+	return n
+}
+
 func (m *Service) Size() (n int) {
 	if m == nil {
 		return 0
@@ -4857,6 +5299,16 @@ func (this *EgressGroupPatch) String() string {
 	}, "")
 	return s
 }
+func (this *Entity) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&Entity{`,
+		`Pod:` + strings.Replace(this.Pod.String(), "PodReference", "PodReference", 1) + `,`,
+		`}`,
+	}, "")
+	return s
+}
 func (this *ExternalEntityReference) String() string {
 	if this == nil {
 		return "nil"
@@ -5055,29 +5507,63 @@ func (this *NetworkPolicy) String() string {
 	}, "")
 	return s
 }
-func (this *NetworkPolicyList) String() string {
+func (this *NetworkPolicyEvaluation) String() string {
 	if this == nil {
 		return "nil"
 	}
-	repeatedStringForItems := "[]NetworkPolicy{"
-	for _, f := range this.Items {
-		repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "NetworkPolicy", "NetworkPolicy", 1), `&`, ``, 1) + ","
-	}
-	repeatedStringForItems += "}"
-	s := strings.Join([]string{`&NetworkPolicyList{`,
-		`ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`,
-		`Items:` + repeatedStringForItems + `,`,
+	s := strings.Join([]string{`&NetworkPolicyEvaluation{`,
+		`Request:` + strings.Replace(this.Request.String(), "NetworkPolicyEvaluationRequest", "NetworkPolicyEvaluationRequest", 1) + `,`,
+		`Response:` + strings.Replace(this.Response.String(), "NetworkPolicyEvaluationResponse", "NetworkPolicyEvaluationResponse", 1) + `,`,
 		`}`,
 	}, "")
 	return s
 }
-func (this *NetworkPolicyNodeStatus) String() string {
+func (this *NetworkPolicyEvaluationRequest) String() string {
 	if this == nil {
 		return "nil"
 	}
-	s := strings.Join([]string{`&NetworkPolicyNodeStatus{`,
-		`NodeName:` + fmt.Sprintf("%v", this.NodeName) + `,`,
-		`Generation:` + fmt.Sprintf("%v", this.Generation) + `,`,
+	s := strings.Join([]string{`&NetworkPolicyEvaluationRequest{`,
+		`Source:` + strings.Replace(strings.Replace(this.Source.String(), "Entity", "Entity", 1), `&`, ``, 1) + `,`,
+		`Destination:` + strings.Replace(strings.Replace(this.Destination.String(), "Entity", "Entity", 1), `&`, ``, 1) + `,`,
+		`}`,
+	}, "")
+	return s
+}
+func (this *NetworkPolicyEvaluationResponse) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&NetworkPolicyEvaluationResponse{`,
+		`NetworkPolicy:` + strings.Replace(strings.Replace(this.NetworkPolicy.String(), "NetworkPolicyReference", "NetworkPolicyReference", 1), `&`, ``, 1) + `,`,
+		`RuleIndex:` + fmt.Sprintf("%v", this.RuleIndex) + `,`,
+		`Rule:` + strings.Replace(strings.Replace(this.Rule.String(), "RuleRef", "RuleRef", 1), `&`, ``, 1) + `,`,
+		`}`,
+	}, "")
+	return s
+}
+func (this *NetworkPolicyList) String() string {
+	if this == nil {
+		return "nil"
+	}
+	repeatedStringForItems := "[]NetworkPolicy{"
+	for _, f := range this.Items {
+		repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "NetworkPolicy", "NetworkPolicy", 1), `&`, ``, 1) + ","
+	}
+	repeatedStringForItems += "}"
+	s := strings.Join([]string{`&NetworkPolicyList{`,
+		`ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`,
+		`Items:` + repeatedStringForItems + `,`,
+		`}`,
+	}, "")
+	return s
+}
+func (this *NetworkPolicyNodeStatus) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&NetworkPolicyNodeStatus{`,
+		`NodeName:` + fmt.Sprintf("%v", this.NodeName) + `,`,
+		`Generation:` + fmt.Sprintf("%v", this.Generation) + `,`,
 		`RealizationFailure:` + fmt.Sprintf("%v", this.RealizationFailure) + `,`,
 		`Message:` + fmt.Sprintf("%v", this.Message) + `,`,
 		`}`,
@@ -5250,6 +5736,18 @@ func (this *PodReference) String() string {
 	}, "")
 	return s
 }
+func (this *RuleRef) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&RuleRef{`,
+		`Direction:` + fmt.Sprintf("%v", this.Direction) + `,`,
+		`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
+		`Action:` + valueToStringGenerated(this.Action) + `,`,
+		`}`,
+	}, "")
+	return s
+}
 func (this *Service) String() string {
 	if this == nil {
 		return "nil"
@@ -7066,6 +7564,92 @@ func (m *EgressGroupPatch) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *Entity) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowGenerated
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Entity: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Entity: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Pod", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Pod == nil {
+				m.Pod = &PodReference{}
+			}
+			if err := m.Pod.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenerated(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *ExternalEntityReference) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
@@ -8982,7 +9566,7 @@ func (m *NetworkPolicy) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *NetworkPolicyList) Unmarshal(dAtA []byte) error {
+func (m *NetworkPolicyEvaluation) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -9005,15 +9589,15 @@ func (m *NetworkPolicyList) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: NetworkPolicyList: wiretype end group for non-group")
+			return fmt.Errorf("proto: NetworkPolicyEvaluation: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: NetworkPolicyList: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: NetworkPolicyEvaluation: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType)
 			}
 			var msglen int
 			for shift := uint(0); ; shift += 7 {
@@ -9040,13 +9624,16 @@ func (m *NetworkPolicyList) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if m.Request == nil {
+				m.Request = &NetworkPolicyEvaluationRequest{}
+			}
+			if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
 		case 2:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Response", wireType)
 			}
 			var msglen int
 			for shift := uint(0); ; shift += 7 {
@@ -9073,8 +9660,10 @@ func (m *NetworkPolicyList) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Items = append(m.Items, NetworkPolicy{})
-			if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if m.Response == nil {
+				m.Response = &NetworkPolicyEvaluationResponse{}
+			}
+			if err := m.Response.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
@@ -9099,7 +9688,7 @@ func (m *NetworkPolicyList) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *NetworkPolicyNodeStatus) Unmarshal(dAtA []byte) error {
+func (m *NetworkPolicyEvaluationRequest) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -9122,17 +9711,17 @@ func (m *NetworkPolicyNodeStatus) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: NetworkPolicyNodeStatus: wiretype end group for non-group")
+			return fmt.Errorf("proto: NetworkPolicyEvaluationRequest: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: NetworkPolicyNodeStatus: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: NetworkPolicyEvaluationRequest: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field NodeName", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
 			}
-			var stringLen uint64
+			var msglen int
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenerated
@@ -9142,68 +9731,30 @@ func (m *NetworkPolicyNodeStatus) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				msglen |= int(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
+			if msglen < 0 {
 				return ErrInvalidLengthGenerated
 			}
-			postIndex := iNdEx + intStringLen
+			postIndex := iNdEx + msglen
 			if postIndex < 0 {
 				return ErrInvalidLengthGenerated
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.NodeName = string(dAtA[iNdEx:postIndex])
+			if err := m.Source.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
 			iNdEx = postIndex
 		case 2:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Generation", wireType)
-			}
-			m.Generation = 0
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowGenerated
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				m.Generation |= int64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-		case 3:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field RealizationFailure", wireType)
-			}
-			var v int
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowGenerated
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				v |= int(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			m.RealizationFailure = bool(v != 0)
-		case 4:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType)
 			}
-			var stringLen uint64
+			var msglen int
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenerated
@@ -9213,23 +9764,24 @@ func (m *NetworkPolicyNodeStatus) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				msglen |= int(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
+			if msglen < 0 {
 				return ErrInvalidLengthGenerated
 			}
-			postIndex := iNdEx + intStringLen
+			postIndex := iNdEx + msglen
 			if postIndex < 0 {
 				return ErrInvalidLengthGenerated
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Message = string(dAtA[iNdEx:postIndex])
+			if err := m.Destination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
 			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
@@ -9252,7 +9804,7 @@ func (m *NetworkPolicyNodeStatus) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *NetworkPolicyPeer) Unmarshal(dAtA []byte) error {
+func (m *NetworkPolicyEvaluationResponse) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -9275,17 +9827,17 @@ func (m *NetworkPolicyPeer) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: NetworkPolicyPeer: wiretype end group for non-group")
+			return fmt.Errorf("proto: NetworkPolicyEvaluationResponse: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: NetworkPolicyPeer: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: NetworkPolicyEvaluationResponse: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field AddressGroups", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field NetworkPolicy", wireType)
 			}
-			var stringLen uint64
+			var msglen int
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenerated
@@ -9295,29 +9847,30 @@ func (m *NetworkPolicyPeer) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				msglen |= int(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
+			if msglen < 0 {
 				return ErrInvalidLengthGenerated
 			}
-			postIndex := iNdEx + intStringLen
+			postIndex := iNdEx + msglen
 			if postIndex < 0 {
 				return ErrInvalidLengthGenerated
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.AddressGroups = append(m.AddressGroups, string(dAtA[iNdEx:postIndex]))
+			if err := m.NetworkPolicy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
 			iNdEx = postIndex
 		case 2:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field IPBlocks", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field RuleIndex", wireType)
 			}
-			var msglen int
+			m.RuleIndex = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenerated
@@ -9327,31 +9880,16 @@ func (m *NetworkPolicyPeer) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				msglen |= int(b&0x7F) << shift
+				m.RuleIndex |= int32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if msglen < 0 {
-				return ErrInvalidLengthGenerated
-			}
-			postIndex := iNdEx + msglen
-			if postIndex < 0 {
-				return ErrInvalidLengthGenerated
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.IPBlocks = append(m.IPBlocks, IPBlock{})
-			if err := m.IPBlocks[len(m.IPBlocks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
-				return err
-			}
-			iNdEx = postIndex
 		case 3:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field FQDNs", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Rule", wireType)
 			}
-			var stringLen uint64
+			var msglen int
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenerated
@@ -9361,24 +9899,443 @@ func (m *NetworkPolicyPeer) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				msglen |= int(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
+			if msglen < 0 {
 				return ErrInvalidLengthGenerated
 			}
-			postIndex := iNdEx + intStringLen
+			postIndex := iNdEx + msglen
 			if postIndex < 0 {
 				return ErrInvalidLengthGenerated
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.FQDNs = append(m.FQDNs, string(dAtA[iNdEx:postIndex]))
-			iNdEx = postIndex
+			if err := m.Rule.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenerated(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *NetworkPolicyList) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowGenerated
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: NetworkPolicyList: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: NetworkPolicyList: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Items = append(m.Items, NetworkPolicy{})
+			if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenerated(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *NetworkPolicyNodeStatus) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowGenerated
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: NetworkPolicyNodeStatus: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: NetworkPolicyNodeStatus: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field NodeName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.NodeName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Generation", wireType)
+			}
+			m.Generation = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Generation |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field RealizationFailure", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				v |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.RealizationFailure = bool(v != 0)
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Message = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenerated(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *NetworkPolicyPeer) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowGenerated
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: NetworkPolicyPeer: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: NetworkPolicyPeer: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AddressGroups", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AddressGroups = append(m.AddressGroups, string(dAtA[iNdEx:postIndex]))
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field IPBlocks", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.IPBlocks = append(m.IPBlocks, IPBlock{})
+			if err := m.IPBlocks[len(m.IPBlocks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FQDNs", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.FQDNs = append(m.FQDNs, string(dAtA[iNdEx:postIndex]))
+			iNdEx = postIndex
 		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ToServices", wireType)
@@ -10842,6 +11799,153 @@ func (m *PodReference) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *RuleRef) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowGenerated
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: RuleRef: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: RuleRef: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Direction", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Direction = Direction(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := antrea_io_antrea_pkg_apis_crd_v1beta1.RuleAction(dAtA[iNdEx:postIndex])
+			m.Action = &s
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenerated(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *Service) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/pkg/apis/controlplane/v1beta2/generated.proto b/pkg/apis/controlplane/v1beta2/generated.proto
index e6e89d96d..eabbf8046 100644
--- a/pkg/apis/controlplane/v1beta2/generated.proto
+++ b/pkg/apis/controlplane/v1beta2/generated.proto
@@ -1,4 +1,4 @@
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -130,6 +130,11 @@ message EgressGroupPatch {
   repeated GroupMember removedGroupMembers = 3;
 }
 
+// Entity contains Namespace and Pod name as a request parameter.
+message Entity {
+  optional PodReference pod = 1;
+}
+
 // ExternalEntityReference represents a ExternalEntity Reference.
 message ExternalEntityReference {
   // The name of this ExternalEntity.
@@ -289,6 +294,31 @@ message NetworkPolicy {
   optional NetworkPolicyReference sourceRef = 6;
 }
 
+// NetworkPolicyEvaluation contains the request and response for a NetworkPolicy evaluation.
+message NetworkPolicyEvaluation {
+  optional NetworkPolicyEvaluationRequest request = 1;
+
+  optional NetworkPolicyEvaluationResponse response = 2;
+}
+
+// NetworkPolicyEvaluationRequest is the request body of NetworkPolicy evaluation.
+message NetworkPolicyEvaluationRequest {
+  optional Entity source = 1;
+
+  optional Entity destination = 2;
+}
+
+// NetworkPolicyEvaluationResponse is the response of NetworkPolicy evaluation.
+message NetworkPolicyEvaluationResponse {
+  // The reference of the effective NetworkPolicy.
+  optional NetworkPolicyReference networkPolicy = 1;
+
+  optional int32 ruleIndex = 2;
+
+  // The content of the effective rule.
+  optional RuleRef rule = 3;
+}
+
 // NetworkPolicyList is a list of NetworkPolicy objects.
 message NetworkPolicyList {
   optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
@@ -449,6 +479,15 @@ message PodReference {
   optional string namespace = 2;
 }
 
+// RuleRef contains basic information for the rule.
+message RuleRef {
+  optional string direction = 1;
+
+  optional string name = 2;
+
+  optional string action = 3;
+}
+
 // Service describes a port to allow traffic on.
 message Service {
   // The protocol (TCP, UDP, SCTP, or ICMP) which traffic must match. If not specified, this
diff --git a/pkg/apis/controlplane/v1beta2/register.go b/pkg/apis/controlplane/v1beta2/register.go
index e90252dfa..df95e9770 100644
--- a/pkg/apis/controlplane/v1beta2/register.go
+++ b/pkg/apis/controlplane/v1beta2/register.go
@@ -44,6 +44,11 @@ var (
 		Version:  SchemeGroupVersion.Version,
 		Resource: "groups",
 	}
+	NetworkPolicyEvaluationVersionResource = schema.GroupVersionResource{
+		Group:    SchemeGroupVersion.Group,
+		Version:  SchemeGroupVersion.Version,
+		Resource: "networkpolicyevaluation",
+	}
 )
 
 // Resource takes an unqualified resource and returns a Group qualified GroupResource.
@@ -69,6 +74,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
 		&NetworkPolicy{},
 		&NetworkPolicyList{},
 		&NetworkPolicyStatus{},
+		&NetworkPolicyEvaluation{},
 		&NodeStatsSummary{},
 		&ClusterGroupMembers{},
 		&GroupMembers{},
diff --git a/pkg/apis/controlplane/v1beta2/types.go b/pkg/apis/controlplane/v1beta2/types.go
index 8eae3c3cc..c5941afca 100644
--- a/pkg/apis/controlplane/v1beta2/types.go
+++ b/pkg/apis/controlplane/v1beta2/types.go
@@ -450,6 +450,45 @@ type NetworkPolicyNodeStatus struct {
 	Message string `json:"message,omitempty" protobuf:"bytes,4,opt,name=message"`
 }
 
+// +genclient
+// +genclient:nonNamespaced
+// +genclient:onlyVerbs=create
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// NetworkPolicyEvaluation contains the request and response for a NetworkPolicy evaluation.
+type NetworkPolicyEvaluation struct {
+	metav1.TypeMeta `json:",inline"`
+	Request         *NetworkPolicyEvaluationRequest  `json:"request,omitempty" protobuf:"bytes,1,opt,name=request"`
+	Response        *NetworkPolicyEvaluationResponse `json:"response,omitempty" protobuf:"bytes,2,opt,name=response"`
+}
+
+// Entity contains Namespace and Pod name as a request parameter.
+type Entity struct {
+	Pod *PodReference `json:"pod,omitempty" protobuf:"bytes,1,opt,name=pod"`
+}
+
+// NetworkPolicyEvaluationRequest is the request body of NetworkPolicy evaluation.
+type NetworkPolicyEvaluationRequest struct {
+	Source      Entity `json:"source,omitempty" protobuf:"bytes,1,opt,name=source"`
+	Destination Entity `json:"destination,omitempty" protobuf:"bytes,2,opt,name=destination"`
+}
+
+// RuleRef contains basic information for the rule.
+type RuleRef struct {
+	Direction Direction              `json:"direction,omitempty" protobuf:"bytes,1,opt,name=direction"`
+	Name      string                 `json:"name,omitempty" protobuf:"bytes,2,opt,name=name"`
+	Action    *crdv1beta1.RuleAction `json:"action,omitempty" protobuf:"bytes,3,opt,name=action,casttype=antrea.io/antrea/pkg/apis/security/v1beta1.RuleAction"`
+}
+
+// NetworkPolicyEvaluationResponse is the response of NetworkPolicy evaluation.
+type NetworkPolicyEvaluationResponse struct {
+	// The reference of the effective NetworkPolicy.
+	NetworkPolicy NetworkPolicyReference `json:"networkPolicy,omitempty" protobuf:"bytes,1,opt,name=networkPolicy"`
+	RuleIndex     int32                  `json:"ruleIndex,omitempty" protobuf:"varint,2,opt,name=ruleIndex"`
+	// The content of the effective rule.
+	Rule RuleRef `json:"rule,omitempty" protobuf:"bytes,3,opt,name=rule"`
+}
+
 type GroupReference struct {
 	// Namespace of the Group. Empty for ClusterGroup.
 	Namespace string `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"`
diff --git a/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go b/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go
index 09df61a86..3c4893358 100644
--- a/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go
+++ b/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go
@@ -1,7 +1,7 @@
 //go:build !ignore_autogenerated
 // +build !ignore_autogenerated
 
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -169,6 +169,16 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*Entity)(nil), (*controlplane.Entity)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_Entity_To_controlplane_Entity(a.(*Entity), b.(*controlplane.Entity), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*controlplane.Entity)(nil), (*Entity)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_controlplane_Entity_To_v1beta2_Entity(a.(*controlplane.Entity), b.(*Entity), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*ExternalEntityReference)(nil), (*controlplane.ExternalEntityReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta2_ExternalEntityReference_To_controlplane_ExternalEntityReference(a.(*ExternalEntityReference), b.(*controlplane.ExternalEntityReference), scope)
 	}); err != nil {
@@ -299,6 +309,36 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*NetworkPolicyEvaluation)(nil), (*controlplane.NetworkPolicyEvaluation)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_NetworkPolicyEvaluation_To_controlplane_NetworkPolicyEvaluation(a.(*NetworkPolicyEvaluation), b.(*controlplane.NetworkPolicyEvaluation), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*controlplane.NetworkPolicyEvaluation)(nil), (*NetworkPolicyEvaluation)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_controlplane_NetworkPolicyEvaluation_To_v1beta2_NetworkPolicyEvaluation(a.(*controlplane.NetworkPolicyEvaluation), b.(*NetworkPolicyEvaluation), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*NetworkPolicyEvaluationRequest)(nil), (*controlplane.NetworkPolicyEvaluationRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_NetworkPolicyEvaluationRequest_To_controlplane_NetworkPolicyEvaluationRequest(a.(*NetworkPolicyEvaluationRequest), b.(*controlplane.NetworkPolicyEvaluationRequest), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*controlplane.NetworkPolicyEvaluationRequest)(nil), (*NetworkPolicyEvaluationRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_controlplane_NetworkPolicyEvaluationRequest_To_v1beta2_NetworkPolicyEvaluationRequest(a.(*controlplane.NetworkPolicyEvaluationRequest), b.(*NetworkPolicyEvaluationRequest), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*NetworkPolicyEvaluationResponse)(nil), (*controlplane.NetworkPolicyEvaluationResponse)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_NetworkPolicyEvaluationResponse_To_controlplane_NetworkPolicyEvaluationResponse(a.(*NetworkPolicyEvaluationResponse), b.(*controlplane.NetworkPolicyEvaluationResponse), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*controlplane.NetworkPolicyEvaluationResponse)(nil), (*NetworkPolicyEvaluationResponse)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_controlplane_NetworkPolicyEvaluationResponse_To_v1beta2_NetworkPolicyEvaluationResponse(a.(*controlplane.NetworkPolicyEvaluationResponse), b.(*NetworkPolicyEvaluationResponse), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*NetworkPolicyList)(nil), (*controlplane.NetworkPolicyList)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta2_NetworkPolicyList_To_controlplane_NetworkPolicyList(a.(*NetworkPolicyList), b.(*controlplane.NetworkPolicyList), scope)
 	}); err != nil {
@@ -409,6 +449,16 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*RuleRef)(nil), (*controlplane.RuleRef)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_RuleRef_To_controlplane_RuleRef(a.(*RuleRef), b.(*controlplane.RuleRef), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*controlplane.RuleRef)(nil), (*RuleRef)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_controlplane_RuleRef_To_v1beta2_RuleRef(a.(*controlplane.RuleRef), b.(*RuleRef), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*Service)(nil), (*controlplane.Service)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta2_Service_To_controlplane_Service(a.(*Service), b.(*controlplane.Service), scope)
 	}); err != nil {
@@ -787,6 +837,26 @@ func Convert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch(in *contr
 	return autoConvert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch(in, out, s)
 }
 
+func autoConvert_v1beta2_Entity_To_controlplane_Entity(in *Entity, out *controlplane.Entity, s conversion.Scope) error {
+	out.Pod = (*controlplane.PodReference)(unsafe.Pointer(in.Pod))
+	return nil
+}
+
+// Convert_v1beta2_Entity_To_controlplane_Entity is an autogenerated conversion function.
+func Convert_v1beta2_Entity_To_controlplane_Entity(in *Entity, out *controlplane.Entity, s conversion.Scope) error {
+	return autoConvert_v1beta2_Entity_To_controlplane_Entity(in, out, s)
+}
+
+func autoConvert_controlplane_Entity_To_v1beta2_Entity(in *controlplane.Entity, out *Entity, s conversion.Scope) error {
+	out.Pod = (*PodReference)(unsafe.Pointer(in.Pod))
+	return nil
+}
+
+// Convert_controlplane_Entity_To_v1beta2_Entity is an autogenerated conversion function.
+func Convert_controlplane_Entity_To_v1beta2_Entity(in *controlplane.Entity, out *Entity, s conversion.Scope) error {
+	return autoConvert_controlplane_Entity_To_v1beta2_Entity(in, out, s)
+}
+
 func autoConvert_v1beta2_ExternalEntityReference_To_controlplane_ExternalEntityReference(in *ExternalEntityReference, out *controlplane.ExternalEntityReference, s conversion.Scope) error {
 	out.Name = in.Name
 	out.Namespace = in.Namespace
@@ -1127,6 +1197,90 @@ func Convert_controlplane_NetworkPolicy_To_v1beta2_NetworkPolicy(in *controlplan
 	return autoConvert_controlplane_NetworkPolicy_To_v1beta2_NetworkPolicy(in, out, s)
 }
 
+func autoConvert_v1beta2_NetworkPolicyEvaluation_To_controlplane_NetworkPolicyEvaluation(in *NetworkPolicyEvaluation, out *controlplane.NetworkPolicyEvaluation, s conversion.Scope) error {
+	out.Request = (*controlplane.NetworkPolicyEvaluationRequest)(unsafe.Pointer(in.Request))
+	out.Response = (*controlplane.NetworkPolicyEvaluationResponse)(unsafe.Pointer(in.Response))
+	return nil
+}
+
+// Convert_v1beta2_NetworkPolicyEvaluation_To_controlplane_NetworkPolicyEvaluation is an autogenerated conversion function.
+func Convert_v1beta2_NetworkPolicyEvaluation_To_controlplane_NetworkPolicyEvaluation(in *NetworkPolicyEvaluation, out *controlplane.NetworkPolicyEvaluation, s conversion.Scope) error {
+	return autoConvert_v1beta2_NetworkPolicyEvaluation_To_controlplane_NetworkPolicyEvaluation(in, out, s)
+}
+
+func autoConvert_controlplane_NetworkPolicyEvaluation_To_v1beta2_NetworkPolicyEvaluation(in *controlplane.NetworkPolicyEvaluation, out *NetworkPolicyEvaluation, s conversion.Scope) error {
+	out.Request = (*NetworkPolicyEvaluationRequest)(unsafe.Pointer(in.Request))
+	out.Response = (*NetworkPolicyEvaluationResponse)(unsafe.Pointer(in.Response))
+	return nil
+}
+
+// Convert_controlplane_NetworkPolicyEvaluation_To_v1beta2_NetworkPolicyEvaluation is an autogenerated conversion function.
+func Convert_controlplane_NetworkPolicyEvaluation_To_v1beta2_NetworkPolicyEvaluation(in *controlplane.NetworkPolicyEvaluation, out *NetworkPolicyEvaluation, s conversion.Scope) error {
+	return autoConvert_controlplane_NetworkPolicyEvaluation_To_v1beta2_NetworkPolicyEvaluation(in, out, s)
+}
+
+func autoConvert_v1beta2_NetworkPolicyEvaluationRequest_To_controlplane_NetworkPolicyEvaluationRequest(in *NetworkPolicyEvaluationRequest, out *controlplane.NetworkPolicyEvaluationRequest, s conversion.Scope) error {
+	if err := Convert_v1beta2_Entity_To_controlplane_Entity(&in.Source, &out.Source, s); err != nil {
+		return err
+	}
+	if err := Convert_v1beta2_Entity_To_controlplane_Entity(&in.Destination, &out.Destination, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_v1beta2_NetworkPolicyEvaluationRequest_To_controlplane_NetworkPolicyEvaluationRequest is an autogenerated conversion function.
+func Convert_v1beta2_NetworkPolicyEvaluationRequest_To_controlplane_NetworkPolicyEvaluationRequest(in *NetworkPolicyEvaluationRequest, out *controlplane.NetworkPolicyEvaluationRequest, s conversion.Scope) error {
+	return autoConvert_v1beta2_NetworkPolicyEvaluationRequest_To_controlplane_NetworkPolicyEvaluationRequest(in, out, s)
+}
+
+func autoConvert_controlplane_NetworkPolicyEvaluationRequest_To_v1beta2_NetworkPolicyEvaluationRequest(in *controlplane.NetworkPolicyEvaluationRequest, out *NetworkPolicyEvaluationRequest, s conversion.Scope) error {
+	if err := Convert_controlplane_Entity_To_v1beta2_Entity(&in.Source, &out.Source, s); err != nil {
+		return err
+	}
+	if err := Convert_controlplane_Entity_To_v1beta2_Entity(&in.Destination, &out.Destination, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_controlplane_NetworkPolicyEvaluationRequest_To_v1beta2_NetworkPolicyEvaluationRequest is an autogenerated conversion function.
+func Convert_controlplane_NetworkPolicyEvaluationRequest_To_v1beta2_NetworkPolicyEvaluationRequest(in *controlplane.NetworkPolicyEvaluationRequest, out *NetworkPolicyEvaluationRequest, s conversion.Scope) error {
+	return autoConvert_controlplane_NetworkPolicyEvaluationRequest_To_v1beta2_NetworkPolicyEvaluationRequest(in, out, s)
+}
+
+func autoConvert_v1beta2_NetworkPolicyEvaluationResponse_To_controlplane_NetworkPolicyEvaluationResponse(in *NetworkPolicyEvaluationResponse, out *controlplane.NetworkPolicyEvaluationResponse, s conversion.Scope) error {
+	if err := Convert_v1beta2_NetworkPolicyReference_To_controlplane_NetworkPolicyReference(&in.NetworkPolicy, &out.NetworkPolicy, s); err != nil {
+		return err
+	}
+	out.RuleIndex = in.RuleIndex
+	if err := Convert_v1beta2_RuleRef_To_controlplane_RuleRef(&in.Rule, &out.Rule, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_v1beta2_NetworkPolicyEvaluationResponse_To_controlplane_NetworkPolicyEvaluationResponse is an autogenerated conversion function.
+func Convert_v1beta2_NetworkPolicyEvaluationResponse_To_controlplane_NetworkPolicyEvaluationResponse(in *NetworkPolicyEvaluationResponse, out *controlplane.NetworkPolicyEvaluationResponse, s conversion.Scope) error {
+	return autoConvert_v1beta2_NetworkPolicyEvaluationResponse_To_controlplane_NetworkPolicyEvaluationResponse(in, out, s)
+}
+
+func autoConvert_controlplane_NetworkPolicyEvaluationResponse_To_v1beta2_NetworkPolicyEvaluationResponse(in *controlplane.NetworkPolicyEvaluationResponse, out *NetworkPolicyEvaluationResponse, s conversion.Scope) error {
+	if err := Convert_controlplane_NetworkPolicyReference_To_v1beta2_NetworkPolicyReference(&in.NetworkPolicy, &out.NetworkPolicy, s); err != nil {
+		return err
+	}
+	out.RuleIndex = in.RuleIndex
+	if err := Convert_controlplane_RuleRef_To_v1beta2_RuleRef(&in.Rule, &out.Rule, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_controlplane_NetworkPolicyEvaluationResponse_To_v1beta2_NetworkPolicyEvaluationResponse is an autogenerated conversion function.
+func Convert_controlplane_NetworkPolicyEvaluationResponse_To_v1beta2_NetworkPolicyEvaluationResponse(in *controlplane.NetworkPolicyEvaluationResponse, out *NetworkPolicyEvaluationResponse, s conversion.Scope) error {
+	return autoConvert_controlplane_NetworkPolicyEvaluationResponse_To_v1beta2_NetworkPolicyEvaluationResponse(in, out, s)
+}
+
 func autoConvert_v1beta2_NetworkPolicyList_To_controlplane_NetworkPolicyList(in *NetworkPolicyList, out *controlplane.NetworkPolicyList, s conversion.Scope) error {
 	out.ListMeta = in.ListMeta
 	if in.Items != nil {
@@ -1484,6 +1638,30 @@ func Convert_controlplane_PodReference_To_v1beta2_PodReference(in *controlplane.
 	return autoConvert_controlplane_PodReference_To_v1beta2_PodReference(in, out, s)
 }
 
+func autoConvert_v1beta2_RuleRef_To_controlplane_RuleRef(in *RuleRef, out *controlplane.RuleRef, s conversion.Scope) error {
+	out.Direction = controlplane.Direction(in.Direction)
+	out.Name = in.Name
+	out.Action = (*v1beta1.RuleAction)(unsafe.Pointer(in.Action))
+	return nil
+}
+
+// Convert_v1beta2_RuleRef_To_controlplane_RuleRef is an autogenerated conversion function.
+func Convert_v1beta2_RuleRef_To_controlplane_RuleRef(in *RuleRef, out *controlplane.RuleRef, s conversion.Scope) error {
+	return autoConvert_v1beta2_RuleRef_To_controlplane_RuleRef(in, out, s)
+}
+
+func autoConvert_controlplane_RuleRef_To_v1beta2_RuleRef(in *controlplane.RuleRef, out *RuleRef, s conversion.Scope) error {
+	out.Direction = Direction(in.Direction)
+	out.Name = in.Name
+	out.Action = (*v1beta1.RuleAction)(unsafe.Pointer(in.Action))
+	return nil
+}
+
+// Convert_controlplane_RuleRef_To_v1beta2_RuleRef is an autogenerated conversion function.
+func Convert_controlplane_RuleRef_To_v1beta2_RuleRef(in *controlplane.RuleRef, out *RuleRef, s conversion.Scope) error {
+	return autoConvert_controlplane_RuleRef_To_v1beta2_RuleRef(in, out, s)
+}
+
 func autoConvert_v1beta2_Service_To_controlplane_Service(in *Service, out *controlplane.Service, s conversion.Scope) error {
 	out.Protocol = (*controlplane.Protocol)(unsafe.Pointer(in.Protocol))
 	out.Port = (*intstr.IntOrString)(unsafe.Pointer(in.Port))
diff --git a/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go b/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go
index 86e63a1a0..7566eb4e4 100644
--- a/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go
+++ b/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go
@@ -1,7 +1,7 @@
 //go:build !ignore_autogenerated
 // +build !ignore_autogenerated
 
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -437,6 +437,27 @@ func (in *EgressGroupPatch) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Entity) DeepCopyInto(out *Entity) {
+	*out = *in
+	if in.Pod != nil {
+		in, out := &in.Pod, &out.Pod
+		*out = new(PodReference)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Entity.
+func (in *Entity) DeepCopy() *Entity {
+	if in == nil {
+		return nil
+	}
+	out := new(Entity)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ExternalEntityReference) DeepCopyInto(out *ExternalEntityReference) {
 	*out = *in
@@ -820,6 +841,77 @@ func (in *NetworkPolicy) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkPolicyEvaluation) DeepCopyInto(out *NetworkPolicyEvaluation) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	if in.Request != nil {
+		in, out := &in.Request, &out.Request
+		*out = new(NetworkPolicyEvaluationRequest)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Response != nil {
+		in, out := &in.Response, &out.Response
+		*out = new(NetworkPolicyEvaluationResponse)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEvaluation.
+func (in *NetworkPolicyEvaluation) DeepCopy() *NetworkPolicyEvaluation {
+	if in == nil {
+		return nil
+	}
+	out := new(NetworkPolicyEvaluation)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NetworkPolicyEvaluation) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkPolicyEvaluationRequest) DeepCopyInto(out *NetworkPolicyEvaluationRequest) {
+	*out = *in
+	in.Source.DeepCopyInto(&out.Source)
+	in.Destination.DeepCopyInto(&out.Destination)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEvaluationRequest.
+func (in *NetworkPolicyEvaluationRequest) DeepCopy() *NetworkPolicyEvaluationRequest {
+	if in == nil {
+		return nil
+	}
+	out := new(NetworkPolicyEvaluationRequest)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkPolicyEvaluationResponse) DeepCopyInto(out *NetworkPolicyEvaluationResponse) {
+	*out = *in
+	out.NetworkPolicy = in.NetworkPolicy
+	in.Rule.DeepCopyInto(&out.Rule)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEvaluationResponse.
+func (in *NetworkPolicyEvaluationResponse) DeepCopy() *NetworkPolicyEvaluationResponse {
+	if in == nil {
+		return nil
+	}
+	out := new(NetworkPolicyEvaluationResponse)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *NetworkPolicyList) DeepCopyInto(out *NetworkPolicyList) {
 	*out = *in
@@ -1135,6 +1227,27 @@ func (in *PodReference) DeepCopy() *PodReference {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RuleRef) DeepCopyInto(out *RuleRef) {
+	*out = *in
+	if in.Action != nil {
+		in, out := &in.Action, &out.Action
+		*out = new(v1beta1.RuleAction)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleRef.
+func (in *RuleRef) DeepCopy() *RuleRef {
+	if in == nil {
+		return nil
+	}
+	out := new(RuleRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Service) DeepCopyInto(out *Service) {
 	*out = *in
diff --git a/pkg/apis/controlplane/zz_generated.deepcopy.go b/pkg/apis/controlplane/zz_generated.deepcopy.go
index c95c73678..9f2f70f8f 100644
--- a/pkg/apis/controlplane/zz_generated.deepcopy.go
+++ b/pkg/apis/controlplane/zz_generated.deepcopy.go
@@ -1,7 +1,7 @@
 //go:build !ignore_autogenerated
 // +build !ignore_autogenerated
 
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -437,6 +437,27 @@ func (in *EgressGroupPatch) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Entity) DeepCopyInto(out *Entity) {
+	*out = *in
+	if in.Pod != nil {
+		in, out := &in.Pod, &out.Pod
+		*out = new(PodReference)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Entity.
+func (in *Entity) DeepCopy() *Entity {
+	if in == nil {
+		return nil
+	}
+	out := new(Entity)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ExternalEntityReference) DeepCopyInto(out *ExternalEntityReference) {
 	*out = *in
@@ -820,6 +841,77 @@ func (in *NetworkPolicy) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkPolicyEvaluation) DeepCopyInto(out *NetworkPolicyEvaluation) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	if in.Request != nil {
+		in, out := &in.Request, &out.Request
+		*out = new(NetworkPolicyEvaluationRequest)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Response != nil {
+		in, out := &in.Response, &out.Response
+		*out = new(NetworkPolicyEvaluationResponse)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEvaluation.
+func (in *NetworkPolicyEvaluation) DeepCopy() *NetworkPolicyEvaluation {
+	if in == nil {
+		return nil
+	}
+	out := new(NetworkPolicyEvaluation)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NetworkPolicyEvaluation) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkPolicyEvaluationRequest) DeepCopyInto(out *NetworkPolicyEvaluationRequest) {
+	*out = *in
+	in.Source.DeepCopyInto(&out.Source)
+	in.Destination.DeepCopyInto(&out.Destination)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEvaluationRequest.
+func (in *NetworkPolicyEvaluationRequest) DeepCopy() *NetworkPolicyEvaluationRequest {
+	if in == nil {
+		return nil
+	}
+	out := new(NetworkPolicyEvaluationRequest)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkPolicyEvaluationResponse) DeepCopyInto(out *NetworkPolicyEvaluationResponse) {
+	*out = *in
+	out.NetworkPolicy = in.NetworkPolicy
+	in.Rule.DeepCopyInto(&out.Rule)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEvaluationResponse.
+func (in *NetworkPolicyEvaluationResponse) DeepCopy() *NetworkPolicyEvaluationResponse {
+	if in == nil {
+		return nil
+	}
+	out := new(NetworkPolicyEvaluationResponse)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *NetworkPolicyList) DeepCopyInto(out *NetworkPolicyList) {
 	*out = *in
@@ -1135,6 +1227,27 @@ func (in *PodReference) DeepCopy() *PodReference {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RuleRef) DeepCopyInto(out *RuleRef) {
+	*out = *in
+	if in.Action != nil {
+		in, out := &in.Action, &out.Action
+		*out = new(v1beta1.RuleAction)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleRef.
+func (in *RuleRef) DeepCopy() *RuleRef {
+	if in == nil {
+		return nil
+	}
+	out := new(RuleRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Service) DeepCopyInto(out *Service) {
 	*out = *in
diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go
index b5d8787ff..7056f7945 100644
--- a/pkg/apiserver/apiserver.go
+++ b/pkg/apiserver/apiserver.go
@@ -52,6 +52,7 @@ import (
 	"antrea.io/antrea/pkg/apiserver/registry/networkpolicy/groupmember"
 	"antrea.io/antrea/pkg/apiserver/registry/networkpolicy/ipgroupassociation"
 	"antrea.io/antrea/pkg/apiserver/registry/networkpolicy/networkpolicy"
+	"antrea.io/antrea/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation"
 	"antrea.io/antrea/pkg/apiserver/registry/stats/antreaclusternetworkpolicystats"
 	"antrea.io/antrea/pkg/apiserver/registry/stats/antreanetworkpolicystats"
 	"antrea.io/antrea/pkg/apiserver/registry/stats/multicastgroup"
@@ -200,6 +201,7 @@ func installAPIGroup(s *APIServer, c completedConfig) error {
 	appliedToGroupStorage := appliedtogroup.NewREST(c.extraConfig.appliedToGroupStore)
 	networkPolicyStorage := networkpolicy.NewREST(c.extraConfig.networkPolicyStore)
 	networkPolicyStatusStorage := networkpolicy.NewStatusREST(c.extraConfig.networkPolicyStatusController)
+	networkPolicyEvaluationStorage := networkpolicyevaluation.NewREST(controllernetworkpolicy.NewPolicyRuleQuerier(c.extraConfig.endpointQuerier))
 	clusterGroupMembershipStorage := clustergroupmember.NewREST(c.extraConfig.networkPolicyController)
 	groupMembershipStorage := groupmember.NewREST(c.extraConfig.networkPolicyController)
 	groupAssociationStorage := groupassociation.NewREST(c.extraConfig.networkPolicyController)
@@ -214,6 +216,7 @@ func installAPIGroup(s *APIServer, c completedConfig) error {
 	cpv1beta2Storage["appliedtogroups"] = appliedToGroupStorage
 	cpv1beta2Storage["networkpolicies"] = networkPolicyStorage
 	cpv1beta2Storage["networkpolicies/status"] = networkPolicyStatusStorage
+	cpv1beta2Storage["networkpolicyevaluation"] = networkPolicyEvaluationStorage
 	cpv1beta2Storage["nodestatssummaries"] = nodeStatsSummaryStorage
 	cpv1beta2Storage["groupassociations"] = groupAssociationStorage
 	cpv1beta2Storage["ipgroupassociations"] = ipGroupAssociationStorage
diff --git a/pkg/apiserver/handlers/endpoint/handler.go b/pkg/apiserver/handlers/endpoint/handler.go
index 69e3d882f..41bcd9490 100644
--- a/pkg/apiserver/handlers/endpoint/handler.go
+++ b/pkg/apiserver/handlers/endpoint/handler.go
@@ -68,14 +68,14 @@ func HandleFunc(eq networkpolicy.EndpointQuerier) http.HandlerFunc {
 		var responsePolicies []v1beta2.NetworkPolicyReference
 		var responsePolicy v1beta2.NetworkPolicyReference
 		for _, internalPolicy := range endpointNetworkPolicyRules.AppliedPolicies {
-			v1beta2.Convert_controlplane_NetworkPolicyReference_To_v1beta2_NetworkPolicyReference(internalPolicy, &responsePolicy, nil)
+			v1beta2.Convert_controlplane_NetworkPolicyReference_To_v1beta2_NetworkPolicyReference(internalPolicy.SourceRef, &responsePolicy, nil)
 			responsePolicies = append(responsePolicies, responsePolicy)
 		}
 		// create rules based on effective rules on this endpoint
 		extractRules := func(effectiveRules []*antreatypes.RuleInfo) []Rule {
 			var responseRules []Rule
 			for _, rule := range effectiveRules {
-				v1beta2.Convert_controlplane_NetworkPolicyReference_To_v1beta2_NetworkPolicyReference(rule.Policy, &responsePolicy, nil)
+				v1beta2.Convert_controlplane_NetworkPolicyReference_To_v1beta2_NetworkPolicyReference(rule.Policy.SourceRef, &responsePolicy, nil)
 				newRule := Rule{
 					PolicyRef: responsePolicy,
 					Direction: v1beta2.Direction(rule.Rule.Direction),
diff --git a/pkg/apiserver/openapi/zz_generated.openapi.go b/pkg/apiserver/openapi/zz_generated.openapi.go
index 398e4641b..22779c684 100644
--- a/pkg/apiserver/openapi/zz_generated.openapi.go
+++ b/pkg/apiserver/openapi/zz_generated.openapi.go
@@ -43,6 +43,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.EgressGroup":                       schema_pkg_apis_controlplane_v1beta2_EgressGroup(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.EgressGroupList":                   schema_pkg_apis_controlplane_v1beta2_EgressGroupList(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.EgressGroupPatch":                  schema_pkg_apis_controlplane_v1beta2_EgressGroupPatch(ref),
+		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.Entity":                            schema_pkg_apis_controlplane_v1beta2_Entity(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.ExternalEntityReference":           schema_pkg_apis_controlplane_v1beta2_ExternalEntityReference(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.GroupAssociation":                  schema_pkg_apis_controlplane_v1beta2_GroupAssociation(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.GroupMember":                       schema_pkg_apis_controlplane_v1beta2_GroupMember(ref),
@@ -56,6 +57,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.MulticastGroupInfo":                schema_pkg_apis_controlplane_v1beta2_MulticastGroupInfo(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NamedPort":                         schema_pkg_apis_controlplane_v1beta2_NamedPort(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicy":                     schema_pkg_apis_controlplane_v1beta2_NetworkPolicy(ref),
+		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluation":           schema_pkg_apis_controlplane_v1beta2_NetworkPolicyEvaluation(ref),
+		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluationRequest":    schema_pkg_apis_controlplane_v1beta2_NetworkPolicyEvaluationRequest(ref),
+		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluationResponse":   schema_pkg_apis_controlplane_v1beta2_NetworkPolicyEvaluationResponse(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyList":                 schema_pkg_apis_controlplane_v1beta2_NetworkPolicyList(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyNodeStatus":           schema_pkg_apis_controlplane_v1beta2_NetworkPolicyNodeStatus(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyPeer":                 schema_pkg_apis_controlplane_v1beta2_NetworkPolicyPeer(ref),
@@ -67,6 +71,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NodeStatsSummary":                  schema_pkg_apis_controlplane_v1beta2_NodeStatsSummary(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.PaginationGetOptions":              schema_pkg_apis_controlplane_v1beta2_PaginationGetOptions(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.PodReference":                      schema_pkg_apis_controlplane_v1beta2_PodReference(ref),
+		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.RuleRef":                           schema_pkg_apis_controlplane_v1beta2_RuleRef(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.Service":                           schema_pkg_apis_controlplane_v1beta2_Service(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.ServiceReference":                  schema_pkg_apis_controlplane_v1beta2_ServiceReference(ref),
 		"antrea.io/antrea/pkg/apis/controlplane/v1beta2.SupportBundleCollection":           schema_pkg_apis_controlplane_v1beta2_SupportBundleCollection(ref),
@@ -1052,6 +1057,26 @@ func schema_pkg_apis_controlplane_v1beta2_EgressGroupPatch(ref common.ReferenceC
 	}
 }
 
+func schema_pkg_apis_controlplane_v1beta2_Entity(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "Entity contains Namespace and Pod name as a request parameter.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"pod": {
+						SchemaProps: spec.SchemaProps{
+							Ref: ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.PodReference"),
+						},
+					},
+				},
+			},
+		},
+		Dependencies: []string{
+			"antrea.io/antrea/pkg/apis/controlplane/v1beta2.PodReference"},
+	}
+}
+
 func schema_pkg_apis_controlplane_v1beta2_ExternalEntityReference(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -1636,6 +1661,107 @@ func schema_pkg_apis_controlplane_v1beta2_NetworkPolicy(ref common.ReferenceCall
 	}
 }
 
+func schema_pkg_apis_controlplane_v1beta2_NetworkPolicyEvaluation(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "NetworkPolicyEvaluation contains the request and response for a NetworkPolicy evaluation.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"kind": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"apiVersion": {
+						SchemaProps: spec.SchemaProps{
+							Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"request": {
+						SchemaProps: spec.SchemaProps{
+							Ref: ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluationRequest"),
+						},
+					},
+					"response": {
+						SchemaProps: spec.SchemaProps{
+							Ref: ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluationResponse"),
+						},
+					},
+				},
+			},
+		},
+		Dependencies: []string{
+			"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluationRequest", "antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyEvaluationResponse"},
+	}
+}
+
+func schema_pkg_apis_controlplane_v1beta2_NetworkPolicyEvaluationRequest(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "NetworkPolicyEvaluationRequest is the request body of NetworkPolicy evaluation.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"source": {
+						SchemaProps: spec.SchemaProps{
+							Default: map[string]interface{}{},
+							Ref:     ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.Entity"),
+						},
+					},
+					"destination": {
+						SchemaProps: spec.SchemaProps{
+							Default: map[string]interface{}{},
+							Ref:     ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.Entity"),
+						},
+					},
+				},
+			},
+		},
+		Dependencies: []string{
+			"antrea.io/antrea/pkg/apis/controlplane/v1beta2.Entity"},
+	}
+}
+
+func schema_pkg_apis_controlplane_v1beta2_NetworkPolicyEvaluationResponse(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "NetworkPolicyEvaluationResponse is the response of NetworkPolicy evaluation.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"networkPolicy": {
+						SchemaProps: spec.SchemaProps{
+							Description: "The reference of the effective NetworkPolicy.",
+							Default:     map[string]interface{}{},
+							Ref:         ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyReference"),
+						},
+					},
+					"ruleIndex": {
+						SchemaProps: spec.SchemaProps{
+							Type:   []string{"integer"},
+							Format: "int32",
+						},
+					},
+					"rule": {
+						SchemaProps: spec.SchemaProps{
+							Description: "The content of the effective rule.",
+							Default:     map[string]interface{}{},
+							Ref:         ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.RuleRef"),
+						},
+					},
+				},
+			},
+		},
+		Dependencies: []string{
+			"antrea.io/antrea/pkg/apis/controlplane/v1beta2.NetworkPolicyReference", "antrea.io/antrea/pkg/apis/controlplane/v1beta2.RuleRef"},
+	}
+}
+
 func schema_pkg_apis_controlplane_v1beta2_NetworkPolicyList(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -2243,6 +2369,37 @@ func schema_pkg_apis_controlplane_v1beta2_PodReference(ref common.ReferenceCallb
 	}
 }
 
+func schema_pkg_apis_controlplane_v1beta2_RuleRef(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "RuleRef contains basic information for the rule.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"direction": {
+						SchemaProps: spec.SchemaProps{
+							Type:   []string{"string"},
+							Format: "",
+						},
+					},
+					"name": {
+						SchemaProps: spec.SchemaProps{
+							Type:   []string{"string"},
+							Format: "",
+						},
+					},
+					"action": {
+						SchemaProps: spec.SchemaProps{
+							Type:   []string{"string"},
+							Format: "",
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
 func schema_pkg_apis_controlplane_v1beta2_Service(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
diff --git a/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation/rest.go b/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation/rest.go
new file mode 100644
index 000000000..c005a9aeb
--- /dev/null
+++ b/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation/rest.go
@@ -0,0 +1,67 @@
+// Copyright 2024 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 networkpolicyevaluation
+
+import (
+	"context"
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apiserver/pkg/registry/rest"
+
+	"antrea.io/antrea/pkg/apis/controlplane"
+	"antrea.io/antrea/pkg/controller/networkpolicy"
+)
+
+type REST struct {
+	querier networkpolicy.PolicyRuleQuerier
+}
+
+var (
+	_ rest.Storage = &REST{}
+	_ rest.Scoper  = &REST{}
+	_ rest.Creater = &REST{}
+)
+
+// NewREST returns a REST object that will work against API services.
+func NewREST(querier networkpolicy.PolicyRuleQuerier) *REST {
+	return &REST{querier}
+}
+
+func (r *REST) New() runtime.Object {
+	return &controlplane.NetworkPolicyEvaluation{}
+}
+
+func (r *REST) Destroy() {
+}
+
+func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
+	eval, ok := obj.(*controlplane.NetworkPolicyEvaluation)
+	if !ok {
+		return nil, errors.NewBadRequest(fmt.Sprintf("not a NetworkPolicyEvaluation object: %T", obj))
+	}
+	response, err := r.querier.QueryNetworkPolicyEvaluation(eval.Request)
+	if err != nil {
+		return nil, errors.NewInternalError(err)
+	}
+	eval.Response = response
+	return eval, nil
+}
+
+func (r *REST) NamespaceScoped() bool {
+	return false
+}
diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go
index 98c7b89f7..5f583d2ab 100644
--- a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go
+++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ type ControlplaneV1beta2Interface interface {
 	GroupMembersGetter
 	IPGroupAssociationsGetter
 	NetworkPoliciesGetter
+	NetworkPolicyEvaluationsGetter
 	NodeStatsSummariesGetter
 	SupportBundleCollectionsGetter
 }
@@ -75,6 +76,10 @@ func (c *ControlplaneV1beta2Client) NetworkPolicies() NetworkPolicyInterface {
 	return newNetworkPolicies(c)
 }
 
+func (c *ControlplaneV1beta2Client) NetworkPolicyEvaluations() NetworkPolicyEvaluationInterface {
+	return newNetworkPolicyEvaluations(c)
+}
+
 func (c *ControlplaneV1beta2Client) NodeStatsSummaries() NodeStatsSummaryInterface {
 	return newNodeStatsSummaries(c)
 }
diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go
index 232d3b75f..d2bd98cc7 100644
--- a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go
+++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -58,6 +58,10 @@ func (c *FakeControlplaneV1beta2) NetworkPolicies() v1beta2.NetworkPolicyInterfa
 	return &FakeNetworkPolicies{c}
 }
 
+func (c *FakeControlplaneV1beta2) NetworkPolicyEvaluations() v1beta2.NetworkPolicyEvaluationInterface {
+	return &FakeNetworkPolicyEvaluations{c}
+}
+
 func (c *FakeControlplaneV1beta2) NodeStatsSummaries() v1beta2.NodeStatsSummaryInterface {
 	return &FakeNodeStatsSummaries{c}
 }
diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_networkpolicyevaluation.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_networkpolicyevaluation.go
new file mode 100644
index 000000000..74ea3b78c
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_networkpolicyevaluation.go
@@ -0,0 +1,45 @@
+// Copyright 2024 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.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	"context"
+
+	v1beta2 "antrea.io/antrea/pkg/apis/controlplane/v1beta2"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeNetworkPolicyEvaluations implements NetworkPolicyEvaluationInterface
+type FakeNetworkPolicyEvaluations struct {
+	Fake *FakeControlplaneV1beta2
+}
+
+var networkpolicyevaluationsResource = schema.GroupVersionResource{Group: "controlplane.antrea.io", Version: "v1beta2", Resource: "networkpolicyevaluations"}
+
+var networkpolicyevaluationsKind = schema.GroupVersionKind{Group: "controlplane.antrea.io", Version: "v1beta2", Kind: "NetworkPolicyEvaluation"}
+
+// Create takes the representation of a networkPolicyEvaluation and creates it.  Returns the server's representation of the networkPolicyEvaluation, and an error, if there is any.
+func (c *FakeNetworkPolicyEvaluations) Create(ctx context.Context, networkPolicyEvaluation *v1beta2.NetworkPolicyEvaluation, opts v1.CreateOptions) (result *v1beta2.NetworkPolicyEvaluation, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootCreateAction(networkpolicyevaluationsResource, networkPolicyEvaluation), &v1beta2.NetworkPolicyEvaluation{})
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta2.NetworkPolicyEvaluation), err
+}
diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go
index b6b453ba5..454339ced 100644
--- a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go
+++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Antrea Authors
+// Copyright 2024 Antrea Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -26,4 +26,6 @@ type GroupAssociationExpansion interface{}
 
 type IPGroupAssociationExpansion interface{}
 
+type NetworkPolicyEvaluationExpansion interface{}
+
 type NodeStatsSummaryExpansion interface{}
diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/networkpolicyevaluation.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/networkpolicyevaluation.go
new file mode 100644
index 000000000..241e320c5
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/networkpolicyevaluation.go
@@ -0,0 +1,62 @@
+// Copyright 2024 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.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1beta2
+
+import (
+	"context"
+
+	v1beta2 "antrea.io/antrea/pkg/apis/controlplane/v1beta2"
+	scheme "antrea.io/antrea/pkg/client/clientset/versioned/scheme"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	rest "k8s.io/client-go/rest"
+)
+
+// NetworkPolicyEvaluationsGetter has a method to return a NetworkPolicyEvaluationInterface.
+// A group's client should implement this interface.
+type NetworkPolicyEvaluationsGetter interface {
+	NetworkPolicyEvaluations() NetworkPolicyEvaluationInterface
+}
+
+// NetworkPolicyEvaluationInterface has methods to work with NetworkPolicyEvaluation resources.
+type NetworkPolicyEvaluationInterface interface {
+	Create(ctx context.Context, networkPolicyEvaluation *v1beta2.NetworkPolicyEvaluation, opts v1.CreateOptions) (*v1beta2.NetworkPolicyEvaluation, error)
+	NetworkPolicyEvaluationExpansion
+}
+
+// networkPolicyEvaluations implements NetworkPolicyEvaluationInterface
+type networkPolicyEvaluations struct {
+	client rest.Interface
+}
+
+// newNetworkPolicyEvaluations returns a NetworkPolicyEvaluations
+func newNetworkPolicyEvaluations(c *ControlplaneV1beta2Client) *networkPolicyEvaluations {
+	return &networkPolicyEvaluations{
+		client: c.RESTClient(),
+	}
+}
+
+// Create takes the representation of a networkPolicyEvaluation and creates it.  Returns the server's representation of the networkPolicyEvaluation, and an error, if there is any.
+func (c *networkPolicyEvaluations) Create(ctx context.Context, networkPolicyEvaluation *v1beta2.NetworkPolicyEvaluation, opts v1.CreateOptions) (result *v1beta2.NetworkPolicyEvaluation, err error) {
+	result = &v1beta2.NetworkPolicyEvaluation{}
+	err = c.client.Post().
+		Resource("networkpolicyevaluations").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(networkPolicyEvaluation).
+		Do(ctx).
+		Into(result)
+	return
+}
diff --git a/pkg/cni/client.go b/pkg/cni/client.go
index ae1cb3d8c..6fd1af40e 100644
--- a/pkg/cni/client.go
+++ b/pkg/cni/client.go
@@ -17,7 +17,6 @@ package cni
 import (
 	"context"
 	"fmt"
-	"net"
 	"os"
 
 	"github.com/containernetworking/cni/pkg/skel"
@@ -27,7 +26,6 @@ import (
 	grpcinsecure "google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/status"
 
-	"antrea.io/antrea/pkg/agent/util"
 	cnipb "antrea.io/antrea/pkg/apis/cni/v1beta1"
 )
 
@@ -91,9 +89,7 @@ func rpcClient(f func(client cnipb.CniClient) error) error {
 	conn, err := grpc.Dial(
 		AntreaCNISocketAddr,
 		grpc.WithTransportCredentials(grpcinsecure.NewCredentials()),
-		grpc.WithContextDialer(func(ctx context.Context, addr string) (conn net.Conn, e error) {
-			return util.DialLocalSocket(addr)
-		}),
+		grpc.WithContextDialer(dial),
 	)
 	if err != nil {
 		return err
diff --git a/pkg/cni/dial_linux.go b/pkg/cni/dial_linux.go
new file mode 100644
index 000000000..b9ae64822
--- /dev/null
+++ b/pkg/cni/dial_linux.go
@@ -0,0 +1,24 @@
+// Copyright 2024 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 cni
+
+import (
+	"context"
+	"net"
+)
+
+func dial(_ context.Context, address string) (net.Conn, error) {
+	return net.Dial("unix", address)
+}
diff --git a/pkg/cni/dial_windows.go b/pkg/cni/dial_windows.go
new file mode 100644
index 000000000..398ce3c84
--- /dev/null
+++ b/pkg/cni/dial_windows.go
@@ -0,0 +1,32 @@
+// Copyright 2024 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 cni
+
+import (
+	"context"
+	"net"
+	"strings"
+
+	"github.com/Microsoft/go-winio"
+)
+
+const namedPipePrefix = `\\.\pipe\`
+
+func dial(_ context.Context, address string) (net.Conn, error) {
+	if strings.HasPrefix(address, namedPipePrefix) {
+		return winio.DialPipe(address, nil)
+	}
+	return net.Dial("unix", address)
+}
diff --git a/pkg/config/agent/config.go b/pkg/config/agent/config.go
index ed57bd00d..1c59f48b3 100644
--- a/pkg/config/agent/config.go
+++ b/pkg/config/agent/config.go
@@ -141,8 +141,6 @@ type AgentConfig struct {
 	ActiveFlowExportTimeout string `yaml:"activeFlowExportTimeout,omitempty"`
 	// Deprecated. Use the FlowExporter config options instead.
 	IdleFlowExportTimeout string `yaml:"idleFlowExportTimeout,omitempty"`
-	// Deprecated. Use the NodePortLocal config options instead.
-	NPLPortRange string `yaml:"nplPortRange,omitempty"`
 	// NodePortLocal (NPL) configuration options.
 	NodePortLocal NodePortLocalConfig `yaml:"nodePortLocal,omitempty"`
 	// FlowExporter configuration options.
@@ -400,7 +398,6 @@ type SecondaryNetworkConfig struct {
 
 type OVSBridgeConfig struct {
 	BridgeName string `yaml:"bridgeName"`
-	// Names of physical interfaces to be connected to the bridge. At the moment,
-	// only a single physical interface is supported.
+	// Names of physical interfaces to be connected to the bridge.
 	PhysicalInterfaces []string `yaml:"physicalInterfaces,omitempty"`
 }
diff --git a/pkg/controller/networkpolicy/endpoint_querier.go b/pkg/controller/networkpolicy/endpoint_querier.go
index 47ace09f3..5ee46c693 100644
--- a/pkg/controller/networkpolicy/endpoint_querier.go
+++ b/pkg/controller/networkpolicy/endpoint_querier.go
@@ -18,7 +18,15 @@
 package networkpolicy
 
 import (
+	"errors"
+	"math"
+	"sort"
+
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/sets"
+
 	"antrea.io/antrea/pkg/apis/controlplane"
+	crdv1beta1 "antrea.io/antrea/pkg/apis/crd/v1beta1"
 	"antrea.io/antrea/pkg/controller/networkpolicy/store"
 	antreatypes "antrea.io/antrea/pkg/controller/types"
 )
@@ -30,24 +38,68 @@ type EndpointQuerier interface {
 	QueryNetworkPolicyRules(namespace, podName string) (*antreatypes.EndpointNetworkPolicyRules, error)
 }
 
-// endpointQuerier implements the EndpointQuerier interface
-type endpointQuerier struct {
+// EndpointQuerierImpl implements the EndpointQuerier interface
+type EndpointQuerierImpl struct {
 	networkPolicyController *NetworkPolicyController
 }
 
-// NewEndpointQuerier returns a new *endpointQuerier.
-func NewEndpointQuerier(networkPolicyController *NetworkPolicyController) *endpointQuerier {
-	n := &endpointQuerier{
+// NewEndpointQuerier returns a new *EndpointQuerierImpl.
+func NewEndpointQuerier(networkPolicyController *NetworkPolicyController) *EndpointQuerierImpl {
+	return &EndpointQuerierImpl{
 		networkPolicyController: networkPolicyController,
 	}
-	return n
+}
+
+// PolicyRuleQuerier handles requests for querying effective policy rule on entities.
+type PolicyRuleQuerier interface {
+	QueryNetworkPolicyEvaluation(entities *controlplane.NetworkPolicyEvaluationRequest) (*controlplane.NetworkPolicyEvaluationResponse, error)
+}
+
+// policyRuleQuerier implements the PolicyRuleQuerier interface
+type policyRuleQuerier struct {
+	endpointQuerier EndpointQuerier
+}
+
+// NewPolicyRuleQuerier returns a new *policyRuleQuerier
+func NewPolicyRuleQuerier(endpointQuerier EndpointQuerier) *policyRuleQuerier {
+	return &policyRuleQuerier{
+		endpointQuerier: endpointQuerier,
+	}
+}
+
+type lessFunc func(p1, p2 *antreatypes.RuleInfo) int
+
+// ByRulePriority implements the Sort interface, sorting the rules within.
+// Comparators should be ordered by their importance in terms of determining rule priority.
+type ByRulePriority struct {
+	rules       []*antreatypes.RuleInfo
+	comparators []lessFunc
+}
+
+func (s ByRulePriority) Len() int { return len(s.rules) }
+
+func (s ByRulePriority) Swap(i, j int) { s.rules[i], s.rules[j] = s.rules[j], s.rules[i] }
+
+func (s ByRulePriority) Less(i, j int) bool {
+	p, q := s.rules[i], s.rules[j]
+	for k := 0; k < len(s.comparators); k++ {
+		less := s.comparators[k]
+		switch less(p, q) {
+		case 1: // p < q
+			return true
+		case -1: // p > q
+			return false
+		}
+		// p == q; try the next comparison.
+	}
+	return false
 }
 
 // QueryNetworkPolicyRules returns network policies and rules relevant to the selected
 // network endpoint. Relevant network policies fall into three categories: applied policies
 // are policies which directly apply to an endpoint, egress/ingress rules are rules which
 // reference the endpoint respectively.
-func (eq *endpointQuerier) QueryNetworkPolicyRules(namespace, podName string) (*antreatypes.EndpointNetworkPolicyRules, error) {
+func (eq *EndpointQuerierImpl) QueryNetworkPolicyRules(namespace, podName string) (*antreatypes.EndpointNetworkPolicyRules, error) {
 	if namespace == "" {
 		namespace = "default"
 	}
@@ -57,7 +109,7 @@ func (eq *endpointQuerier) QueryNetworkPolicyRules(namespace, podName string) (*
 	}
 
 	// create network policies categories
-	var applied []*controlplane.NetworkPolicyReference
+	var applied []*antreatypes.NetworkPolicy
 	var ingress, egress []*antreatypes.RuleInfo
 	// get all appliedToGroups using filter, then get applied policies using appliedToGroup
 	appliedToGroupKeys := groups[appliedToGroupType]
@@ -76,7 +128,7 @@ func (eq *endpointQuerier) QueryNetworkPolicyRules(namespace, podName string) (*
 			return nil, err
 		}
 		for _, policy := range policies {
-			applied = append(applied, policy.(*antreatypes.NetworkPolicy).SourceRef)
+			applied = append(applied, policy.(*antreatypes.NetworkPolicy))
 		}
 	}
 	// get all addressGroups using filter, then get ingress and egress policies using addressGroup
@@ -98,7 +150,7 @@ func (eq *endpointQuerier) QueryNetworkPolicyRules(namespace, podName string) (*
 			for _, rule := range policy.(*antreatypes.NetworkPolicy).Rules {
 				for _, addressGroupTrial := range rule.To.AddressGroups {
 					if addressGroupTrial == string(addressGroup.(*antreatypes.AddressGroup).UID) {
-						egress = append(egress, &antreatypes.RuleInfo{Policy: policy.(*antreatypes.NetworkPolicy).SourceRef, Index: egressIndex,
+						egress = append(egress, &antreatypes.RuleInfo{Policy: policy.(*antreatypes.NetworkPolicy), Index: egressIndex,
 							Rule: &controlplane.NetworkPolicyRule{Direction: rule.Direction, Name: rule.Name, Action: rule.Action}})
 						// an AddressGroup can only be referenced in a rule once
 						break
@@ -106,7 +158,7 @@ func (eq *endpointQuerier) QueryNetworkPolicyRules(namespace, podName string) (*
 				}
 				for _, addressGroupTrial := range rule.From.AddressGroups {
 					if addressGroupTrial == string(addressGroup.(*antreatypes.AddressGroup).UID) {
-						ingress = append(ingress, &antreatypes.RuleInfo{Policy: policy.(*antreatypes.NetworkPolicy).SourceRef, Index: ingressIndex,
+						ingress = append(ingress, &antreatypes.RuleInfo{Policy: policy.(*antreatypes.NetworkPolicy), Index: ingressIndex,
 							Rule: &controlplane.NetworkPolicyRule{Direction: rule.Direction, Name: rule.Name, Action: rule.Action}})
 						// an AddressGroup can only be referenced in a rule once
 						break
@@ -125,3 +177,147 @@ func (eq *endpointQuerier) QueryNetworkPolicyRules(namespace, podName string) (*
 	}
 	return &antreatypes.EndpointNetworkPolicyRules{Namespace: namespace, Name: podName, AppliedPolicies: applied, EndpointAsIngressSrcRules: ingress, EndpointAsEgressDstRules: egress}, nil
 }
+
+// processEndpointAppliedRules processes NetworkPolicy rules applied to an endpoint,
+// returns a set of the corresponding policy UIDs, and manually generates Kubernetes
+// NetworkPolicy default isolation rules if they exist. The default isolation rule's
+// direction depends on isSourceEndpoint, and has the lowest precedence.
+func processEndpointAppliedRules(appliedPolicies []*antreatypes.NetworkPolicy, isSourceEndpoint bool) (sets.Set[types.UID], []*antreatypes.RuleInfo) {
+	policyUIDs := sets.New[types.UID]()
+	isolationRules := make([]*antreatypes.RuleInfo, 0)
+	for _, internalPolicy := range appliedPolicies {
+		policyUIDs.Insert(internalPolicy.SourceRef.UID)
+		if internalPolicy.SourceRef.Type == controlplane.K8sNetworkPolicy {
+			// check if the Kubernetes NetworkPolicy creates ingress or egress isolationRules
+			for _, rule := range internalPolicy.Rules {
+				if rule.Direction == controlplane.DirectionIn && !isSourceEndpoint {
+					isolationRules = append(isolationRules, &antreatypes.RuleInfo{Policy: internalPolicy, Index: math.MaxInt,
+						Rule: &controlplane.NetworkPolicyRule{Direction: rule.Direction, Name: rule.Name, Action: rule.Action}})
+				} else if rule.Direction == controlplane.DirectionOut && isSourceEndpoint {
+					isolationRules = append(isolationRules, &antreatypes.RuleInfo{Policy: internalPolicy, Index: math.MaxInt,
+						Rule: &controlplane.NetworkPolicyRule{Direction: rule.Direction, Name: rule.Name, Action: rule.Action}})
+				}
+			}
+		}
+	}
+	return policyUIDs, isolationRules
+}
+
+// predictEndpointsRules returns the predicted rules effective from srcEndpoints to dstEndpoints.
+// Rules returned satisfy a. in source applied policies and destination egress rules,
+// or b. in source ingress rules and destination applied policies or c. applied to KNP default isolation.
+func predictEndpointsRules(srcEndpointRules, dstEndpointRules *antreatypes.EndpointNetworkPolicyRules) (commonRule *antreatypes.RuleInfo) {
+	commonRules := make([]*antreatypes.RuleInfo, 0)
+	if srcEndpointRules != nil && dstEndpointRules != nil {
+		srcPolicies, srcIsolated := processEndpointAppliedRules(srcEndpointRules.AppliedPolicies, true)
+		dstPolicies, dstIsolated := processEndpointAppliedRules(dstEndpointRules.AppliedPolicies, false)
+		for _, rule := range dstEndpointRules.EndpointAsEgressDstRules {
+			if srcPolicies.Has(rule.Policy.SourceRef.UID) {
+				commonRules = append(commonRules, rule)
+			}
+		}
+		for _, rule := range srcEndpointRules.EndpointAsIngressSrcRules {
+			if dstPolicies.Has(rule.Policy.SourceRef.UID) {
+				commonRules = append(commonRules, rule)
+			}
+		}
+		for _, defaultDropRule := range srcIsolated {
+			commonRules = append(commonRules, defaultDropRule)
+		}
+		for _, defaultDropRule := range dstIsolated {
+			commonRules = append(commonRules, defaultDropRule)
+		}
+	}
+
+	// sort the common rules based on multiple closures, the top rule has the highest precedence
+	tierPriority := func(r1, r2 *antreatypes.RuleInfo) int {
+		effectiveTierPriorityK8sNP := (DefaultTierPriority + BaselineTierPriority) / 2
+		r1Priority, r2Priority := effectiveTierPriorityK8sNP, effectiveTierPriorityK8sNP
+		if r1.Policy.TierPriority != nil {
+			r1Priority = *r1.Policy.TierPriority
+		}
+		if r2.Policy.TierPriority != nil {
+			r2Priority = *r2.Policy.TierPriority
+		}
+		if r1Priority < r2Priority {
+			return 1
+		} else if r1Priority > r2Priority {
+			return -1
+		}
+		return 0
+	}
+	policyPriority := func(r1, r2 *antreatypes.RuleInfo) int {
+		if r1.Policy.Priority != nil && r2.Policy.Priority != nil {
+			if *r1.Policy.Priority < *r2.Policy.Priority {
+				return 1
+			} else if *r1.Policy.Priority > *r2.Policy.Priority {
+				return -1
+			}
+		}
+		return 0
+	}
+	rulePriority := func(r1, r2 *antreatypes.RuleInfo) int {
+		if r1.Index < r2.Index {
+			return 1
+		} else if r1.Index > r2.Index {
+			return -1
+		}
+		return 0
+	}
+	defaultOrder := func(r1, r2 *antreatypes.RuleInfo) int {
+		if r1.Policy.Name < r2.Policy.Name {
+			return 1
+		}
+		return 0
+	}
+	sort.Sort(ByRulePriority{rules: commonRules, comparators: []lessFunc{tierPriority, policyPriority, rulePriority, defaultOrder}})
+	if len(commonRules) > 0 {
+		commonRule = commonRules[0]
+		// filter Antrea-native policy rules with Pass action
+		// if pass rule currently has the highest precedence, skip the remaining rules
+		// until the next K8s rule or Baseline rule, or return the pass rule otherwise
+		isPass := func(ruleInfo *controlplane.NetworkPolicyRule) bool {
+			return ruleInfo.Action != nil && *ruleInfo.Action == crdv1beta1.RuleActionPass
+		}
+		if isPass(commonRule.Rule) {
+			for _, rule := range commonRules[1:] {
+				if rule.Policy.SourceRef.Type == controlplane.K8sNetworkPolicy ||
+					(rule.Policy.TierPriority != nil && *rule.Policy.TierPriority == BaselineTierPriority && !isPass(rule.Rule)) {
+					commonRule = rule
+					break
+				}
+			}
+		}
+	}
+	return
+}
+
+// QueryNetworkPolicyEvaluation returns the effective NetworkPolicy rule on given
+// source and destination entities.
+func (eq *policyRuleQuerier) QueryNetworkPolicyEvaluation(entities *controlplane.NetworkPolicyEvaluationRequest) (*controlplane.NetworkPolicyEvaluationResponse, error) {
+	if entities.Source.Pod == nil || entities.Destination.Pod == nil || entities.Source.Pod.Name == "" || entities.Destination.Pod.Name == "" {
+		return nil, errors.New("invalid NetworkPolicyEvaluation request entities")
+	}
+	// query endpoints and handle response errors
+	endpointAnalysisSource, err := eq.endpointQuerier.QueryNetworkPolicyRules(entities.Source.Pod.Namespace, entities.Source.Pod.Name)
+	if err != nil {
+		return nil, err
+	}
+	endpointAnalysisDestination, err := eq.endpointQuerier.QueryNetworkPolicyRules(entities.Destination.Pod.Namespace, entities.Destination.Pod.Name)
+	if err != nil {
+		return nil, err
+	}
+	endpointAnalysisRule := predictEndpointsRules(endpointAnalysisSource, endpointAnalysisDestination)
+	if endpointAnalysisRule == nil {
+		return nil, nil
+	}
+	return &controlplane.NetworkPolicyEvaluationResponse{
+		NetworkPolicy: *endpointAnalysisRule.Policy.SourceRef,
+		RuleIndex:     int32(endpointAnalysisRule.Index),
+		Rule: controlplane.RuleRef{
+			Direction: endpointAnalysisRule.Rule.Direction,
+			Name:      endpointAnalysisRule.Rule.Name,
+			Action:    endpointAnalysisRule.Rule.Action,
+		},
+	}, nil
+}
diff --git a/pkg/controller/types/networkpolicy.go b/pkg/controller/types/networkpolicy.go
index b43bfd302..534e099a7 100644
--- a/pkg/controller/types/networkpolicy.go
+++ b/pkg/controller/types/networkpolicy.go
@@ -125,7 +125,7 @@ func (p *NetworkPolicy) GetAppliedToGroups() sets.Set[string] {
 // RuleInfo stores the original NetworkPolicy info, index of this rule in the NetworkPolicy
 // corresponding ingress/egress rules, and the original rule info.
 type RuleInfo struct {
-	Policy *controlplane.NetworkPolicyReference
+	Policy *NetworkPolicy
 	Index  int
 	Rule   *controlplane.NetworkPolicyRule
 }
@@ -135,7 +135,7 @@ type RuleInfo struct {
 type EndpointNetworkPolicyRules struct {
 	Namespace                 string
 	Name                      string
-	AppliedPolicies           []*controlplane.NetworkPolicyReference
+	AppliedPolicies           []*NetworkPolicy
 	EndpointAsIngressSrcRules []*RuleInfo
 	EndpointAsEgressDstRules  []*RuleInfo
 }


Test patch:
diff --git a/ci/kind/test-secondary-network-kind.sh b/ci/kind/test-secondary-network-kind.sh
index f88897dfa..0b8e120a9 100755
--- a/ci/kind/test-secondary-network-kind.sh
+++ b/ci/kind/test-secondary-network-kind.sh
@@ -112,6 +112,8 @@ function run_test {
 
   # Wait for antrea-controller start to make sure the IPPool validation webhook is ready.
   kubectl rollout status --timeout=1m deployment.apps/antrea-controller -n kube-system
+  # An extra small delay to reduce the possibility of failure in CI.
+  sleep 5
   kubectl apply -f $ATTACHMENT_DEFINITION_YAML
   kubectl apply -f $SECONDARY_NETWORKS_YAML
 
diff --git a/ci/kind/test-upgrade-antrea.sh b/ci/kind/test-upgrade-antrea.sh
index baf2d24ff..e1f9f91eb 100755
--- a/ci/kind/test-upgrade-antrea.sh
+++ b/ci/kind/test-upgrade-antrea.sh
@@ -148,8 +148,18 @@ fi
 
 echo "Running upgrade test for tag $FROM_TAG"
 
-DOCKER_IMAGES=("busybox" "projects.registry.vmware.com/antrea/antrea-ubuntu:$FROM_TAG")
+function version_lt() { test "$(printf '%s\n' "$@" | sort -rV | head -n 1)" != "$1"; }
+
+DOCKER_IMAGES=()
+if version_lt "$FROM_TAG" v1.15; then
+    DOCKER_IMAGES+=("projects.registry.vmware.com/antrea/antrea-ubuntu:$FROM_TAG")
+else
+    DOCKER_IMAGES+=("projects.registry.vmware.com/antrea/antrea-agent-ubuntu:$FROM_TAG" \
+                    "projects.registry.vmware.com/antrea/antrea-controller-ubuntu:$FROM_TAG")
+fi
 
+# Silence CLI suggestions.
+export DOCKER_CLI_HINTS=false
 for img in "${DOCKER_IMAGES[@]}"; do
     echo "Pulling $img"
     for i in `seq 3`; do
diff --git a/cmd/antrea-agent/options_test.go b/cmd/antrea-agent/options_test.go
index 22c612d7c..4b62f7758 100644
--- a/cmd/antrea-agent/options_test.go
+++ b/cmd/antrea-agent/options_test.go
@@ -319,8 +319,8 @@ func TestOptionsValidateSecondaryNetworkConfig(t *testing.T) {
 			name:               "two interfaces",
 			featureGateValue:   true,
 			ovsBridges:         []string{"br1"},
-			physicalInterfaces: []string{"eth1", "eth2"},
-			expectedErr:        "at most one physical interface can be specified for the secondary network OVS bridge",
+			physicalInterfaces: []string{"eth1", "eth2", "eth3", "eth4", "eth5", "eth6", "eth7", "eth8", "eth9"},
+			expectedErr:        "at most eight physical interfaces can be specified for the secondary network OVS bridge",
 		},
 	}
 	for _, tc := range tests {
diff --git a/pkg/agent/nodeportlocal/portcache/port_table_others_test.go b/pkg/agent/nodeportlocal/portcache/port_table_others_test.go
index 25231c326..2ac40b34e 100644
--- a/pkg/agent/nodeportlocal/portcache/port_table_others_test.go
+++ b/pkg/agent/nodeportlocal/portcache/port_table_others_test.go
@@ -35,25 +35,22 @@ func TestRestoreRules(t *testing.T) {
 	portTable := newPortTable(mockIPTables, mockPortOpener)
 	allNPLPorts := []rules.PodNodePort{
 		{
-			NodePort:  nodePort1,
-			PodPort:   1001,
-			PodIP:     podIP,
-			Protocol:  "tcp",
-			Protocols: []string{"tcp"},
+			NodePort: nodePort1,
+			PodPort:  1001,
+			PodIP:    podIP,
+			Protocol: "tcp",
 		},
 		{
-			NodePort:  nodePort1,
-			PodPort:   1001,
-			PodIP:     podIP,
-			Protocol:  "udp",
-			Protocols: []string{"udp"},
+			NodePort: nodePort1,
+			PodPort:  1001,
+			PodIP:    podIP,
+			Protocol: "udp",
 		},
 		{
-			NodePort:  nodePort2,
-			PodPort:   1002,
-			PodIP:     podIP,
-			Protocol:  "udp",
-			Protocols: []string{"udp"},
+			NodePort: nodePort2,
+			PodPort:  1002,
+			PodIP:    podIP,
+			Protocol: "udp",
 		},
 	}
 
diff --git a/pkg/agent/nodeportlocal/rules/iptable_rule_test.go b/pkg/agent/nodeportlocal/rules/iptable_rule_test.go
index 4915d7495..e9887496c 100644
--- a/pkg/agent/nodeportlocal/rules/iptable_rule_test.go
+++ b/pkg/agent/nodeportlocal/rules/iptable_rule_test.go
@@ -114,6 +114,8 @@ func TestAddAndDeleteAllRules(t *testing.T) {
 				mockIPTables.AppendRule(iptables.ProtocolIPv4, iptables.NATTable, iptables.OutputChain, []string{"-p", "all", "-m", "addrtype", "--dst-type", "LOCAL", "-j", NodePortLocalChain})
 				mockIPTables.Restore(`*nat
 :ANTREA-NODE-PORT-LOCAL - [0:0]
+-A ANTREA-NODE-PORT-LOCAL -p udp -m udp --dport 8227 -j DNAT --to-destination 10.10.10.11:7237
+-A ANTREA-NODE-PORT-LOCAL -p tcp -m tcp --dport 8247 -j DNAT --to-destination 10.11.10.12:7257
 COMMIT
 `, false, false)
 				// Set the expectation for ChainExists to return true
@@ -151,6 +153,9 @@ COMMIT
 				mockIPTables.AppendRule(iptables.ProtocolIPv4, iptables.NATTable, iptables.OutputChain, []string{"-p", "all", "-m", "addrtype", "--dst-type", "LOCAL", "-j", NodePortLocalChain})
 				mockIPTables.Restore(`*nat
 :ANTREA-NODE-PORT-LOCAL - [0:0]
+-A ANTREA-NODE-PORT-LOCAL -p tcp -m tcp --dport 7 -j DNAT --to-destination 10.10.10.10:17
+-A ANTREA-NODE-PORT-LOCAL -p udp -m udp --dport 27 -j DNAT --to-destination 10.10.10.11:37
+-A ANTREA-NODE-PORT-LOCAL -p tcp -m tcp --dport 47 -j DNAT --to-destination 10.11.10.12:57
 COMMIT
 `, false, false)
 				// Set the expectation for ChainExists to return true
diff --git a/pkg/agent/nodeportlocal/testing/annotations.go b/pkg/agent/nodeportlocal/testing/annotations.go
index ba479b980..e98559dca 100644
--- a/pkg/agent/nodeportlocal/testing/annotations.go
+++ b/pkg/agent/nodeportlocal/testing/annotations.go
@@ -47,8 +47,7 @@ func (a *ExpectedNPLAnnotations) find(podPort int, protocol string) *types.NPLAn
 }
 
 func (a *ExpectedNPLAnnotations) Add(nodePort *int, podPort int, protocol string) *ExpectedNPLAnnotations {
-	protocols := []string{protocol}
-	annotation := types.NPLAnnotation{PodPort: podPort, Protocol: protocol, Protocols: protocols}
+	annotation := types.NPLAnnotation{PodPort: podPort, Protocol: protocol}
 	if nodePort != nil {
 		annotation.NodePort = *nodePort
 	}
@@ -66,7 +65,6 @@ func (a *ExpectedNPLAnnotations) Check(t *testing.T, nplValue []types.NPLAnnotat
 		if !assert.NotNilf(t, expectedAnnotation, "Unexpected annotation with PodPort %d", nplAnnotation.PodPort) {
 			continue
 		}
-		assert.ElementsMatch(t, expectedAnnotation.Protocols, nplAnnotation.Protocols, "Protocols mismatch in annotation")
 		if expectedAnnotation.NodeIP != "" {
 			assert.Equal(t, expectedAnnotation.NodeIP, nplAnnotation.NodeIP, "NodeIP mismatch in annotation")
 		}
diff --git a/pkg/agent/secondarynetwork/init_test.go b/pkg/agent/secondarynetwork/init_test.go
index 56845365e..b9be6fe0f 100644
--- a/pkg/agent/secondarynetwork/init_test.go
+++ b/pkg/agent/secondarynetwork/init_test.go
@@ -82,6 +82,8 @@ func TestCreateOVSBridge(t *testing.T) {
 				m.EXPECT().Create().Return(nil)
 				m.EXPECT().GetOFPort("eth1", false).Return(int32(0), ovsconfig.InvalidArgumentsError("port not found"))
 				m.EXPECT().CreateUplinkPort("eth1", int32(0), map[string]interface{}{"antrea-type": "uplink"}).Return("", nil)
+				m.EXPECT().GetOFPort("eth2", false).Return(int32(1), ovsconfig.InvalidArgumentsError("port not found"))
+				m.EXPECT().CreateUplinkPort("eth2", int32(1), map[string]interface{}{"antrea-type": "uplink"}).Return("", nil)
 			},
 		},
 		{
diff --git a/pkg/antctl/parameter/parameter_test.go b/pkg/antctl/parameter/parameter_test.go
new file mode 100644
index 000000000..c13676e83
--- /dev/null
+++ b/pkg/antctl/parameter/parameter_test.go
@@ -0,0 +1,80 @@
+// Copyright 2024 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 parameter
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"k8s.io/apimachinery/pkg/runtime"
+
+	cpv1beta "antrea.io/antrea/pkg/apis/controlplane/v1beta2"
+)
+
+func TestNewNetworkPolicyEvaluation(t *testing.T) {
+	tests := []struct {
+		name           string
+		args           map[string]string
+		expectedObject runtime.Object
+		expectedError  string
+	}{
+		{
+			name: "Successful parsing",
+			args: map[string]string{
+				"source":      "ns/pod1",
+				"destination": "ns/pod2",
+			},
+			expectedObject: &cpv1beta.NetworkPolicyEvaluation{
+				Request: &cpv1beta.NetworkPolicyEvaluationRequest{
+					Source:      cpv1beta.Entity{Pod: &cpv1beta.PodReference{Namespace: "ns", Name: "pod1"}},
+					Destination: cpv1beta.Entity{Pod: &cpv1beta.PodReference{Namespace: "ns", Name: "pod2"}},
+				},
+			},
+		},
+		{
+			name: "Invalid format",
+			args: map[string]string{
+				"destination": "ns",
+			},
+			expectedError: "missing entities for NetworkPolicyEvaluation request",
+		},
+		{
+			name: "Default namespaces",
+			args: map[string]string{
+				"source":      "pod1",
+				"destination": "pod2",
+			},
+			expectedObject: &cpv1beta.NetworkPolicyEvaluation{
+				Request: &cpv1beta.NetworkPolicyEvaluationRequest{
+					Source:      cpv1beta.Entity{Pod: &cpv1beta.PodReference{Namespace: "default", Name: "pod1"}},
+					Destination: cpv1beta.Entity{Pod: &cpv1beta.PodReference{Namespace: "default", Name: "pod2"}},
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotObject, err := NewNetworkPolicyEvaluation(tt.args)
+			if tt.expectedError == "" {
+				require.NoError(t, err)
+				assert.Equal(t, tt.expectedObject, gotObject)
+			} else {
+				assert.ErrorContains(t, err, tt.expectedError)
+			}
+		})
+	}
+}
diff --git a/pkg/antctl/transform/networkpolicy/transform_test.go b/pkg/antctl/transform/networkpolicy/transform_test.go
index b5fcdb411..cc409bd8c 100644
--- a/pkg/antctl/transform/networkpolicy/transform_test.go
+++ b/pkg/antctl/transform/networkpolicy/transform_test.go
@@ -130,3 +130,20 @@ func TestListTransform(t *testing.T) {
 		})
 	}
 }
+
+func TestEvaluationResponseTransform(t *testing.T) {
+	test := EvaluationResponse{&cpv1beta.NetworkPolicyEvaluation{}}
+	assert.Equal(t, []string{"NAME", "NAMESPACE", "POLICY-TYPE", "RULE-INDEX", "DIRECTION"}, test.GetTableHeader())
+	assert.False(t, test.SortRows())
+	assert.Equal(t, []string{"", "", "", "", ""}, test.GetTableRow(32))
+	test.Response = &cpv1beta.NetworkPolicyEvaluationResponse{
+		NetworkPolicy: cpv1beta.NetworkPolicyReference{
+			Type:      cpv1beta.K8sNetworkPolicy,
+			Namespace: "ns",
+			Name:      "testName",
+		},
+		RuleIndex: 10,
+		Rule:      cpv1beta.RuleRef{Direction: cpv1beta.DirectionIn},
+	}
+	assert.Equal(t, []string{"testName", "ns", "K8sNetworkPolicy", "10", "In"}, test.GetTableRow(32))
+}
diff --git a/pkg/apiserver/handlers/endpoint/handler_test.go b/pkg/apiserver/handlers/endpoint/handler_test.go
index bb2cb0039..1ac99b389 100644
--- a/pkg/apiserver/handlers/endpoint/handler_test.go
+++ b/pkg/apiserver/handlers/endpoint/handler_test.go
@@ -110,13 +110,15 @@ func TestSinglePolicyResponse(t *testing.T) {
 			},
 			argsMock: []string{namespace, pod},
 			mockQueryResponse: &antreatypes.EndpointNetworkPolicyRules{
-				AppliedPolicies: []*controlplane.NetworkPolicyReference{
-					{Name: "policy1"},
+				AppliedPolicies: []*antreatypes.NetworkPolicy{
+					{SourceRef: &controlplane.NetworkPolicyReference{Name: "policy1"}},
 				},
 				EndpointAsIngressSrcRules: []*antreatypes.RuleInfo{
 					{
-						Policy: &controlplane.NetworkPolicyReference{Name: "policy2"},
-						Index:  0,
+						Policy: &antreatypes.NetworkPolicy{
+							SourceRef: &controlplane.NetworkPolicyReference{Name: "policy2"},
+						},
+						Index: 0,
 						Rule: &controlplane.NetworkPolicyRule{
 							Direction: controlplane.DirectionIn,
 						},
@@ -151,8 +153,9 @@ func TestMultiPolicyResponse(t *testing.T) {
 			},
 			argsMock: []string{namespace, pod},
 			mockQueryResponse: &antreatypes.EndpointNetworkPolicyRules{
-				AppliedPolicies: []*controlplane.NetworkPolicyReference{
-					{Name: "policy1"}, {Name: "policy2"},
+				AppliedPolicies: []*antreatypes.NetworkPolicy{
+					{SourceRef: &controlplane.NetworkPolicyReference{Name: "policy1"}},
+					{SourceRef: &controlplane.NetworkPolicyReference{Name: "policy2"}},
 				},
 			},
 		},
diff --git a/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation/rest_test.go b/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation/rest_test.go
new file mode 100644
index 000000000..7631b5704
--- /dev/null
+++ b/pkg/apiserver/registry/networkpolicy/networkpolicyevaluation/rest_test.go
@@ -0,0 +1,96 @@
+// Copyright 2024 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 networkpolicyevaluation
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"go.uber.org/mock/gomock"
+	"k8s.io/apimachinery/pkg/api/errors"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+
+	"antrea.io/antrea/pkg/apis/controlplane"
+	queriermock "antrea.io/antrea/pkg/controller/networkpolicy/testing"
+)
+
+func TestREST(t *testing.T) {
+	r := NewREST(nil)
+	assert.Equal(t, &controlplane.NetworkPolicyEvaluation{}, r.New())
+	assert.False(t, r.NamespaceScoped())
+}
+
+func TestRESTCreate(t *testing.T) {
+	request := controlplane.NetworkPolicyEvaluationRequest{Source: controlplane.Entity{Pod: &controlplane.PodReference{Namespace: "ns", Name: "pod1"}}, Destination: controlplane.Entity{Pod: &controlplane.PodReference{Namespace: "ns", Name: "pod2"}}}
+	tests := []struct {
+		name                string
+		obj                 runtime.Object
+		expectedReturnedObj runtime.Object
+		expectedErr         error
+		mockResponse        *controlplane.NetworkPolicyEvaluationResponse
+		mockErr             error
+	}{
+		{
+			name: "Succeed",
+			obj: &controlplane.NetworkPolicyEvaluation{
+				Request: &request,
+			},
+			expectedReturnedObj: &controlplane.NetworkPolicyEvaluation{
+				Request: &request,
+				Response: &controlplane.NetworkPolicyEvaluationResponse{
+					NetworkPolicy: controlplane.NetworkPolicyReference{Name: "test"},
+					Rule:          controlplane.RuleRef{Direction: controlplane.DirectionIn},
+				},
+			},
+			mockResponse: &controlplane.NetworkPolicyEvaluationResponse{
+				NetworkPolicy: controlplane.NetworkPolicyReference{Name: "test"},
+				Rule:          controlplane.RuleRef{Direction: controlplane.DirectionIn},
+			},
+		},
+		{
+			name: "Query error",
+			obj: &controlplane.NetworkPolicyEvaluation{
+				Request: &request,
+			},
+			mockErr:     fmt.Errorf("querier error"),
+			expectedErr: errors.NewInternalError(fmt.Errorf("querier error")),
+		},
+		{
+			name: "Unexpected type",
+			obj: &controlplane.NetworkPolicy{
+				ObjectMeta: v1.ObjectMeta{
+					Name: "foo",
+				},
+			},
+			expectedErr: errors.NewBadRequest("not a NetworkPolicyEvaluation object: *controlplane.NetworkPolicy"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			mockCtrl := gomock.NewController(t)
+			mockQuerier := queriermock.NewMockPolicyRuleQuerier(mockCtrl)
+			if tt.mockResponse != nil || tt.mockErr != nil {
+				mockQuerier.EXPECT().QueryNetworkPolicyEvaluation(tt.obj.(*controlplane.NetworkPolicyEvaluation).Request).Return(tt.mockResponse, tt.mockErr)
+			}
+			r := NewREST(mockQuerier)
+			actualObj, err := r.Create(context.TODO(), tt.obj, nil, &v1.CreateOptions{})
+			assert.Equal(t, tt.expectedErr, err)
+			assert.Equal(t, tt.expectedReturnedObj, actualObj)
+		})
+	}
+}
diff --git a/pkg/controller/networkpolicy/endpoint_querier_test.go b/pkg/controller/networkpolicy/endpoint_querier_test.go
index e1690b108..f4994cccc 100644
--- a/pkg/controller/networkpolicy/endpoint_querier_test.go
+++ b/pkg/controller/networkpolicy/endpoint_querier_test.go
@@ -15,18 +15,23 @@
 package networkpolicy
 
 import (
+	"fmt"
 	"testing"
 	"time"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"go.uber.org/mock/gomock"
 	corev1 "k8s.io/api/core/v1"
 	networkingv1 "k8s.io/api/networking/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 
 	"antrea.io/antrea/pkg/apis/controlplane"
+	crdv1beta1 "antrea.io/antrea/pkg/apis/crd/v1beta1"
+	queriermock "antrea.io/antrea/pkg/controller/networkpolicy/testing"
 	antreatypes "antrea.io/antrea/pkg/controller/types"
 )
 
@@ -181,7 +186,7 @@ var namespaces = []*corev1.Namespace{
 	},
 }
 
-func makeControllerAndEndpointQuerier(objects ...runtime.Object) *endpointQuerier {
+func makeControllerAndEndpointQuerier(objects ...runtime.Object) *EndpointQuerierImpl {
 	// create controller
 	_, c := newController(objects, nil)
 	c.heartbeatCh = make(chan heartbeat, 1000)
@@ -251,14 +256,16 @@ func TestQueryNetworkPolicyRules(t *testing.T) {
 			podNamespace: ns,
 			podName:      podA,
 			expectedResponse: &antreatypes.EndpointNetworkPolicyRules{
-				Namespace:       ns,
-				Name:            podA,
-				AppliedPolicies: []*controlplane.NetworkPolicyReference{&policyRef},
+				Namespace: ns,
+				Name:      podA,
+				AppliedPolicies: []*antreatypes.NetworkPolicy{
+					{SourceRef: &policyRef},
+				},
 				EndpointAsIngressSrcRules: []*antreatypes.RuleInfo{
-					{Policy: &policyRef, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionIn}},
+					{Policy: &antreatypes.NetworkPolicy{SourceRef: &policyRef}, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionIn}},
 				},
 				EndpointAsEgressDstRules: []*antreatypes.RuleInfo{
-					{Policy: &policyRef, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionOut}},
+					{Policy: &antreatypes.NetworkPolicy{SourceRef: &policyRef}, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionOut}},
 				},
 			},
 		},
@@ -268,14 +275,17 @@ func TestQueryNetworkPolicyRules(t *testing.T) {
 			podNamespace: ns,
 			podName:      podA,
 			expectedResponse: &antreatypes.EndpointNetworkPolicyRules{
-				Namespace:       ns,
-				Name:            podA,
-				AppliedPolicies: []*controlplane.NetworkPolicyReference{&policyRef, &policyRef1},
+				Namespace: ns,
+				Name:      podA,
+				AppliedPolicies: []*antreatypes.NetworkPolicy{
+					{SourceRef: &policyRef},
+					{SourceRef: &policyRef1},
+				},
 				EndpointAsIngressSrcRules: []*antreatypes.RuleInfo{
-					{Policy: &policyRef, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionIn}},
+					{Policy: &antreatypes.NetworkPolicy{SourceRef: &policyRef}, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionIn}},
 				},
 				EndpointAsEgressDstRules: []*antreatypes.RuleInfo{
-					{Policy: &policyRef, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionOut}},
+					{Policy: &antreatypes.NetworkPolicy{SourceRef: &policyRef}, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionOut}},
 				},
 			},
 		},
@@ -285,11 +295,13 @@ func TestQueryNetworkPolicyRules(t *testing.T) {
 			podNamespace: ns,
 			podName:      podA,
 			expectedResponse: &antreatypes.EndpointNetworkPolicyRules{
-				Namespace:       ns,
-				Name:            podA,
-				AppliedPolicies: []*controlplane.NetworkPolicyReference{&policyRef2},
+				Namespace: ns,
+				Name:      podA,
+				AppliedPolicies: []*antreatypes.NetworkPolicy{
+					{SourceRef: &policyRef2},
+				},
 				EndpointAsIngressSrcRules: []*antreatypes.RuleInfo{
-					{Policy: &policyRef2, Index: 1, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionIn}},
+					{Policy: &antreatypes.NetworkPolicy{SourceRef: &policyRef2}, Index: 1, Rule: &controlplane.NetworkPolicyRule{Direction: controlplane.DirectionIn}},
 				},
 			},
 		},
@@ -300,7 +312,7 @@ func TestQueryNetworkPolicyRules(t *testing.T) {
 		for idx := range expectedRules {
 			assert.EqualValues(t, expectedRules[idx].Rule.Direction, responseRules[idx].Rule.Direction)
 			assert.Equal(t, expectedRules[idx].Index, responseRules[idx].Index)
-			assert.Equal(t, expectedRules[idx].Policy, responseRules[idx].Policy)
+			assert.Equal(t, expectedRules[idx].Policy.SourceRef, responseRules[idx].Policy.SourceRef)
 		}
 		return
 	}
@@ -320,8 +332,8 @@ func TestQueryNetworkPolicyRules(t *testing.T) {
 				assert.Equal(t, len(tc.expectedResponse.AppliedPolicies), len(response.AppliedPolicies))
 				var expectedPolicies, responsePolicies []*controlplane.NetworkPolicyReference
 				for idx, expected := range tc.expectedResponse.AppliedPolicies {
-					expectedPolicies = append(expectedPolicies, expected)
-					responsePolicies = append(responsePolicies, response.AppliedPolicies[idx])
+					expectedPolicies = append(expectedPolicies, expected.SourceRef)
+					responsePolicies = append(responsePolicies, response.AppliedPolicies[idx].SourceRef)
 				}
 				assert.ElementsMatch(t, expectedPolicies, responsePolicies)
 				evaluateResponse(tc.expectedResponse.EndpointAsIngressSrcRules, response.EndpointAsIngressSrcRules)
@@ -330,3 +342,258 @@ func TestQueryNetworkPolicyRules(t *testing.T) {
 		})
 	}
 }
+
+type AccessTestCase struct {
+	name              string
+	request           *controlplane.NetworkPolicyEvaluationRequest
+	mockQueryResponse []mockResponse
+	expectedResult    *controlplane.NetworkPolicyEvaluationResponse
+	expectedErr       string
+}
+
+type mockResponse struct {
+	response *antreatypes.EndpointNetworkPolicyRules
+	error    error
+}
+
+func TestQueryNetworkPolicyEvaluation(t *testing.T) {
+	mockCtrl := gomock.NewController(t)
+	namespace, pod1, pod2 := "ns", "pod1", "pod2"
+	accessRequest := &controlplane.NetworkPolicyEvaluationRequest{
+		Source:      controlplane.Entity{Pod: &controlplane.PodReference{Namespace: namespace, Name: pod1}},
+		Destination: controlplane.Entity{Pod: &controlplane.PodReference{Namespace: namespace, Name: pod2}},
+	}
+	argsMock := []string{namespace, pod1, namespace, pod2}
+	uid1, uid2 := types.UID(fmt.Sprint(111)), types.UID(fmt.Sprint(222))
+	priority1, priority2, defaultPriority, tierEmergency := float64(10), float64(15), float64(-1), int32(50)
+
+	// functions used to generate mock responses
+	generatePolicies := func(policyUID types.UID, policyType controlplane.NetworkPolicyType, direction controlplane.Direction, tierPriority *int32, policyPriority *float64, numRules int, action *crdv1beta1.RuleAction) []*antreatypes.NetworkPolicy {
+		rules := make([]controlplane.NetworkPolicyRule, numRules)
+		for i := 0; i < numRules; i++ {
+			rules[i] = controlplane.NetworkPolicyRule{
+				Direction: direction,
+				Name:      fmt.Sprintf("Policy%sRule%d", policyUID, i),
+				Priority:  int32(i),
+			}
+			if action != nil {
+				rules[i].Action = action
+			}
+		}
+		return []*antreatypes.NetworkPolicy{{
+			UID:          policyUID,
+			Name:         fmt.Sprintf("Policy%s", policyUID),
+			SourceRef:    &controlplane.NetworkPolicyReference{Type: policyType, Namespace: namespace, Name: fmt.Sprintf("Policy%s", policyUID), UID: policyUID},
+			Rules:        rules,
+			TierPriority: tierPriority,
+			Priority:     policyPriority,
+		}}
+	}
+	generateRuleInfo := func(policy *antreatypes.NetworkPolicy) []*antreatypes.RuleInfo {
+		ruleInfoMatches := make([]*antreatypes.RuleInfo, len(policy.Rules))
+		for i := 0; i < len(policy.Rules); i++ {
+			ruleInfoMatches[i] = &antreatypes.RuleInfo{
+				Policy: policy,
+				Index:  i,
+				Rule:   &controlplane.NetworkPolicyRule{Direction: policy.Rules[i].Direction, Name: policy.Rules[i].Name, Action: policy.Rules[i].Action},
+			}
+		}
+		return ruleInfoMatches
+	}
+	generateResponse := func(podID int, appliedPolicies []*antreatypes.NetworkPolicy, matchedRules []*antreatypes.RuleInfo) *antreatypes.EndpointNetworkPolicyRules {
+		endpointRule := &antreatypes.EndpointNetworkPolicyRules{
+			Namespace:       namespace,
+			Name:            fmt.Sprintf("pod%d", podID),
+			AppliedPolicies: appliedPolicies,
+		}
+		if podID == 1 {
+			endpointRule.EndpointAsIngressSrcRules = matchedRules
+		} else if podID == 2 {
+			endpointRule.EndpointAsEgressDstRules = matchedRules
+		}
+		return endpointRule
+	}
+
+	expectedResponse111 := controlplane.NetworkPolicyEvaluationResponse{
+		NetworkPolicy: controlplane.NetworkPolicyReference{Type: controlplane.AntreaNetworkPolicy, Namespace: namespace, Name: "Policy111", UID: uid1},
+		RuleIndex:     0,
+		Rule:          controlplane.RuleRef{Direction: controlplane.DirectionOut, Name: "Policy111Rule0", Action: &allowAction},
+	}
+	expectedResponse222 := controlplane.NetworkPolicyEvaluationResponse{
+		NetworkPolicy: controlplane.NetworkPolicyReference{Type: controlplane.AntreaNetworkPolicy, Namespace: namespace, Name: "Policy222", UID: uid2},
+		RuleIndex:     0,
+		Rule:          controlplane.RuleRef{Direction: controlplane.DirectionIn, Name: "Policy222Rule0", Action: &allowAction},
+	}
+
+	testCases := []AccessTestCase{
+		{
+			name:    "Pass rule fallthrough",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &BaselineTierPriority, nil, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &tierEmergency, nil, 1, &passAction)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &tierEmergency, nil, 1, &passAction),
+					generateRuleInfo(generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &BaselineTierPriority, nil, 1, &allowAction)[0]))},
+			},
+			expectedResult: &expectedResponse111,
+		},
+		{
+			name:    "Different Tier priorities",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &tierEmergency, nil, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, nil, 1, &allowAction)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, nil, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &tierEmergency, nil, 1, &allowAction)[0]))},
+			},
+			expectedResult: &expectedResponse111,
+		},
+		{
+			name:    "Different policy priorities 1",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, &priority1, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority2, 1, &allowAction)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority2, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, &priority1, 1, &allowAction)[0]))},
+			},
+			expectedResult: &expectedResponse111,
+		},
+		{
+			name:    "Different policy priorities 2",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, &priority2, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority1, 1, &allowAction)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority1, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, &priority2, 1, &allowAction)[0]))},
+			},
+			expectedResult: &expectedResponse222,
+		},
+		{
+			name:    "Different rule priorities",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, nil, generateRuleInfo(generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority1, 2, &allowAction)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority1, 2, &allowAction), nil)},
+			},
+			expectedResult: &expectedResponse222,
+		},
+		{
+			name:    "Different policy names",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, &priority1, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority1, 1, &allowAction)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.AntreaNetworkPolicy, controlplane.DirectionIn, &DefaultTierPriority, &priority1, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, &priority1, 1, &allowAction)[0]))},
+			},
+			expectedResult: &expectedResponse111,
+		},
+		{
+			name:    "KNP and baseline ANP",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &BaselineTierPriority, nil, 1, &allowAction),
+					generateRuleInfo(generatePolicies(uid2, controlplane.K8sNetworkPolicy, controlplane.DirectionIn, nil, &defaultPriority, 1, nil)[0]))},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.K8sNetworkPolicy, controlplane.DirectionIn, nil, &defaultPriority, 1, nil),
+					generateRuleInfo(generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &BaselineTierPriority, nil, 1, &allowAction)[0]))},
+			},
+			expectedResult: &controlplane.NetworkPolicyEvaluationResponse{
+				NetworkPolicy: controlplane.NetworkPolicyReference{Type: controlplane.K8sNetworkPolicy, Namespace: namespace, Name: "Policy222", UID: uid2},
+				RuleIndex:     0,
+				Rule:          controlplane.RuleRef{Direction: controlplane.DirectionIn, Name: "Policy222Rule0"},
+			},
+		},
+		{
+			name:    "KNP and default isolation",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.K8sNetworkPolicy, controlplane.DirectionOut, nil, &defaultPriority, 1, nil), nil)},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.K8sNetworkPolicy, controlplane.DirectionIn, nil, &defaultPriority, 1, nil),
+					generateRuleInfo(generatePolicies(uid1, controlplane.K8sNetworkPolicy, controlplane.DirectionOut, nil, &defaultPriority, 1, nil)[0]))},
+			},
+			expectedResult: &controlplane.NetworkPolicyEvaluationResponse{
+				NetworkPolicy: controlplane.NetworkPolicyReference{Type: controlplane.K8sNetworkPolicy, Namespace: namespace, Name: "Policy111", UID: uid1},
+				RuleIndex:     0,
+				Rule:          controlplane.RuleRef{Direction: controlplane.DirectionOut, Name: "Policy111Rule0"},
+			},
+		},
+		{
+			name:    "KNP egress default isolation",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.K8sNetworkPolicy, controlplane.DirectionOut, nil, &defaultPriority, 1, nil), nil)},
+				{response: generateResponse(2, nil, nil)},
+			},
+			expectedResult: &controlplane.NetworkPolicyEvaluationResponse{
+				NetworkPolicy: controlplane.NetworkPolicyReference{Type: controlplane.K8sNetworkPolicy, Namespace: namespace, Name: "Policy111", UID: uid1},
+				RuleIndex:     -1,
+				Rule:          controlplane.RuleRef{Direction: controlplane.DirectionOut, Name: "Policy111Rule0"},
+			},
+		},
+		{
+			name:    "KNP ingress default isolation",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, nil, nil)},
+				{response: generateResponse(2, generatePolicies(uid2, controlplane.K8sNetworkPolicy, controlplane.DirectionIn, nil, &defaultPriority, 1, nil), nil)},
+			},
+			expectedResult: &controlplane.NetworkPolicyEvaluationResponse{
+				NetworkPolicy: controlplane.NetworkPolicyReference{Type: controlplane.K8sNetworkPolicy, Namespace: namespace, Name: "Policy222", UID: uid2},
+				RuleIndex:     -1,
+				Rule:          controlplane.RuleRef{Direction: controlplane.DirectionIn, Name: "Policy222Rule0"},
+			},
+		},
+		{
+			name:    "No common rule found",
+			request: accessRequest,
+			mockQueryResponse: []mockResponse{
+				{response: generateResponse(1, generatePolicies(uid1, controlplane.AntreaNetworkPolicy, controlplane.DirectionOut, &DefaultTierPriority, nil, 1, &allowAction), nil)},
+				{response: generateResponse(2, nil, nil)},
+			},
+		},
+		{
+			name:              "Querier error 1",
+			request:           accessRequest,
+			mockQueryResponse: []mockResponse{{}, {error: errors.NewInternalError(fmt.Errorf("querier error"))}},
+			expectedErr:       "querier error",
+		},
+		{
+			name:              "Querier error 2",
+			request:           accessRequest,
+			mockQueryResponse: []mockResponse{{error: errors.NewInternalError(fmt.Errorf("querier error"))}, {}},
+			expectedErr:       "querier error",
+		},
+		{
+			name:        "Request error",
+			request:     &controlplane.NetworkPolicyEvaluationRequest{Destination: controlplane.Entity{Pod: &controlplane.PodReference{Namespace: namespace}}},
+			expectedErr: "invalid NetworkPolicyEvaluation request entities",
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			mockQuerier := queriermock.NewMockEndpointQuerier(mockCtrl)
+			if tc.mockQueryResponse != nil {
+				for i, mock := range tc.mockQueryResponse {
+					mockQuerier.EXPECT().QueryNetworkPolicyRules(argsMock[2*i], argsMock[2*i+1]).Return(mock.response, mock.error)
+					if mock.error != nil {
+						break
+					}
+				}
+			}
+			policyRuleQuerier := NewPolicyRuleQuerier(mockQuerier)
+			response, err := policyRuleQuerier.QueryNetworkPolicyEvaluation(tc.request)
+			if tc.expectedErr == "" {
+				assert.Nil(t, err)
+				assert.Equal(t, tc.expectedResult, response)
+			} else {
+				assert.ErrorContains(t, err, tc.expectedErr)
+			}
+
+		})
+	}
+}
diff --git a/pkg/controller/networkpolicy/testing/mock_networkpolicy.go b/pkg/controller/networkpolicy/testing/mock_networkpolicy.go
index 7dc7698f8..bc90e3769 100644
--- a/pkg/controller/networkpolicy/testing/mock_networkpolicy.go
+++ b/pkg/controller/networkpolicy/testing/mock_networkpolicy.go
@@ -14,11 +14,11 @@
 //
 
 // Code generated by MockGen. DO NOT EDIT.
-// Source: antrea.io/antrea/pkg/controller/networkpolicy (interfaces: EndpointQuerier)
+// Source: antrea.io/antrea/pkg/controller/networkpolicy (interfaces: EndpointQuerier,PolicyRuleQuerier)
 //
 // Generated by this command:
 //
-//	mockgen -copyright_file hack/boilerplate/license_header.raw.txt -destination pkg/controller/networkpolicy/testing/mock_networkpolicy.go -package testing antrea.io/antrea/pkg/controller/networkpolicy EndpointQuerier
+//	mockgen -copyright_file hack/boilerplate/license_header.raw.txt -destination pkg/controller/networkpolicy/testing/mock_networkpolicy.go -package testing antrea.io/antrea/pkg/controller/networkpolicy EndpointQuerier,PolicyRuleQuerier
 //
 // Package testing is a generated GoMock package.
 package testing
@@ -26,6 +26,7 @@ package testing
 import (
 	reflect "reflect"
 
+	controlplane "antrea.io/antrea/pkg/apis/controlplane"
 	types "antrea.io/antrea/pkg/controller/types"
 	gomock "go.uber.org/mock/gomock"
 )
@@ -67,3 +68,41 @@ func (mr *MockEndpointQuerierMockRecorder) QueryNetworkPolicyRules(arg0, arg1 an
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryNetworkPolicyRules", reflect.TypeOf((*MockEndpointQuerier)(nil).QueryNetworkPolicyRules), arg0, arg1)
 }
+
+// MockPolicyRuleQuerier is a mock of PolicyRuleQuerier interface.
+type MockPolicyRuleQuerier struct {
+	ctrl     *gomock.Controller
+	recorder *MockPolicyRuleQuerierMockRecorder
+}
+
+// MockPolicyRuleQuerierMockRecorder is the mock recorder for MockPolicyRuleQuerier.
+type MockPolicyRuleQuerierMockRecorder struct {
+	mock *MockPolicyRuleQuerier
+}
+
+// NewMockPolicyRuleQuerier creates a new mock instance.
+func NewMockPolicyRuleQuerier(ctrl *gomock.Controller) *MockPolicyRuleQuerier {
+	mock := &MockPolicyRuleQuerier{ctrl: ctrl}
+	mock.recorder = &MockPolicyRuleQuerierMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockPolicyRuleQuerier) EXPECT() *MockPolicyRuleQuerierMockRecorder {
+	return m.recorder
+}
+
+// QueryNetworkPolicyEvaluation mocks base method.
+func (m *MockPolicyRuleQuerier) QueryNetworkPolicyEvaluation(arg0 *controlplane.NetworkPolicyEvaluationRequest) (*controlplane.NetworkPolicyEvaluationResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "QueryNetworkPolicyEvaluation", arg0)
+	ret0, _ := ret[0].(*controlplane.NetworkPolicyEvaluationResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// QueryNetworkPolicyEvaluation indicates an expected call of QueryNetworkPolicyEvaluation.
+func (mr *MockPolicyRuleQuerierMockRecorder) QueryNetworkPolicyEvaluation(arg0 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryNetworkPolicyEvaluation", reflect.TypeOf((*MockPolicyRuleQuerier)(nil).QueryNetworkPolicyEvaluation), arg0)
+}
diff --git a/test/e2e-secondary-network/secondary_network_test.go b/test/e2e-secondary-network/secondary_network_test.go
index 3b210897e..262069f13 100644
--- a/test/e2e-secondary-network/secondary_network_test.go
+++ b/test/e2e-secondary-network/secondary_network_test.go
@@ -105,29 +105,34 @@ func (data *testData) createPodForSecondaryNetwork(ns string, pod *testPodInfo)
 	return podBuilder.Create(data.e2eTestData)
 }
 
-// getSecondaryInterface checks the secondary interfaces created for the specific Pod and returns its IPv4 address.
-func (data *testData) getSecondaryInterface(targetPod *testPodInfo, interfaceName string) (string, error) {
-	cmd := []string{"/bin/sh", "-c", fmt.Sprintf("ip addr show %s | grep 'inet ' | awk '{print $2}' | cut -d/ -f1", interfaceName)}
+// listPodIPs returns a map of Pod IPs, indexed by the interface name. All interfaces are included
+// and only IPv4 addresses are considered. If an interface is not assigned an IPv4 address, it will
+// be included in the map, with a nil value.
+func (data *testData) listPodIPs(targetPod *testPodInfo) (map[string]net.IP, error) {
+	cmd := []string{"ip", "addr", "show"}
 	stdout, _, err := data.e2eTestData.RunCommandFromPod(data.e2eTestData.GetTestNamespace(), targetPod.podName, containerName, cmd)
-	stdout = strings.TrimSuffix(stdout, "\n")
-	if err != nil || stdout == "" {
-		return "", fmt.Errorf("interface %s not found on %s. err: %v", interfaceName, targetPod.podName, err)
+	if err != nil {
+		return nil, fmt.Errorf("error when listing interfaces for %s: %w", targetPod.podName, err)
 	}
-	return stdout, nil
-}
-
-// checkSubnet checks if the IP address to be pinged has the same subnet as the Pod from which the IP Address is pinged.
-func (data *testData) checkSubnet(t *testing.T, sourcePod, targetPod *testPodInfo, targetNetwork string) (bool, error) {
-	for i, n := range sourcePod.interfaceNetworks {
-		if n == targetNetwork {
-			_, err := data.getSecondaryInterface(sourcePod, i)
-			if err != nil {
-				return false, err
+	result := make(map[string]net.IP)
+	var currentInterface string
+	lines := strings.Split(stdout, "\n")
+	for _, line := range lines {
+		fields := strings.Fields(line)
+		if len(fields) >= 2 && strings.HasSuffix(fields[0], ":") {
+			// first field is ifindex, second field is interface name
+			currentInterface = strings.Split(strings.TrimSuffix(fields[1], ":"), "@")[0]
+			result[currentInterface] = nil
+		} else if len(fields) >= 2 && fields[0] == "inet" {
+			ipStr := strings.Split(fields[1], "/")[0]
+			ip := net.ParseIP(ipStr)
+			if ip == nil {
+				return nil, fmt.Errorf("failed to parse IP (%s) for interface %s of Pod %s", ipStr, currentInterface, targetPod.podName)
 			}
-			return true, nil
+			result[currentInterface] = ip
 		}
 	}
-	return false, nil
+	return result, nil
 }
 
 // pingBetweenInterfaces parses through all the created Pods and pings the other Pod if the two Pods
@@ -135,44 +140,82 @@ func (data *testData) checkSubnet(t *testing.T, sourcePod, targetPod *testPodInf
 func (data *testData) pingBetweenInterfaces(t *testing.T) error {
 	e2eTestData := data.e2eTestData
 	namespace := e2eTestData.GetTestNamespace()
-	for _, sourcePod := range data.pods {
-		for _, targetPod := range data.pods {
-			if targetPod.podName == sourcePod.podName {
-				continue
+
+	type attachment struct {
+		network string
+		iface   string
+		ip      net.IP
+	}
+	type network struct {
+		// maps each Pod to its attachments in this network (typically just one)
+		podAttachments map[*testPodInfo][]*attachment
+	}
+	networks := make(map[string]*network)
+	addPodNetworkAttachments := func(pod *testPodInfo, podAttachments []*attachment) {
+		for _, pa := range podAttachments {
+			if _, ok := networks[pa.network]; !ok {
+				networks[pa.network] = &network{
+					podAttachments: make(map[*testPodInfo][]*attachment),
+				}
 			}
-			for i, n := range targetPod.interfaceNetworks {
-				_, err := e2eTestData.PodWaitFor(defaultTimeout, targetPod.podName, namespace, func(pod *corev1.Pod) (bool, error) {
-					return pod.Status.Phase == corev1.PodRunning, nil
-				})
-				if err != nil {
-					return fmt.Errorf("error when waiting for Pod %s: %v", targetPod.podName, err)
+			networks[pa.network].podAttachments[pod] = append(networks[pa.network].podAttachments[pod], pa)
+		}
+	}
+
+	// Collect all secondary network IPs when they are available.
+	for _, testPod := range data.pods {
+		_, err := e2eTestData.PodWaitFor(defaultTimeout, testPod.podName, namespace, func(pod *corev1.Pod) (bool, error) {
+			if pod.Status.Phase != corev1.PodRunning {
+				return false, nil
+			}
+			var podNetworkAttachments []*attachment
+			podIPs, err := data.listPodIPs(testPod)
+			if err != nil {
+				return false, err
+			}
+			for iface, net := range testPod.interfaceNetworks {
+				if podIPs[iface] == nil {
+					return false, nil
 				}
+				podNetworkAttachments = append(podNetworkAttachments, &attachment{
+					network: net,
+					iface:   iface,
+					ip:      podIPs[iface],
+				})
+			}
+			// we found all the expected secondary network interfaces / attachments
+			addPodNetworkAttachments(testPod, podNetworkAttachments)
+			return true, nil
+		})
+		if err != nil {
+			return fmt.Errorf("error when waiting for secondary IPs for Pod %+v: %v", testPod, err)
+		}
+	}
 
-				matched, _ := data.checkSubnet(t, sourcePod, targetPod, n)
-				if matched {
-					secondaryIPAddress, err := data.getSecondaryInterface(targetPod, i)
-					if err != nil {
-						return err
-					}
-					ip := net.ParseIP(secondaryIPAddress)
-					if ip == nil {
-						return fmt.Errorf("failed to parse IP (%s) for interface %s of Pod %s", secondaryIPAddress, i, targetPod.podName)
-					}
+	// Run ping-mesh test for each secondary network.
+	for _, network := range networks {
+		for sourcePod := range network.podAttachments {
+			for targetPod, targetPodAttachments := range network.podAttachments {
+				if sourcePod == targetPod {
+					continue
+				}
+				for _, targetAttachment := range targetPodAttachments {
 					var IPToPing antreae2e.PodIPs
-					if ip.To4() != nil {
-						IPToPing = antreae2e.PodIPs{IPv4: &ip}
+					if targetAttachment.ip.To4() != nil {
+						IPToPing = antreae2e.PodIPs{IPv4: &targetAttachment.ip}
 					} else {
-						IPToPing = antreae2e.PodIPs{IPv6: &ip}
+						IPToPing = antreae2e.PodIPs{IPv6: &targetAttachment.ip}
 					}
 					if err := e2eTestData.RunPingCommandFromTestPod(antreae2e.PodInfo{Name: sourcePod.podName, OS: osType, NodeName: sourcePod.nodeName, Namespace: namespace},
 						namespace, &IPToPing, containerName, pingCount, pingSize, false); err != nil {
-						return fmt.Errorf("ping '%s' -> '%s'(Interface: %s, IP Address: %s) failed: %v", sourcePod.podName, targetPod.podName, i, secondaryIPAddress, err)
+						return fmt.Errorf("ping '%s' -> '%s'(Interface: %s, IP Address: %s) failed: %v", sourcePod.podName, targetPod.podName, targetAttachment.iface, targetAttachment.ip, err)
 					}
-					logs.Infof("ping '%s' -> '%s'( Interface: %s, IP Address: %s): OK", sourcePod.podName, targetPod.podName, i, secondaryIPAddress)
+					logs.Infof("ping '%s' -> '%s'( Interface: %s, IP Address: %s): OK", sourcePod.podName, targetPod.podName, targetAttachment.iface, targetAttachment.ip)
 				}
 			}
 		}
 	}
+
 	return nil
 }
 
diff --git a/test/e2e/nodeportlocal_test.go b/test/e2e/nodeportlocal_test.go
index fee779211..3a95ed3bf 100644
--- a/test/e2e/nodeportlocal_test.go
+++ b/test/e2e/nodeportlocal_test.go
@@ -164,16 +164,14 @@ func checkNPLRules(t *testing.T, data *TestData, r *require.Assertions, nplAnnot
 func checkNPLRulesForPod(t *testing.T, data *TestData, r *require.Assertions, nplAnnotations []types.NPLAnnotation, antreaPod, podIP string, present bool) {
 	var rules []nplRuleData
 	for _, ann := range nplAnnotations {
-		for _, protocol := range ann.Protocols {
-			rule := nplRuleData{
-				nodeIP:   ann.NodeIP,
-				nodePort: ann.NodePort,
-				podIP:    podIP,
-				podPort:  ann.PodPort,
-				protocol: protocol,
-			}
-			rules = append(rules, rule)
+		rule := nplRuleData{
+			nodeIP:   ann.NodeIP,
+			nodePort: ann.NodePort,
+			podIP:    podIP,
+			podPort:  ann.PodPort,
+			protocol: ann.Protocol,
 		}
+		rules = append(rules, rule)
 	}
 	checkForNPLRuleInIPTables(t, data, r, antreaPod, rules, present)
 	checkForNPLListeningSockets(t, data, r, antreaPod, rules, present)
@@ -182,16 +180,14 @@ func checkNPLRulesForPod(t *testing.T, data *TestData, r *require.Assertions, np
 func checkNPLRulesForWindowsPod(t *testing.T, data *TestData, r *require.Assertions, nplAnnotations []types.NPLAnnotation, antreaPod, podIP string, nodeName string, present bool) {
 	var rules []nplRuleData
 	for _, ann := range nplAnnotations {
-		for _, protocol := range ann.Protocols {
-			rule := nplRuleData{
-				nodeIP:   ann.NodeIP,
-				nodePort: ann.NodePort,
-				podIP:    podIP,
-				podPort:  ann.PodPort,
-				protocol: protocol,
-			}
-			rules = append(rules, rule)
+		rule := nplRuleData{
+			nodeIP:   ann.NodeIP,
+			nodePort: ann.NodePort,
+			podIP:    podIP,
+			podPort:  ann.PodPort,
+			protocol: ann.Protocol,
 		}
+		rules = append(rules, rule)
 	}
 	checkForNPLRuleInNetNat(t, data, r, antreaPod, nodeName, rules, present)
 }
@@ -345,10 +341,8 @@ func deleteNPLRuleFromNetNat(t *testing.T, data *TestData, r *require.Assertions
 
 func checkTrafficForNPL(data *TestData, r *require.Assertions, nplAnnotations []types.NPLAnnotation, clientName string) {
 	for i := range nplAnnotations {
-		for j := range nplAnnotations[i].Protocols {
-			err := data.runNetcatCommandFromTestPodWithProtocol(clientName, data.testNamespace, "agnhost", nplAnnotations[i].NodeIP, int32(nplAnnotations[i].NodePort), nplAnnotations[i].Protocols[j])
-			r.NoError(err, "Traffic test failed for NodeIP: %s, NodePort: %d, Protocol: %s", nplAnnotations[i].NodeIP, nplAnnotations[i].NodePort, nplAnnotations[i].Protocols[j])
-		}
+		err := data.runNetcatCommandFromTestPodWithProtocol(clientName, data.testNamespace, "agnhost", nplAnnotations[i].NodeIP, int32(nplAnnotations[i].NodePort), nplAnnotations[i].Protocol)
+		r.NoError(err, "Traffic test failed for NodeIP: %s, NodePort: %d, Protocol: %s", nplAnnotations[i].NodeIP, nplAnnotations[i].NodePort, nplAnnotations[i].Protocol)
 	}
 }
 
@@ -645,7 +639,7 @@ func testNPLMultiplePodsAgentRestart(t *testing.T, data *TestData) {
 
 // testNPLChangePortRangeAgentRestart tests NodePortLocal functionalities after changing port range.
 // - Create multiple Nginx Pods.
-// - Change nplPortRange.
+// - Change the PortRange.
 // - Restart Antrea Agent Pods.
 // - Verify that updated port range is being used for NPL.
 func testNPLChangePortRangeAgentRestart(t *testing.T, data *TestData) {
@@ -677,15 +671,13 @@ func testNPLChangePortRangeAgentRestart(t *testing.T, data *TestData) {
 	for _, testPodName := range testPods {
 		nplAnnotations, testPodIP := getNPLAnnotations(t, data, r, testPodName, nil)
 		for i := range nplAnnotations {
-			for j := range nplAnnotations[i].Protocols {
-				rule := nplRuleData{
-					nodePort: nplAnnotations[i].NodePort,
-					podIP:    testPodIP,
-					podPort:  nplAnnotations[i].PodPort,
-					protocol: nplAnnotations[i].Protocols[j],
-				}
-				rules = append(rules, rule)
+			rule := nplRuleData{
+				nodePort: nplAnnotations[i].NodePort,
+				podIP:    testPodIP,
+				podPort:  nplAnnotations[i].PodPort,
+				protocol: nplAnnotations[i].Protocol,
 			}
+			rules = append(rules, rule)
 		}
 	}
 	configureNPLForAgent(t, data, updatedStartPort, updatedEndPort)