Repository: envoyproxy/gateway

Issue:
BackendTrafficPolicy & SecurityPolicy targetSelectors selects resources across namespaces
*Description*:
We are seeing conflict between namespaced `BackendTrafficPolicies` due to them targeting (incorrectly) HTTPRoutes across namespaces. We are using `ReferenceGrants` allowing targeting/using services in our primary namespace from the beta namespaces for `HTTPRoute`s and `SecurityPolicy`, however the `BackendTrafficPolicy`, and `SecurityPolicy` both use `targetSelectors` on `HTTPRoutes` which should only allow targeting the same namespace. 

https://gateway.envoyproxy.io/docs/concepts/gateway_api_extensions/backend-traffic-policy/#targets
https://gateway.envoyproxy.io/docs/concepts/gateway_api_extensions/security-policy/#targets
> Important: A SecurityPolicy can only target resources in the same namespace as the policy itself.

*Repro steps*:
```
# namespace: example-web-beta-242
> kubectl get backendtrafficpolicies www-proxy-replacement-beta-dynamic-retry -oyaml
NAME                                       AGE
www-proxy-replacement-beta-dynamic-retry   19m

> kubectl get backendtrafficpolicies www-proxy-replacement-beta-dynamic-retry -oyaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  annotations:
    argocd.argoproj.io/tracking-id: example-web-beta-242:gateway.envoyproxy.io/BackendTrafficPolicy:example-web-beta-242/www-proxy-replacement-beta-dynamic-retry
  name: www-proxy-replacement-beta-dynamic-retry
  namespace: example-web-beta-242
spec:
  healthCheck:
    passive:
      baseEjectionTime: 15s
      consecutive5XxErrors: 5
      consecutiveGatewayErrors: 5
      consecutiveLocalOriginFailures: 5
      interval: 3s
      maxEjectionPercent: 10
      splitExternalLocalOriginErrors: false
  retry:
    numRetries: 2
    perRetry:
      backOff:
        baseInterval: 100ms
        maxInterval: 10s
    retryOn:
      triggers:
      - connect-failure
      - gateway-error
      - refused-stream
      - reset
  targetSelectors:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    matchLabels:
      envoy-retry: www-proxy-replacement-beta-dynamic
  timeout:
    http:
      requestTimeout: 30s
    tcp:
      connectTimeout: 2s
status:
  ancestors:
  - ancestorRef:
      group: gateway.networking.k8s.io
      kind: Gateway
      name: external-iap
      namespace: envoy-gateway-system
    conditions:
    - lastTransitionTime: "2025-09-04T08:58:59Z"
      message: Unable to target HTTPRoute www-proxy-replacement-beta-dynamic-marketing-api-external-iap,
        another BackendTrafficPolicy has already attached to it
      observedGeneration: 1
      reason: Conflicted
      status: "False"
      type: Accepted
    controllerName: gateway.envoyproxy.io/gatewayclass-controller

> kubectl get referencegrant -A
NAMESPACE            NAME                   AGE
example-production   example-web-beta-104   9d
example-production   example-web-beta-108   9d
example-production   example-web-beta-110   9d
example-production   example-web-beta-111   9d
example-production   example-web-beta-122   9d
example-production   example-web-beta-123   9d
example-production   example-web-beta-139   9d
example-production   example-web-beta-140   9d
example-production   example-web-beta-145   9d
example-production   example-web-beta-146   9d
example-production   example-web-beta-154   9d
example-production   example-web-beta-174   9d
example-production   example-web-beta-185   8d
example-production   example-web-beta-191   7d19h
example-production   example-web-beta-193   7d1h
example-production   example-web-beta-195   7d
example-production   example-web-beta-207   2d21h
example-production   example-web-beta-214   47h
example-production   example-web-beta-215   47h
example-production   example-web-beta-216   47h
example-production   example-web-beta-222   42h
example-production   example-web-beta-224   25h
example-production   example-web-beta-227   24h
example-production   example-web-beta-231   23h
example-production   example-web-beta-232   23h
example-production   example-web-beta-233   23h
example-production   example-web-beta-235   22h
example-production   example-web-beta-237   20h
example-production   example-web-beta-240   18h
example-production   example-web-beta-242   115m
example-production   example-web-beta-243   53m
example-production   example-web-beta-63    9d
example-production   example-web-beta-64    9d
example-production   example-web-beta-94    9d
example-production   example-web-beta-99    9d

> kubectl get referencegrant -n example-production example-web-beta-242 -oyaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  annotations:
    argocd.argoproj.io/tracking-id: example-web-beta-242:gateway.networking.k8s.io/ReferenceGrant:example-production/example-web-beta-242
  name: example-web-beta-242
  namespace: example-production
spec:
  from:
  - group: gateway.envoyproxy.io
    kind: SecurityPolicy
    namespace: example-web-beta-242
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: example-web-beta-242
  to:
  - group: ""
    kind: Service
	
❯ egctl x status backendtrafficpolicy -A
NAMESPACE              NAME                                       ANCESTOR REFERENCE         TYPE       STATUS    REASON
asimov                 asimov                                     gateway/external-iap       Accepted   True      Accepted
mail-template-editor   mail-template-editor                       gateway/external-iap       Accepted   True      Accepted
example-production     fraudulent-listings-api                    gateway/cluster-internal   Accepted   True      Accepted
example-production     kibana-myexample                           gateway/external-iap       Accepted   True      Accepted
example-production     logviewer                                  gateway/external-iap       Accepted   True      Accepted
example-production     similar-items-recs-api                     gateway/cluster-internal   Accepted   True      Accepted
example-production     webapi                                     gateway/cluster-internal   Accepted   True      Accepted
example-production     www-proxy-replacement-beta-a-retry         gateway/cluster-internal   Accepted   True      Accepted
example-production     www-proxy-replacement-beta-b-retry         gateway/cluster-internal   Accepted   True      Accepted
example-production     www-proxy-replacement-beta-c-retry         gateway/cluster-internal   Accepted   True      Accepted
example-production     www-proxy-replacement-beta-verify-retry    gateway/cluster-internal   Accepted   True      Accepted
example-production     www-proxy-replacement-canary-retry         gateway/cluster-internal   Accepted   True      Accepted
example-production     www-proxy-replacement-retry                gateway/cluster-internal   Accepted   True      Accepted
example-production     you-might-like-recs-api-cluster            gateway/cluster-internal   Accepted   True      Accepted
example-web-beta-104   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-108   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-110   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-111   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-122   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-123   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-139   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-140   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-145   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-146   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-154   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-174   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-185   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-191   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-193   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-195   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-207   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-214   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-215   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-216   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-222   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-224   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-227   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-231   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-232   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-233   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-235   www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-237   www-proxy-replacement-beta-dynamic-retry   gateway/external-iap       Accepted   False     Conflicted
example-web-beta-240   www-proxy-replacement-beta-dynamic-retry   gateway/external-iap       Accepted   False     Conflicted
example-web-beta-242   www-proxy-replacement-beta-dynamic-retry   gateway/external-iap       Accepted   False     Conflicted
example-web-beta-243   www-proxy-replacement-beta-dynamic-retry   gateway/external-iap       Accepted   False     Conflicted
example-web-beta-63    www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-64    www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-94    www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
example-web-beta-99    www-proxy-replacement-beta-dynamic-retry   gateway/cluster-internal   Accepted   False     Conflicted
```

*Environment*:
>Include the environment like gateway version, envoy version and so on.
```
❯ kubectl version
Client Version: v1.32.2
Kustomize Version: v5.5.0
Server Version: v1.32.6-gke.1060000
❯ egctl version
client: v1.5.0
server:
- Name: envoy-gateway-56f7549fb-52l8m
  Namespace: envoy-gateway-system
  envoyGatewayVersion: v1.5.0
  envoyProxyVersion: distroless-v1.35.0
  gatewayAPIVersion: v1.3.1-0.20250527223622-54df0a899c1c
  gitCommitID: c2e5b2e118a4d81db698d22d4ff4ebb0211ec8a2
  golangVersion: go1.24.6
- Name: envoy-gateway-56f7549fb-thwqw
  Namespace: envoy-gateway-system
  envoyGatewayVersion: v1.5.0
  envoyProxyVersion: distroless-v1.35.0
  gatewayAPIVersion: v1.3.1-0.20250527223622-54df0a899c1c
  gitCommitID: c2e5b2e118a4d81db698d22d4ff4ebb0211ec8a2
  golangVersion: go1.24.6
- Name: envoy-gateway-56f7549fb-zk45c
  Namespace: envoy-gateway-system
  envoyGatewayVersion: v1.5.0
  envoyProxyVersion: distroless-v1.35.0
  gatewayAPIVersion: v1.3.1-0.20250527223622-54df0a899c1c
  gitCommitID: c2e5b2e118a4d81db698d22d4ff4ebb0211ec8a2
  golangVersion: go1.24.6
```


Hints from issue comments:
@arkodg: I cannot repro this issue

```
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.in.yaml
new file mode 100644
index 000000000..33be32b9f
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.in.yaml
@@ -0,0 +1,128 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    namespace: envoy-gateway
+    name: gateway-1
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+      allowedRoutes:
+        namespaces:
+          from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-1
+    labels:
+      app: web-service  
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: envoy-gateway
+    name: httproute-2
+    labels:
+      app: web-service  
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-2
+        namespace: envoy-gateway
+        port: 8080
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    namespace: envoy-gateway
+    name: policy-for-route-2
+  spec:
+    targetSelectors:
+    - group: gateway.networking.k8s.io
+      kind: HTTPRoute 
+      matchLabels:
+        app: web-service    
+    useClientProtocol: true
+services:
+- apiVersion: v1
+  kind: Service
+  metadata:
+    namespace: default 
+    name: service-1
+  spec:
+    ports:
+    - port: 8080
+      name: http
+      protocol: TCP
+- apiVersion: v1
+  kind: Service
+  metadata:
+    namespace: envoy-gateway
+    name: service-2
+  spec:
+    ports:
+    - port: 8080
+      name: http
+      protocol: TCP
+endpointSlices:
+- apiVersion: discovery.k8s.io/v1
+  kind: EndpointSlice
+  metadata:
+    name: endpointslice-service-1
+    namespace: default
+    labels:
+      kubernetes.io/service-name: service-1 
+  addressType: IPv4
+  ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+  endpoints:
+  - addresses:
+    - 8.8.8.8
+    conditions:
+      ready: true
+- apiVersion: discovery.k8s.io/v1
+  kind: EndpointSlice
+  metadata:
+    name: endpointslice-service-2
+    namespace: envoy-gateway
+    labels:
+      kubernetes.io/service-name: service-2
+  addressType: IPv4
+  ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+  endpoints:
+  - addresses:
+    - 8.8.8.8
+    conditions:
+      ready: true
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml
new file mode 100644
index 000000000..4bc9c5e51
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml
@@ -0,0 +1,278 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    creationTimestamp: null
+    name: policy-for-route-2
+    namespace: envoy-gateway
+  spec:
+    targetSelectors:
+    - group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      matchLabels:
+        app: web-service
+    useClientProtocol: true
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-1
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 2
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    labels:
+      app: web-service
+    name: httproute-1
+    namespace: default
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-1
+        port: 8080
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    labels:
+      app: web-service
+    name: httproute-2
+    namespace: envoy-gateway
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-2
+        namespace: envoy-gateway
+        port: 8080
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+infraIR:
+  envoy-gateway/gateway-1:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-1/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+        ownerReference:
+          kind: GatewayClass
+          name: envoy-gateway-class
+      name: envoy-gateway/gateway-1
+      namespace: envoy-gateway-system
+xdsIR:
+  envoy-gateway/gateway-1:
+    accessLog:
+      json:
+      - path: /dev/stdout
+    globalResources:
+      proxyServiceCluster:
+        name: envoy-gateway/gateway-1
+        settings:
+        - addressType: IP
+          endpoints:
+          - host: 7.6.5.4
+            port: 8080
+            zone: zone1
+          metadata:
+            name: envoy-envoy-gateway-gateway-1-196ae069
+            namespace: envoy-gateway-system
+            sectionName: "8080"
+          name: envoy-gateway/gateway-1
+          protocol: TCP
+    http:
+    - address: 0.0.0.0
+      externalPort: 80
+      hostnames:
+      - '*'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-1/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+      routes:
+      - destination:
+          metadata:
+            kind: HTTPRoute
+            name: httproute-1
+            namespace: default
+          name: httproute/default/httproute-1/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 8.8.8.8
+              port: 8080
+            - host: 7.7.7.7
+              port: 8080
+            metadata:
+              kind: Service
+              name: service-1
+              namespace: default
+              sectionName: "8080"
+            name: httproute/default/httproute-1/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute-1
+          namespace: default
+        name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
+      - destination:
+          metadata:
+            kind: HTTPRoute
+            name: httproute-2
+            namespace: envoy-gateway
+          name: httproute/envoy-gateway/httproute-2/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 8.8.8.8
+              port: 8080
+            metadata:
+              kind: Service
+              name: service-2
+              namespace: envoy-gateway
+              sectionName: "8080"
+            name: httproute/envoy-gateway/httproute-2/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute-2
+          namespace: envoy-gateway
+        name: httproute/envoy-gateway/httproute-2/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
+        traffic: {}
+        useClientProtocol: true
+    readyListener:
+      address: 0.0.0.0
+      ipFamily: IPv4
+      path: /ready
```

@BadLiveware: Trying to get a minimal reproduction going I got an even stranger result(at least to me), now we have a conflict with a single BackendTrafficPolicy deployed in a kind cluster:

```
❯ egctl x status all -A
NAME                   TYPE       STATUS    REASON
gatewayclass/cluster   Accepted   True      Accepted

NAMESPACE              NAME              TYPE         STATUS    REASON
envoy-gateway-system   gateway/gateway   Programmed   True      Programmed
                                         Accepted     True      Accepted

NAMESPACE       NAME                    PARENT            TYPE           STATUS    REASON
echo-server-1   httproute/echo-server   gateway/gateway   ResolvedRefs   True      ResolvedRefs
                                                          Accepted       True      Accepted
echo-server-2   httproute/echo-server   gateway/gateway   ResolvedRefs   True      ResolvedRefs
                                                          Accepted       True      Accepted

❯ kubectl apply -f ~/code/crossnsrepro.yaml
envoyproxy.gateway.envoyproxy.io/cluster unchanged
gatewayclass.gateway.networking.k8s.io/cluster unchanged
gateway.gateway.networking.k8s.io/gateway unchanged
httproute.gateway.networking.k8s.io/echo-server configured
httproute.gateway.networking.k8s.io/echo-server configured
backendtrafficpolicy.gateway.envoyproxy.io/policy-for-route-1 created

❯ egctl x status all -A
NAME                   TYPE       STATUS    REASON
gatewayclass/cluster   Accepted   True      Accepted

NAMESPACE              NAME              TYPE         STATUS    REASON
envoy-gateway-system   gateway/gateway   Programmed   True      Programmed
                                         Accepted     True      Accepted

NAMESPACE       NAME                    PARENT            TYPE           STATUS    REASON
echo-server-1   httproute/echo-server   gateway/gateway   ResolvedRefs   True      ResolvedRefs
                                                          Accepted       True      Accepted
echo-server-2   httproute/echo-server   gateway/gateway   ResolvedRefs   True      ResolvedRefs
                                                          Accepted       True      Accepted

NAMESPACE       NAME                                      ANCESTOR REFERENCE   TYPE       STATUS    REASON
echo-server-1   backendtrafficpolicy/policy-for-route-1   gateway/gateway      Accepted   False     Conflicted

❯ kubectl get backendtrafficpolicies policy-for-route-1 -oyaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"gateway.envoyproxy.io/v1alpha1","kind":"BackendTrafficPolicy","metadata":{"annotations":{},"name":"policy-for-route-1","namespace":"echo-server-1"},"spec":{"targetSelectors":[{"group":"gateway.networking.k8s.io","kind":"HTTPRoute","matchLabels":{"app":"web-service"}}],"useClientProtocol":true}}
  creationTimestamp: "2025-09-05T07:51:04Z"
  generation: 1
  name: policy-for-route-1
  namespace: echo-server-1
  resourceVersion: "5065"
  uid: e9965db1-829b-43ac-9b85-a9036eccbf55
spec:
  targetSelectors:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    matchLabels:
      app: web-service
  useClientProtocol: true
status:
  ancestors:
  - ancestorRef:
      group: gateway.networking.k8s.io
      kind: Gateway
      name: gateway
      namespace: envoy-gateway-system
    conditions:
    - lastTransitionTime: "2025-09-05T07:51:04Z"
      message: Unable to target HTTPRoute echo-server, another BackendTrafficPolicy
        has already attached to it
      observedGeneration: 1
      reason: Conflicted
      status: "False"
      type: Accepted
    controllerName: gateway.envoyproxy.io/gatewayclass-controller
```

deployed app x2 in different namespaces, `echo-server-1`, `echo-server-2`
```
---
# Source: ealenn/charts/echo-server/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: k8s-echo-server
  labels:
    app.kubernetes.io/name: echo-server
    helm.sh/chart: echo-server-0.5.0
    app.kubernetes.io/instance: k8s
    app.kubernetes.io/version: "0.6.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: ealenn/charts/echo-server/templates/configmap.yaml
apiVersion:  v1
kind: ConfigMap
metadata: 
  name: k8s-echo-server
data: 
    PORT: "80"
    LOGS__IGNORE__PING: "true"
    ENABLE__HOST: "true"
    ENABLE__HTTP: "true"
    ENABLE__REQUEST: "true"
    ENABLE__COOKIES: "true"
    ENABLE__ENVIRONMENT: "true"
    ENABLE__FILE: "true"
    ENABLE__HEADER: "true"
---
# Source: ealenn/charts/echo-server/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: k8s-echo-server
  labels:
    app.kubernetes.io/name: echo-server
    helm.sh/chart: echo-server-0.5.0
    app.kubernetes.io/instance: k8s
    app.kubernetes.io/version: "0.6.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: echo-server
    app.kubernetes.io/instance: k8s
---
# Source: ealenn/charts/echo-server/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-echo-server
  labels:
    app.kubernetes.io/name: echo-server
    helm.sh/chart: echo-server-0.5.0
    app.kubernetes.io/instance: k8s
    app.kubernetes.io/version: "0.6.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: echo-server
      app.kubernetes.io/instance: k8s
  template:
    metadata:
      annotations:
        checksum/config: 4e1a19a7047808d712d08702916217a96656c4b4ac963538e440f4ebd68c5a02
      labels:
        app.kubernetes.io/name: echo-server
        app.kubernetes.io/instance: k8s
    spec:
      serviceAccountName: k8s-echo-server
      securityContext:
        {}
      containers:
        - name: echo-server
          securityContext:
            {}
          image: "ealen/echo-server:0.6.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          envFrom:
            - configMapRef:
                name: k8s-echo-server
          livenessProbe:
            httpGet:
                path: /ping
                httpHeaders:
                - name: x-echo-code
                  value: "200"
                port: http
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3
            successThreshold: 1
          resources:
            limits:
              cpu: 50m
              memory: 128Mi
            requests:
              cpu: 50m
              memory: 128Mi
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - preference:
              matchExpressions:
              - key: lifecycle
                operator: In
                values:
                - unstable
            weight: 10
      tolerations:
        - effect: NoSchedule
          key: lifecycle
          operator: Equal
          value: unstable
```


repro envoy setup:
```
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: cluster
  namespace: envoy-gateway-system
spec:
  logging:
    level:
      default: debug
  provider:
    kubernetes:
      envoyDeployment:
        name: cluster
        strategy:
          rollingUpdate:
            maxSurge: 1
            maxUnavailable: 0
          type: RollingUpdate
      envoyService:
        annotations:
          networking.gke.io/internal-load-balancer-allow-global-access: "true"
          networking.gke.io/load-balancer-type: Internal
          service.kubernetes.io/topology-mode: auto
        externalTrafficPolicy: Local
        name: cluster
        type: ClusterIP
    type: Kubernetes
  shutdown:
    drainTimeout: 60s
  telemetry:
    accessLog:
      disable: false
      settings:
      - format:
          json:
            bytes_received: '%BYTES_RECEIVED%'
            downstream_local_address: '%DOWNSTREAM_LOCAL_ADDRESS%'
            downstream_remote_address: '%DOWNSTREAM_REMOTE_ADDRESS%'
            duration: '%DURATION%ms'
            logging.googleapis.com/spanId: '%REQ(UNIQUE_ID)%'
            logging.googleapis.com/trace: '%REQ(TRACE_ID)%'
            logging.googleapis.com/trace_sampled: "true"
            remoteIp: '%DOWNSTREAM_REMOTE_ADDRESS%'
            requestMethod: '%REQ(:METHOD)%'
            requestUrl: '%REQ(:AUTHORITY?)%%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%'
            requested_server_name: '%REQUESTED_SERVER_NAME%'
            response_code_details: '%RESPONSE_CODE_DETAILS%'
            response_flags: '%RESPONSE_FLAGS%'
            responseSize: '%BYTES_SENT%'
            route_name: '%ROUTE_NAME%'
            serverIp: '%UPSTREAM_HOST%'
            severity: INFO
            status: '%RESPONSE_CODE%'
            timestamp: '%START_TIME(%Y-%m-%dT%H:%M:%S.%NZ)%'
            upstream_cluster: '%UPSTREAM_CLUSTER%'
            upstream_duration: '%COMMON_DURATION(US_TX_BEG:US_RX_END:ms)%'
            upstream_local_address: '%UPSTREAM_LOCAL_ADDRESS%'
            upstream_transport_failure_reason: '%UPSTREAM_TRANSPORT_FAILURE_REASON%'
            userAgent: '%REQ(USER-AGENT)%'
            x-envoy-upstream-service-time: '%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%'
            x-forwarded-for: '%REQ(X-FORWARDED-FOR)%'
          type: JSON
        matches:
        - response.code >= 500 && response.code < 600
        sinks:
        - file:
            path: /dev/stdout
          type: File
...
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: cluster
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    name: cluster
    namespace: envoy-gateway-system
...
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
  namespace: envoy-gateway-system
spec:
  gatewayClassName: cluster
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All
...
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo-server
  namespace: echo-server-1
  labels:
    app: web-service  
spec:
  hostnames:
  - echo-server-1.example.org
  parentRefs:
  - namespace: envoy-gateway-system
    name: gateway
  rules:
  - matches:
    - path:
        value: "/"
    backendRefs:
    - name: k8s-echo-server
      port: 80
...
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo-server
  namespace: echo-server-2
  labels:
    app: web-service  
spec:
  hostnames:
  - echo-server-2.example.org
  parentRefs:
  - namespace: envoy-gateway-system
    name: gateway
  rules:
  - matches:
    - path:
        value: "/"
    backendRefs:
    - name: k8s-echo-server
      port: 80
...
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: policy-for-route-1
  namespace: echo-server-1
spec:
  targetSelectors:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute 
    matchLabels:
      app: web-service
  useClientProtocol: true
...
```

[logs.txt](https://github.com/user-attachments/files/22167642/logs.txt)@arkodg: I can't repro, but raised #6917 to improve targetRef selection which can affect policy status@github-actions: This issue has been automatically marked as stale because it has not had activity in the last 30 days.@arkodg: @BadLiveware are you still seeing this on v1.5.2 ?@BadLiveware: > [@BadLiveware](https://github.com/BadLiveware) are you still seeing this on v1.5.2 ?

This does seem fixed, thank you!

Solution patch:
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 494ef2d3d..17b7ce138 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -48,7 +48,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 	routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{}
 	for _, route := range routes {
 		key := policyTargetRouteKey{
-			Kind:      string(GetRouteType(route)),
+			Kind:      string(route.GetRouteType()),
 			Name:      route.GetName(),
 			Namespace: route.GetNamespace(),
 		}
@@ -77,7 +77,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 	// TODO: This loop is similar to the one 'Process the policies targeting Gateways', we may want to
 	// merge them into one if possible.
 	for _, currPolicy := range backendTrafficPolicies {
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind == resource.KindGateway {
 				// Check if the gateway exists
@@ -104,7 +104,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 	// Process the policies targeting xRoutes
 	for _, currPolicy := range backendTrafficPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind != resource.KindGateway {
 				policy, found := handledPolicies[policyName]
@@ -232,7 +232,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 	// Process the policies targeting Gateways
 	for _, currPolicy := range backendTrafficPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind == resource.KindGateway {
 				policy, found := handledPolicies[policyName]
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index f3f9f0f09..53cb8538b 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -163,7 +163,7 @@ func (t *Translator) ProcessClientTrafficPolicies(
 	// Policy with no section set (targeting all sections)
 	for _, currPolicy := range clientTrafficPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if !hasSectionName(&currTarget) {
 
diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go
index f09c4e4d3..f9e8fb7d9 100644
--- a/internal/gatewayapi/contexts.go
+++ b/internal/gatewayapi/contexts.go
@@ -148,140 +148,315 @@ func (l *ListenerContext) SetTLSSecrets(tlsSecrets []*corev1.Secret) {
 // that can reference Gateway objects.
 type RouteContext interface {
 	client.Object
+	GetRouteType() gwapiv1.Kind
+	HasRuleNames(sectionName gwapiv1.SectionName) bool
+	GetHostnames() []string
+	GetParentReferences() []gwapiv1.ParentReference
+	GetRouteStatus() *gwapiv1.RouteStatus
+	GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext
+	SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext)
 }
 
 // HTTPRouteContext wraps an HTTPRoute and provides helper methods for
 // accessing the route's parents.
 type HTTPRouteContext struct {
-	// GatewayControllerName is the name of the Gateway API controller.
-	GatewayControllerName string
-
 	*gwapiv1.HTTPRoute
 
 	ParentRefs map[gwapiv1.ParentReference]*RouteParentContext
 }
 
+func (r *HTTPRouteContext) GetRouteType() gwapiv1.Kind {
+	return resource.KindHTTPRoute
+}
+
+func (r *HTTPRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool {
+	rs := r.Spec.Rules
+	for _, rule := range rs {
+		if rule.Name != nil {
+			if *rule.Name == sectionName {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (r *HTTPRouteContext) GetHostnames() []string {
+	hs := r.Spec.Hostnames
+	hostnames := make([]string, len(hs))
+	for i := range hs {
+		hostnames[i] = string(hs[i])
+	}
+	return hostnames
+}
+
+func (r *HTTPRouteContext) GetParentReferences() []gwapiv1.ParentReference {
+	return r.Spec.ParentRefs
+}
+
+func (r *HTTPRouteContext) GetRouteStatus() *gwapiv1.RouteStatus {
+	return &r.Status.RouteStatus
+}
+
+func (r *HTTPRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	return r.ParentRefs[forParentRef]
+}
+
+func (r *HTTPRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	r.ParentRefs[forParentRef] = ctx
+}
+
 // GRPCRouteContext wraps a GRPCRoute and provides helper methods for
 // accessing the route's parents.
 type GRPCRouteContext struct {
-	// GatewayControllerName is the name of the Gateway API controller.
-	GatewayControllerName string
-
 	*gwapiv1.GRPCRoute
 
 	ParentRefs map[gwapiv1.ParentReference]*RouteParentContext
 }
 
+func (r *GRPCRouteContext) GetRouteType() gwapiv1.Kind {
+	return resource.KindGRPCRoute
+}
+
+func (r *GRPCRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool {
+	rs := r.Spec.Rules
+	for _, rule := range rs {
+		if rule.Name != nil {
+			if *rule.Name == sectionName {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (r *GRPCRouteContext) GetHostnames() []string {
+	hs := r.Spec.Hostnames
+	hostnames := make([]string, len(hs))
+	for i := range hs {
+		hostnames[i] = string(hs[i])
+	}
+	return hostnames
+}
+
+func (r *GRPCRouteContext) GetParentReferences() []gwapiv1.ParentReference {
+	return r.Spec.ParentRefs
+}
+
+func (r *GRPCRouteContext) GetRouteStatus() *gwapiv1.RouteStatus {
+	return &r.Status.RouteStatus
+}
+
+func (r *GRPCRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	return r.ParentRefs[forParentRef]
+}
+
+func (r *GRPCRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	r.ParentRefs[forParentRef] = ctx
+}
+
 // TLSRouteContext wraps a TLSRoute and provides helper methods for
 // accessing the route's parents.
 type TLSRouteContext struct {
-	// GatewayControllerName is the name of the Gateway API controller.
-	GatewayControllerName string
-
 	*gwapiv1a2.TLSRoute
 
 	ParentRefs map[gwapiv1.ParentReference]*RouteParentContext
 }
 
+func (r *TLSRouteContext) GetRouteType() gwapiv1.Kind {
+	return resource.KindTLSRoute
+}
+
+func (r *TLSRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool {
+	rs := r.Spec.Rules
+	for _, rule := range rs {
+		if rule.Name != nil {
+			if *rule.Name == sectionName {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (r *TLSRouteContext) GetHostnames() []string {
+	hs := r.Spec.Hostnames
+	hostnames := make([]string, len(hs))
+	for i := range hs {
+		hostnames[i] = string(hs[i])
+	}
+	return hostnames
+}
+
+func (r *TLSRouteContext) GetParentReferences() []gwapiv1.ParentReference {
+	return r.Spec.ParentRefs
+}
+
+func (r *TLSRouteContext) GetRouteStatus() *gwapiv1.RouteStatus {
+	return &r.Status.RouteStatus
+}
+
+func (r *TLSRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	return r.ParentRefs[forParentRef]
+}
+
+func (r *TLSRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	r.ParentRefs[forParentRef] = ctx
+}
+
 // UDPRouteContext wraps a UDPRoute and provides helper methods for
 // accessing the route's parents.
 type UDPRouteContext struct {
-	// GatewayControllerName is the name of the Gateway API controller.
-	GatewayControllerName string
-
 	*gwapiv1a2.UDPRoute
 
 	ParentRefs map[gwapiv1.ParentReference]*RouteParentContext
 }
 
+func (r *UDPRouteContext) GetRouteType() gwapiv1.Kind {
+	return resource.KindUDPRoute
+}
+
+func (r *UDPRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool {
+	rs := r.Spec.Rules
+	for _, rule := range rs {
+		if rule.Name != nil {
+			if *rule.Name == sectionName {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (r *UDPRouteContext) GetHostnames() []string {
+	// UDPRoute doesn't have hostnames, return empty slice
+	return []string{}
+}
+
+func (r *UDPRouteContext) GetParentReferences() []gwapiv1.ParentReference {
+	return r.Spec.ParentRefs
+}
+
+func (r *UDPRouteContext) GetRouteStatus() *gwapiv1.RouteStatus {
+	return &r.Status.RouteStatus
+}
+
+func (r *UDPRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	return r.ParentRefs[forParentRef]
+}
+
+func (r *UDPRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
+	}
+	r.ParentRefs[forParentRef] = ctx
+}
+
+func (r *UDPRouteContext) GetParentRefs() map[gwapiv1.ParentReference]*RouteParentContext {
+	return r.ParentRefs
+}
+
 // TCPRouteContext wraps a TCPRoute and provides helper methods for
 // accessing the route's parents.
 type TCPRouteContext struct {
-	// GatewayControllerName is the name of the Gateway API controller.
-	GatewayControllerName string
-
 	*gwapiv1a2.TCPRoute
 
 	ParentRefs map[gwapiv1.ParentReference]*RouteParentContext
 }
 
-// GetRouteType returns the Kind of the Route object, HTTPRoute,
-// TLSRoute, TCPRoute, UDPRoute etc.
-func GetRouteType(route RouteContext) gwapiv1.Kind {
-	rv := reflect.ValueOf(route).Elem()
-	return gwapiv1.Kind(rv.FieldByName("Kind").String())
+func (r *TCPRouteContext) GetRouteType() gwapiv1.Kind {
+	return resource.KindTCPRoute
 }
 
-// GetRuleNames returns the rule names targeted by the Route object.
-func GetRuleNames(route RouteContext) []gwapiv1.SectionName {
-	rv := reflect.ValueOf(route).Elem()
-
-	rs := rv.FieldByName("Spec").FieldByName("Rules")
-	ruleNames := make([]gwapiv1.SectionName, 0, rs.Len())
-	for i := 0; i < rs.Len(); i++ {
-		nameField := rs.Index(i).FieldByName("Name")
-		if !nameField.IsNil() {
-			ruleNames = append(ruleNames, nameField.Elem().Interface().(gwapiv1.SectionName))
+func (r *TCPRouteContext) HasRuleNames(sectionName gwapiv1.SectionName) bool {
+	rs := r.Spec.Rules
+	for _, rule := range rs {
+		if rule.Name != nil {
+			if *rule.Name == sectionName {
+				return true
+			}
 		}
 	}
-	return ruleNames
+	return false
 }
 
-// GetHostnames returns the hosts targeted by the Route object.
-func GetHostnames(route RouteContext) []string {
-	rv := reflect.ValueOf(route).Elem()
-	kind := rv.FieldByName("Kind").String()
-	if kind == resource.KindTCPRoute || kind == resource.KindUDPRoute {
-		return nil
+func (r *TCPRouteContext) GetHostnames() []string {
+	// TCPRoute doesn't have hostnames, return empty slice
+	return []string{}
+}
+
+func (r *TCPRouteContext) GetParentReferences() []gwapiv1.ParentReference {
+	return r.Spec.ParentRefs
+}
+
+func (r *TCPRouteContext) GetRouteStatus() *gwapiv1.RouteStatus {
+	return &r.Status.RouteStatus
+}
+
+func (r *TCPRouteContext) GetRouteParentContext(forParentRef gwapiv1.ParentReference) *RouteParentContext {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
 	}
+	return r.ParentRefs[forParentRef]
+}
 
-	hs := rv.FieldByName("Spec").FieldByName("Hostnames")
-	hostnames := make([]string, hs.Len())
-	for i := 0; i < len(hostnames); i++ {
-		hostnames[i] = hs.Index(i).String()
+func (r *TCPRouteContext) SetRouteParentContext(forParentRef gwapiv1.ParentReference, ctx *RouteParentContext) {
+	if r.ParentRefs == nil {
+		r.ParentRefs = make(map[gwapiv1.ParentReference]*RouteParentContext)
 	}
-	return hostnames
+	r.ParentRefs[forParentRef] = ctx
+}
+
+// GetHostnames returns the hosts targeted by the Route object.
+func GetHostnames(route RouteContext) []string {
+	return route.GetHostnames()
 }
 
 // GetParentReferences returns the ParentReference of the Route object.
 func GetParentReferences(route RouteContext) []gwapiv1.ParentReference {
-	rv := reflect.ValueOf(route).Elem()
-	pr := rv.FieldByName("Spec").FieldByName("ParentRefs")
-	return pr.Interface().([]gwapiv1.ParentReference)
+	return route.GetParentReferences()
 }
 
 // GetRouteStatus returns the RouteStatus object associated with the Route.
 func GetRouteStatus(route RouteContext) *gwapiv1.RouteStatus {
-	rv := reflect.ValueOf(route).Elem()
-	rs := rv.FieldByName("Status").FieldByName("RouteStatus").Interface().(gwapiv1.RouteStatus)
-	return &rs
+	return route.GetRouteStatus()
 }
 
 // GetRouteParentContext returns RouteParentContext by using the Route objects' ParentReference.
 // It creates a new RouteParentContext and add a new RouteParentStatus to the Route's Status if the ParentReference is not found.
-func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentReference) *RouteParentContext {
-	rv := reflect.ValueOf(route).Elem()
-	pr := rv.FieldByName("ParentRefs")
-
-	// If the ParentRefs field is nil, initialize it.
-	if pr.IsNil() {
-		mm := reflect.MakeMap(reflect.TypeOf(map[gwapiv1.ParentReference]*RouteParentContext{}))
-		pr.Set(mm)
-	}
-
+func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentReference, controllerName string) *RouteParentContext {
 	// If the RouteParentContext is already in the RouteContext, return it.
-	if p := pr.MapIndex(reflect.ValueOf(forParentRef)); p.IsValid() && !p.IsZero() {
-		ctx := p.Interface().(*RouteParentContext)
-		return ctx
+	if existingCtx := route.GetRouteParentContext(forParentRef); existingCtx != nil {
+		return existingCtx
 	}
 
 	// Verify that the ParentReference is present in the Route.Spec.ParentRefs.
 	// This is just a sanity check, the parentRef should always be present, otherwise it's a programming error.
 	var parentRef *gwapiv1.ParentReference
-	specParentRefs := rv.FieldByName("Spec").FieldByName("ParentRefs")
-	for i := 0; i < specParentRefs.Len(); i++ {
-		p := specParentRefs.Index(i).Interface().(gwapiv1.ParentReference)
-		if reflect.DeepEqual(p, forParentRef) {
+	specParentRefs := route.GetParentReferences()
+	for _, p := range specParentRefs {
+		if isParentRefEqual(p, forParentRef, route.GetNamespace()) {
 			parentRef = &p
 			break
 		}
@@ -292,11 +467,10 @@ func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentRefere
 
 	// Find the parent in the Route's Status.
 	routeParentStatusIdx := -1
-	statusParents := rv.FieldByName("Status").FieldByName("Parents")
+	routeStatus := route.GetRouteStatus()
 
-	for i := 0; i < statusParents.Len(); i++ {
-		p := statusParents.Index(i).FieldByName("ParentRef").Interface().(gwapiv1.ParentReference)
-		if isParentRefEqual(p, *parentRef, route.GetNamespace()) {
+	for i, parent := range routeStatus.Parents {
+		if isParentRefEqual(parent.ParentRef, *parentRef, route.GetNamespace()) {
 			routeParentStatusIdx = i
 			break
 		}
@@ -305,11 +479,11 @@ func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentRefere
 	// If the parent is not found in the Route's Status, create a new RouteParentStatus and add it to the Route's Status.
 	if routeParentStatusIdx == -1 {
 		rParentStatus := gwapiv1a2.RouteParentStatus{
-			ControllerName: gwapiv1a2.GatewayController(rv.FieldByName("GatewayControllerName").String()),
+			ControllerName: gwapiv1a2.GatewayController(controllerName),
 			ParentRef:      forParentRef,
 		}
-		statusParents.Set(reflect.Append(statusParents, reflect.ValueOf(rParentStatus)))
-		routeParentStatusIdx = statusParents.Len() - 1
+		routeStatus.Parents = append(routeStatus.Parents, rParentStatus)
+		routeParentStatusIdx = len(routeStatus.Parents) - 1
 	}
 
 	// Also add the RouteParentContext to the RouteContext.
@@ -317,9 +491,22 @@ func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentRefere
 		ParentReference:      parentRef,
 		routeParentStatusIdx: routeParentStatusIdx,
 	}
-	rctx := reflect.ValueOf(ctx)
-	rctx.Elem().FieldByName(string(GetRouteType(route))).Set(rv.Field(1))
-	pr.SetMapIndex(reflect.ValueOf(forParentRef), rctx)
+
+	// Set the appropriate route field based on the route type
+	switch route.GetRouteType() {
+	case resource.KindHTTPRoute:
+		ctx.HTTPRoute = route.(*HTTPRouteContext).HTTPRoute
+	case resource.KindGRPCRoute:
+		ctx.GRPCRoute = route.(*GRPCRouteContext).GRPCRoute
+	case resource.KindTLSRoute:
+		ctx.TLSRoute = route.(*TLSRouteContext).TLSRoute
+	case resource.KindTCPRoute:
+		ctx.TCPRoute = route.(*TCPRouteContext).TCPRoute
+	case resource.KindUDPRoute:
+		ctx.UDPRoute = route.(*UDPRouteContext).UDPRoute
+	}
+
+	route.SetRouteParentContext(forParentRef, ctx)
 	return ctx
 }
 
diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go
index e942ae699..a2515fe97 100644
--- a/internal/gatewayapi/envoyextensionpolicy.go
+++ b/internal/gatewayapi/envoyextensionpolicy.go
@@ -47,7 +47,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
 	routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{}
 	for _, route := range routes {
 		key := policyTargetRouteKey{
-			Kind:      string(GetRouteType(route)),
+			Kind:      string(route.GetRouteType()),
 			Name:      route.GetName(),
 			Namespace: route.GetNamespace(),
 		}
@@ -75,7 +75,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
 	// Process the policies targeting RouteRules
 	for _, currPolicy := range envoyExtensionPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil {
 				policy, found := handledPolicies[policyName]
@@ -94,7 +94,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
 	// Process the policies targeting xRoutes
 	for _, currPolicy := range envoyExtensionPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil {
 				policy, found := handledPolicies[policyName]
@@ -113,7 +113,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
 	// Process the policies targeting Listeners
 	for _, currPolicy := range envoyExtensionPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil {
 				policy, found := handledPolicies[policyName]
@@ -132,7 +132,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv
 	// Process the policies targeting Gateways
 	for _, currPolicy := range envoyExtensionPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil {
 				policy, found := handledPolicies[policyName]
@@ -466,7 +466,7 @@ func (t *Translator) translateEnvoyExtensionPolicyForRoute(
 	prefix := irRoutePrefix(route)
 	parentRefs := GetParentReferences(route)
 	for _, p := range parentRefs {
-		parentRefCtx := GetRouteParentContext(route, p)
+		parentRefCtx := GetRouteParentContext(route, p, t.GatewayControllerName)
 		gtwCtx := parentRefCtx.GetGateway()
 		if gtwCtx == nil {
 			continue
diff --git a/internal/gatewayapi/extensionserverpolicy.go b/internal/gatewayapi/extensionserverpolicy.go
index d10fb8116..b80c838fa 100644
--- a/internal/gatewayapi/extensionserverpolicy.go
+++ b/internal/gatewayapi/extensionserverpolicy.go
@@ -103,7 +103,7 @@ func extractTargetRefs(policy *unstructured.Unstructured, gateways []*GatewayCon
 	if err := json.Unmarshal(specAsJSON, &targetRefs); err != nil {
 		return nil, fmt.Errorf("no targets found for the policy")
 	}
-	ret := getPolicyTargetRefs(targetRefs, gateways)
+	ret := getPolicyTargetRefs(targetRefs, gateways, policy.GetNamespace())
 	if len(ret) == 0 {
 		return nil, fmt.Errorf("no targets found for the policy")
 	}
diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go
index 4bb313f12..47f0f5569 100644
--- a/internal/gatewayapi/filters.go
+++ b/internal/gatewayapi/filters.go
@@ -946,7 +946,7 @@ func (t *Translator) processRequestMirrorFilter(
 	}
 
 	// Get the route type from the filter context to determine the correct BackendRef type
-	routeType := GetRouteType(filterContext.Route)
+	routeType := filterContext.Route.GetRouteType()
 	weight := int32(1)
 	mirrorBackend := mirrorFilter.BackendRef
 
diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go
index 782db3281..2d479478f 100644
--- a/internal/gatewayapi/helpers.go
+++ b/internal/gatewayapi/helpers.go
@@ -386,7 +386,7 @@ func irListenerPortName(proto ir.ProtocolType, port int32) string {
 func irRoutePrefix(route RouteContext) string {
 	// add a "/" at the end of the prefix to prevent mismatching routes with the
 	// same prefix. For example, route prefix "/foo/" should not match a route "/foobar".
-	return fmt.Sprintf("%s/%s/%s/", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName())
+	return fmt.Sprintf("%s/%s/%s/", strings.ToLower(string(route.GetRouteType())), route.GetNamespace(), route.GetName())
 }
 
 func irRouteName(route RouteContext, ruleIdx, matchIdx int) string {
@@ -394,7 +394,7 @@ func irRouteName(route RouteContext, ruleIdx, matchIdx int) string {
 }
 
 func irTCPRouteName(route RouteContext) string {
-	return fmt.Sprintf("%s/%s/%s", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName())
+	return fmt.Sprintf("%s/%s/%s", strings.ToLower(string(route.GetRouteType())), route.GetNamespace(), route.GetName())
 }
 
 func irUDPRouteName(route RouteContext) string {
@@ -557,7 +557,7 @@ func selectorFromTargetSelector(selector egv1a1.TargetSelector) labels.Selector
 	return l
 }
 
-func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences, potentialTargets []T) []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName {
+func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences, potentialTargets []T, policyNamespace string) []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName {
 	dedup := sets.New[targetRefWithTimestamp]()
 	for _, currSelector := range policy.TargetSelectors {
 		labelSelector := selectorFromTargetSelector(currSelector)
@@ -568,6 +568,11 @@ func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences,
 				continue
 			}
 
+			// Skip objects not in the same namespace as the policy
+			if obj.GetNamespace() != policyNamespace {
+				continue
+			}
+
 			if labelSelector.Matches(labels.Set(obj.GetLabels())) {
 				dedup.Insert(targetRefWithTimestamp{
 					CreationTimestamp: obj.GetCreationTimestamp(),
diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go
index 031764753..62fa944c0 100644
--- a/internal/gatewayapi/route.go
+++ b/internal/gatewayapi/route.go
@@ -60,10 +60,7 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways
 		if h == nil {
 			panic("received nil httproute")
 		}
-		httpRoute := &HTTPRouteContext{
-			GatewayControllerName: t.GatewayControllerName,
-			HTTPRoute:             h.DeepCopy(),
-		}
+		httpRoute := &HTTPRouteContext{HTTPRoute: h.DeepCopy()}
 
 		// Find out if this route attaches to one of our Gateway's listeners,
 		// and if so, get the list of listeners that allow it to attach for each
@@ -90,10 +87,7 @@ func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*gwapiv1.GRPCRoute, gateways
 		if g == nil {
 			panic("received nil grpcroute")
 		}
-		grpcRoute := &GRPCRouteContext{
-			GatewayControllerName: t.GatewayControllerName,
-			GRPCRoute:             g.DeepCopy(),
-		}
+		grpcRoute := &GRPCRouteContext{GRPCRoute: g.DeepCopy()}
 
 		// Find out if this route attaches to one of our Gateway's listeners,
 		// and if so, get the list of listeners that allow it to attach for each
@@ -184,7 +178,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
 		irRoutes []*ir.HTTPRoute
 		errs     = &status.MultiStatusError{}
 	)
-	pattern := getStatPattern(httpRoute, parentRef)
+	pattern := getStatPattern(httpRoute, parentRef, t.GatewayControllerName)
 
 	// process each HTTPRouteRule, generate a unique Xds IR HTTPRoute per match of the rule
 	for ruleIdx, rule := range httpRoute.Spec.Rules {
@@ -638,7 +632,7 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe
 		irRoutes []*ir.HTTPRoute
 		errs     = &status.MultiStatusError{}
 	)
-	pattern := getStatPattern(grpcRoute, parentRef)
+	pattern := getStatPattern(grpcRoute, parentRef, t.GatewayControllerName)
 
 	// compute matches, filters, backends
 	for ruleIdx, rule := range grpcRoute.Spec.Rules {
@@ -878,7 +872,7 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route
 		irListener := xdsIR[irKey].GetHTTPListener(irListenerName(listener))
 
 		if irListener != nil {
-			if GetRouteType(route) == resource.KindGRPCRoute {
+			if route.GetRouteType() == resource.KindGRPCRoute {
 				irListener.IsHTTP2 = true
 			}
 			irListener.Routes = append(irListener.Routes, perHostRoutes...)
@@ -922,10 +916,7 @@ func (t *Translator) ProcessTLSRoutes(tlsRoutes []*gwapiv1a2.TLSRoute, gateways
 		if tls == nil {
 			panic("received nil tlsroute")
 		}
-		tlsRoute := &TLSRouteContext{
-			GatewayControllerName: t.GatewayControllerName,
-			TLSRoute:              tls.DeepCopy(),
-		}
+		tlsRoute := &TLSRouteContext{TLSRoute: tls.DeepCopy()}
 
 		// Find out if this route attaches to one of our Gateway's listeners,
 		// and if so, get the list of listeners that allow it to attach for each
@@ -1069,10 +1060,7 @@ func (t *Translator) ProcessUDPRoutes(udpRoutes []*gwapiv1a2.UDPRoute, gateways
 		if u == nil {
 			panic("received nil udproute")
 		}
-		udpRoute := &UDPRouteContext{
-			GatewayControllerName: t.GatewayControllerName,
-			UDPRoute:              u.DeepCopy(),
-		}
+		udpRoute := &UDPRouteContext{UDPRoute: u.DeepCopy()}
 
 		// Find out if this route attaches to one of our Gateway's listeners,
 		// and if so, get the list of listeners that allow it to attach for each
@@ -1220,10 +1208,7 @@ func (t *Translator) ProcessTCPRoutes(tcpRoutes []*gwapiv1a2.TCPRoute, gateways
 		if tcp == nil {
 			panic("received nil tcproute")
 		}
-		tcpRoute := &TCPRouteContext{
-			GatewayControllerName: t.GatewayControllerName,
-			TCPRoute:              tcp.DeepCopy(),
-		}
+		tcpRoute := &TCPRouteContext{TCPRoute: tcp.DeepCopy()}
 
 		// Find out if this route attaches to one of our Gateway's listeners,
 		// and if so, get the list of listeners that allow it to attach for each
@@ -1379,7 +1364,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour
 func (t *Translator) processDestination(name string, backendRefContext BackendRefContext,
 	parentRef *RouteParentContext, route RouteContext, resources *resource.Resources,
 ) (ds *ir.DestinationSetting, unstructuredRef *ir.UnstructuredRef, err status.Error) {
-	routeType := GetRouteType(route)
+	routeType := route.GetRouteType()
 	weight := uint32(1)
 	backendRef := GetBackendRef(backendRefContext)
 	if backendRef.Weight != nil {
@@ -1403,7 +1388,7 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe
 	}
 
 	var envoyProxy *egv1a1.EnvoyProxy
-	gatewayCtx := GetRouteParentContext(route, *parentRef.ParentReference).GetGateway()
+	gatewayCtx := GetRouteParentContext(route, *parentRef.ParentReference, t.GatewayControllerName).GetGateway()
 	if gatewayCtx != nil {
 		envoyProxy = gatewayCtx.envoyProxy
 	}
@@ -1731,7 +1716,7 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont
 		}
 		relevantRoute = true
 
-		parentRefCtx := GetRouteParentContext(routeContext, parentRef)
+		parentRefCtx := GetRouteParentContext(routeContext, parentRef, t.GatewayControllerName)
 		// Reset conditions since they will be recomputed during translation
 		parentRefCtx.ResetConditions(routeContext)
 
@@ -1750,7 +1735,7 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont
 
 		var allowedListeners []*ListenerContext
 		for _, listener := range selectedListeners {
-			acceptedKind := GetRouteType(routeContext)
+			acceptedKind := routeContext.GetRouteType()
 			if listener.AllowsKind(gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: acceptedKind}) &&
 				listener.AllowsNamespace(resources.GetNamespace(routeContext.GetNamespace())) {
 				allowedListeners = append(allowedListeners, listener)
@@ -2066,10 +2051,10 @@ func backendAppProtocolToIRAppProtocol(ap egv1a1.AppProtocolType, defaultProtoco
 	}
 }
 
-func getStatPattern(routeContext RouteContext, parentRef *RouteParentContext) string {
+func getStatPattern(routeContext RouteContext, parentRef *RouteParentContext, controllerName string) string {
 	var pattern string
 	var envoyProxy *egv1a1.EnvoyProxy
-	gatewayCtx := GetRouteParentContext(routeContext, *parentRef.ParentReference).GetGateway()
+	gatewayCtx := GetRouteParentContext(routeContext, *parentRef.ParentReference, controllerName).GetGateway()
 	if gatewayCtx != nil {
 		envoyProxy = gatewayCtx.envoyProxy
 	}
diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go
index e35127050..1769e3559 100644
--- a/internal/gatewayapi/securitypolicy.go
+++ b/internal/gatewayapi/securitypolicy.go
@@ -64,7 +64,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
 	routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{}
 	for _, route := range routes {
 		key := policyTargetRouteKey{
-			Kind:      string(GetRouteType(route)),
+			Kind:      string(route.GetRouteType()),
 			Name:      route.GetName(),
 			Namespace: route.GetNamespace(),
 		}
@@ -91,7 +91,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
 	// Process the policies targeting RouteRules
 	for _, currPolicy := range securityPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil {
 				policy, found := handledPolicies[policyName]
@@ -109,7 +109,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
 	// Process the policies targeting xRoutes
 	for _, currPolicy := range securityPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil {
 				policy, found := handledPolicies[policyName]
@@ -127,7 +127,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
 	// Process the policies targeting Listeners
 	for _, currPolicy := range securityPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil {
 				policy, found := handledPolicies[policyName]
@@ -145,7 +145,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
 	// Process the policies targeting Gateways
 	for _, currPolicy := range securityPolicies {
 		policyName := utils.NamespacedName(currPolicy)
-		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways)
+		targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
 		for _, currTarget := range targetRefs {
 			if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil {
 				policy, found := handledPolicies[policyName]
@@ -581,7 +581,7 @@ func (t *Translator) translateSecurityPolicyForRoute(
 	prefix := irRoutePrefix(route)
 	parentRefs := GetParentReferences(route)
 	for _, p := range parentRefs {
-		parentRefCtx := GetRouteParentContext(route, p)
+		parentRefCtx := GetRouteParentContext(route, p, t.GatewayControllerName)
 		gtwCtx := parentRefCtx.GetGateway()
 		if gtwCtx == nil {
 			continue
diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go
index 9bbfc240c..8a4edaa46 100644
--- a/internal/gatewayapi/validate.go
+++ b/internal/gatewayapi/validate.go
@@ -1095,14 +1095,7 @@ func validateRouteRuleSectionName(
 	targetKey policyTargetRouteKey,
 	route *policyRouteTargetContext,
 ) *status.PolicyResolveError {
-	found := false
-	for _, name := range GetRuleNames(route.RouteContext) {
-		if name == sectionName {
-			found = true
-			break
-		}
-	}
-	if !found {
+	if !route.HasRuleNames(sectionName) {
 		message := fmt.Sprintf("No section name %s found for %s %s/%s",
 			string(sectionName), targetKey.Kind, targetKey.Namespace, targetKey.Name)
 
diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go
index 492bef67b..b1637bd63 100644
--- a/internal/provider/kubernetes/controller.go
+++ b/internal/provider/kubernetes/controller.go
@@ -1267,7 +1267,7 @@ func (r *gatewayAPIReconciler) processBtpConfigMapRefs(
 ) error {
 	for _, policy := range resourceTree.BackendTrafficPolicies {
 		for _, ro := range policy.Spec.ResponseOverride {
-			if ro.Response.Body != nil && ro.Response.Body.ValueRef != nil && string(ro.Response.Body.ValueRef.Kind) == resource.KindConfigMap {
+			if ro.Response != nil && ro.Response.Body != nil && ro.Response.Body.ValueRef != nil && string(ro.Response.Body.ValueRef.Kind) == resource.KindConfigMap {
 				configMap := new(corev1.ConfigMap)
 				err := r.client.Get(ctx,
 					types.NamespacedName{Namespace: policy.Namespace, Name: string(ro.Response.Body.ValueRef.Name)},
diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go
index 11418f63d..2b9b5efdb 100644
--- a/internal/provider/kubernetes/indexers.go
+++ b/internal/provider/kubernetes/indexers.go
@@ -837,7 +837,7 @@ func configMapBtpIndexFunc(rawObj client.Object) []string {
 	var configMapReferences []string
 
 	for _, ro := range btp.Spec.ResponseOverride {
-		if ro.Response.Body != nil && ro.Response.Body.ValueRef != nil {
+		if ro.Response != nil && ro.Response.Body != nil && ro.Response.Body.ValueRef != nil {
 			if string(ro.Response.Body.ValueRef.Kind) == resource.KindConfigMap {
 				configMapReferences = append(configMapReferences,
 					types.NamespacedName{
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 428d2defd..5cc1b58fe 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -17,6 +17,7 @@ bug fixes: |
   Fixed log formatting of improper key-value pairs to avoid DPANIC in controller-runtime logger.
   Fixed handling of context-related transient errors to prevent incorrect state reconciliation and unintended behavior.
   Fixed the controller cannot read the EnvoyProxy attached gatewayclass only.
+  Fixed indexer and controller crashing when BackendTrafficPolicy has a redirect response override.
 
 # Enhancements that improve performance.
 performance improvements: |



Test patch:
diff --git a/internal/gatewayapi/helpers_test.go b/internal/gatewayapi/helpers_test.go
index 72df6c66c..228daed0d 100644
--- a/internal/gatewayapi/helpers_test.go
+++ b/internal/gatewayapi/helpers_test.go
@@ -581,7 +581,7 @@ func TestGetPolicyTargetRefs(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			results := getPolicyTargetRefs(tc.policy, tc.targets)
+			results := getPolicyTargetRefs(tc.policy, tc.targets, "default")
 			require.ElementsMatch(t, results, tc.results)
 		})
 	}
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.in.yaml
new file mode 100644
index 000000000..9ce7c95d6
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.in.yaml
@@ -0,0 +1,128 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    namespace: envoy-gateway
+    name: gateway-1
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+      allowedRoutes:
+        namespaces:
+          from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute
+    labels:
+      app: web-service
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: envoy-gateway
+    name: httproute
+    labels:
+      app: web-service
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-2
+        namespace: envoy-gateway
+        port: 8080
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    namespace: envoy-gateway
+    name: policy-for-route-in-envoy-gateway-ns
+  spec:
+    targetSelectors:
+    - group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      matchLabels:
+        app: web-service
+    useClientProtocol: true
+services:
+- apiVersion: v1
+  kind: Service
+  metadata:
+    namespace: default
+    name: service-1
+  spec:
+    ports:
+    - port: 8080
+      name: http
+      protocol: TCP
+- apiVersion: v1
+  kind: Service
+  metadata:
+    namespace: envoy-gateway
+    name: service-2
+  spec:
+    ports:
+    - port: 8080
+      name: http
+      protocol: TCP
+endpointSlices:
+- apiVersion: discovery.k8s.io/v1
+  kind: EndpointSlice
+  metadata:
+    name: endpointslice-service-1
+    namespace: default
+    labels:
+      kubernetes.io/service-name: service-1
+  addressType: IPv4
+  ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+  endpoints:
+  - addresses:
+    - 8.8.8.8
+    conditions:
+      ready: true
+- apiVersion: discovery.k8s.io/v1
+  kind: EndpointSlice
+  metadata:
+    name: endpointslice-service-2
+    namespace: envoy-gateway
+    labels:
+      kubernetes.io/service-name: service-2
+  addressType: IPv4
+  ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+  endpoints:
+  - addresses:
+    - 8.8.8.8
+    conditions:
+      ready: true
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml
new file mode 100644
index 000000000..838797318
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml
@@ -0,0 +1,278 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    creationTimestamp: null
+    name: policy-for-route-in-envoy-gateway-ns
+    namespace: envoy-gateway
+  spec:
+    targetSelectors:
+    - group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      matchLabels:
+        app: web-service
+    useClientProtocol: true
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-1
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 2
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    labels:
+      app: web-service
+    name: httproute
+    namespace: default
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-1
+        port: 8080
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    labels:
+      app: web-service
+    name: httproute
+    namespace: envoy-gateway
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-2
+        namespace: envoy-gateway
+        port: 8080
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+infraIR:
+  envoy-gateway/gateway-1:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-1/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+        ownerReference:
+          kind: GatewayClass
+          name: envoy-gateway-class
+      name: envoy-gateway/gateway-1
+      namespace: envoy-gateway-system
+xdsIR:
+  envoy-gateway/gateway-1:
+    accessLog:
+      json:
+      - path: /dev/stdout
+    globalResources:
+      proxyServiceCluster:
+        name: envoy-gateway/gateway-1
+        settings:
+        - addressType: IP
+          endpoints:
+          - host: 7.6.5.4
+            port: 8080
+            zone: zone1
+          metadata:
+            name: envoy-envoy-gateway-gateway-1-196ae069
+            namespace: envoy-gateway-system
+            sectionName: "8080"
+          name: envoy-gateway/gateway-1
+          protocol: TCP
+    http:
+    - address: 0.0.0.0
+      externalPort: 80
+      hostnames:
+      - '*'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-1/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+      routes:
+      - destination:
+          metadata:
+            kind: HTTPRoute
+            name: httproute
+            namespace: default
+          name: httproute/default/httproute/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 8.8.8.8
+              port: 8080
+            - host: 7.7.7.7
+              port: 8080
+            metadata:
+              kind: Service
+              name: service-1
+              namespace: default
+              sectionName: "8080"
+            name: httproute/default/httproute/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute
+          namespace: default
+        name: httproute/default/httproute/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
+      - destination:
+          metadata:
+            kind: HTTPRoute
+            name: httproute
+            namespace: envoy-gateway
+          name: httproute/envoy-gateway/httproute/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 8.8.8.8
+              port: 8080
+            metadata:
+              kind: Service
+              name: service-2
+              namespace: envoy-gateway
+              sectionName: "8080"
+            name: httproute/envoy-gateway/httproute/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute
+          namespace: envoy-gateway
+        name: httproute/envoy-gateway/httproute/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
+        traffic: {}
+        useClientProtocol: true
+    readyListener:
+      address: 0.0.0.0
+      ipFamily: IPv4
+      path: /ready
+      port: 19003
diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go
index 61125e80a..7478febea 100644
--- a/internal/provider/kubernetes/predicates_test.go
+++ b/internal/provider/kubernetes/predicates_test.go
@@ -286,6 +286,42 @@ func TestValidateConfigMapForReconcile(t *testing.T) {
 	}
 }
 
+// TestValidateBackendTrafficPolicyForReconcileWithRedirectResponseOverride tests the validateBackendTrafficPolicyForReconcile
+// predicate function with a redirect response override.
+func TestValidateBackendTrafficPolicyForReconcileWithRedirectResponseOverride(t *testing.T) {
+	btpWithRedirect := &egv1a1.BackendTrafficPolicy{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "response-override",
+			Namespace: "envoy-gateway-system",
+		},
+		Spec: egv1a1.BackendTrafficPolicySpec{
+			ResponseOverride: []*egv1a1.ResponseOverride{
+				{
+					Match: egv1a1.CustomResponseMatch{
+						StatusCodes: []egv1a1.StatusCodeMatch{
+							{
+								Type: &[]egv1a1.StatusCodeValueType{egv1a1.StatusCodeValueTypeRange}[0],
+								Range: &egv1a1.StatusCodeRange{
+									Start: 500,
+									End:   511,
+								},
+							},
+						},
+					},
+					// Using redirect instead of response causes ro.Response to be nil
+					Redirect: &egv1a1.CustomRedirect{
+						Hostname:   &[]gwapiv1.PreciseHostname{"custom-errors.example.com"}[0],
+						StatusCode: &[]int{302}[0],
+						Scheme:     &[]string{"https"}[0],
+					},
+				},
+			},
+		},
+	}
+	result := configMapBtpIndexFunc(btpWithRedirect)
+	require.Empty(t, result)
+}
+
 // TestValidateSecretForReconcile tests the validateSecretForReconcile
 // predicate function.
 func TestValidateSecretForReconcile(t *testing.T) {
diff --git a/test/gobench/translate_test.go b/test/gobench/translate_test.go
new file mode 100644
index 000000000..7bb02c07d
--- /dev/null
+++ b/test/gobench/translate_test.go
@@ -0,0 +1,226 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package fuzz
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/envoyproxy/gateway/internal/cmd/egctl"
+	"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
+)
+
+// Reused YAML snippets.
+const (
+	baseYAML = `apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+  name: eg
+spec:
+  controllerName: gateway.envoyproxy.io/gatewayclass-controller
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: eg
+  namespace: default
+spec:
+  gatewayClassName: eg
+  listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+`
+	backendYAML = `---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: Backend
+metadata:
+  name: provided-backend
+  namespace: default
+spec:
+  endpoints:
+    - ip:
+        address: 0.0.0.0
+        port: 8000
+`
+	grpcRouteYAML = `---
+apiVersion: gateway.networking.k8s.io/v1
+kind: GRPCRoute
+metadata:
+  name: backend
+  namespace: default
+spec:
+  parentRefs:
+    - name: eg
+      sectionName: grpc
+  hostnames:
+    - "www.grpc-example.com"
+  rules:
+    - matches:
+        - method:
+            service: com.example.Things
+            method: DoThing
+          headers:
+            - name: com.example.Header
+              value: foobar
+      backendRefs:
+        - name: provided-backend
+          port: 9000
+`
+	httpRouteYAML = `---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: backend
+  namespace: default
+spec:
+  parentRefs:
+    - name: eg
+  hostnames:
+    - "www.example.com"
+  rules:
+    - backendRefs:
+        - name: provided-backend
+          port: 8000
+`
+	udpRouteYAML = `---
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: UDPRoute
+metadata:
+  name: backend
+  namespace: default
+spec:
+  parentRefs:
+    - name: eg
+      sectionName: udp
+  rules:
+    - backendRefs:
+        - name: provided-backend
+          port: 3000
+`
+)
+
+// Helpers for benchmark route generation.
+func genHTTPRoutes(n int) string {
+	var sb strings.Builder
+	for i := 0; i < n; i++ {
+		sb.WriteString(fmt.Sprintf(`---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: backend-%d
+  namespace: default
+spec:
+  parentRefs:
+    - name: eg
+  hostnames:
+    - "www.example-%d.com"
+  rules:
+    - backendRefs:
+        - name: provided-backend
+          port: 8000
+`, i, i))
+	}
+	return sb.String()
+}
+
+func genGRPCRoutes(n int) string {
+	var sb strings.Builder
+	for i := 0; i < n; i++ {
+		sb.WriteString(fmt.Sprintf(`---
+apiVersion: gateway.networking.k8s.io/v1
+kind: GRPCRoute
+metadata:
+  name: backend-grpc-%d
+  namespace: default
+spec:
+  parentRefs:
+    - name: eg
+      sectionName: grpc
+  hostnames:
+    - "www.grpc-%d.example.com"
+  rules:
+    - matches:
+        - method:
+            service: com.example.Service%d
+            method: Call
+      backendRefs:
+        - name: provided-backend
+          port: 9000
+`, i, i, i))
+	}
+	return sb.String()
+}
+
+func genUDPRoutes(n int) string {
+	var sb strings.Builder
+	for i := 0; i < n; i++ {
+		sb.WriteString(fmt.Sprintf(`---
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: UDPRoute
+metadata:
+  name: backend-udp-%d
+  namespace: default
+spec:
+  parentRefs:
+    - name: eg
+      sectionName: udp
+  rules:
+    - backendRefs:
+        - name: provided-backend
+          port: %d
+`, i, 3000+i))
+	}
+	return sb.String()
+}
+
+// Benchmark cases: small / medium / large.
+func BenchmarkGatewayAPItoXDS(b *testing.B) {
+	type benchCase struct {
+		name string
+		yaml string
+	}
+	medium := baseYAML + backendYAML +
+		genHTTPRoutes(10) +
+		genGRPCRoutes(5) +
+		genUDPRoutes(2)
+	large := baseYAML + backendYAML +
+		genHTTPRoutes(100) +
+		genGRPCRoutes(50) +
+		genUDPRoutes(10)
+
+	cases := []benchCase{
+		{
+			name: "small",
+			yaml: baseYAML + httpRouteYAML + backendYAML,
+		},
+		{
+			name: "medium",
+			yaml: medium,
+		},
+		{
+			name: "large",
+			yaml: large,
+		},
+	}
+
+	for _, tc := range cases {
+		b.Run(tc.name, func(b *testing.B) {
+			rs, err := resource.LoadResourcesFromYAMLBytes([]byte(tc.yaml), true)
+			if err != nil {
+				b.Fatalf("load: %v", err)
+			}
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				_, err = egctl.TranslateGatewayAPIToXds("default", "cluster.local", "all", rs)
+				if err != nil && strings.Contains(err.Error(), "failed to translate xds") {
+					b.Fatalf("%v", err)
+				}
+			}
+		})
+	}
+}