Repository: envoyproxy/gateway
Pull request #3383 (2024-05-13 04:53:08)

build(deps): bump github/codeql-action from 3.25.3 to 3.25.4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.3 to 3.25.4.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a href="https://github.com/github/codeql-action/releases">releases page</a> for the relevant changes to the CodeQL CLI and language packs.</p>
<p>Note that the only difference between <code>v2</code> and <code>v3</code> of the CodeQL Action is the node version they support, with <code>v3</code> running on node 20 while we continue to release <code>v2</code> to support running on node 16. For example <code>3.22.11</code> was the first <code>v3</code> release and is functionally identical to <code>2.22.11</code>. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.</p>
<h2>[UNRELEASED]</h2>
<ul>
<li>Add a compatibility matrix of supported CodeQL Action, CodeQL CLI, and GitHub Enterprise Server versions to the <a href="https://github.com/github/codeql-action/blob/main/README.md">https://github.com/github/codeql-action/blob/main/README.md</a>. <a href="https://redirect.github.com/github/codeql-action/pull/2273">#2273</a></li>
<li>Avoid printing out a warning for a missing <code>on.push</code> trigger when the CodeQL Action is triggered via a <code>workflow_call</code> event. <a href="https://redirect.github.com/github/codeql-action/pull/2274">#2274</a></li>
</ul>
<h2>3.25.4 - 08 May 2024</h2>
<ul>
<li>Update default CodeQL bundle version to 2.17.2. <a href="https://redirect.github.com/github/codeql-action/pull/2270">#2270</a></li>
</ul>
<h2>3.25.3 - 25 Apr 2024</h2>
<ul>
<li>Update default CodeQL bundle version to 2.17.1. <a href="https://redirect.github.com/github/codeql-action/pull/2247">#2247</a></li>
<li>Workflows running on <code>macos-latest</code> using CodeQL CLI versions before v2.15.1 will need to either upgrade their CLI version to v2.15.1 or newer, or change the platform to an Intel MacOS runner, such as <code>macos-12</code>. ARM machines with SIP disabled, including the newest <code>macos-latest</code> image, are unsupported for CLI versions before 2.15.1. <a href="https://redirect.github.com/github/codeql-action/pull/2261">#2261</a></li>
</ul>
<h2>3.25.2 - 22 Apr 2024</h2>
<p>No user facing changes.</p>
<h2>3.25.1 - 17 Apr 2024</h2>
<ul>
<li>We are rolling out a feature in April/May 2024 that improves the reliability and performance of analyzing code when analyzing a compiled language with the <code>autobuild</code> <a href="https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#codeql-build-modes">build mode</a>. <a href="https://redirect.github.com/github/codeql-action/pull/2235">#2235</a></li>
<li>Fix a bug where the <code>init</code> Action would fail if <code>--overwrite</code> was specified in <code>CODEQL_ACTION_EXTRA_OPTIONS</code>. <a href="https://redirect.github.com/github/codeql-action/pull/2245">#2245</a></li>
</ul>
<h2>3.25.0 - 15 Apr 2024</h2>
<ul>
<li>
<p>The deprecated feature for extracting dependencies for a Python analysis has been removed. <a href="https://redirect.github.com/github/codeql-action/pull/2224">#2224</a></p>
<p>As a result, the following inputs and environment variables are now ignored:</p>
<ul>
<li>The <code>setup-python-dependencies</code> input to the <code>init</code> Action</li>
<li>The <code>CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION</code> environment variable</li>
</ul>
<p>We recommend removing any references to these from your workflows. For more information, see the release notes for CodeQL Action v3.23.0 and v2.23.0.</p>
</li>
<li>
<p>Automatically overwrite an existing database if found on the filesystem. <a href="https://redirect.github.com/github/codeql-action/pull/2229">#2229</a></p>
</li>
<li>
<p>Bump the minimum CodeQL bundle version to 2.12.6. <a href="https://redirect.github.com/github/codeql-action/pull/2232">#2232</a></p>
</li>
<li>
<p>A more relevant log message and a diagnostic are now emitted when the <code>file</code> program is not installed on a Linux runner, but is required for Go tracing to succeed. <a href="https://redirect.github.com/github/codeql-action/pull/2234">#2234</a></p>
</li>
</ul>
<h2>3.24.10 - 05 Apr 2024</h2>
<ul>
<li>Update default CodeQL bundle version to 2.17.0. <a href="https://redirect.github.com/github/codeql-action/pull/2219">#2219</a></li>
<li>Add a deprecation warning for customers using CodeQL version 2.12.5 and earlier. These versions of CodeQL were discontinued on 26 March 2024 alongside GitHub Enterprise Server 3.8, and will be unsupported by CodeQL Action versions 3.25.0 and later and versions 2.25.0 and later. <a href="https://redirect.github.com/github/codeql-action/pull/2220">#2220</a>
<ul>
<li>If you are using one of these versions, please update to CodeQL CLI version 2.12.6 or later. For instance, if you have specified a custom version of the CLI using the 'tools' input to the 'init' Action, you can remove this input to use the default version.</li>
<li>Alternatively, if you want to continue using a version of the CodeQL CLI between 2.11.6 and 2.12.5, you can replace <code>github/codeql-action/*@v3</code> by <code>github/codeql-action/*@v3.24.10</code> and <code>github/codeql-action/*@v2</code> by <code>github/codeql-action/*@v2.24.10</code> in your code scanning workflow to ensure you continue using this version of the CodeQL Action.</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/github/codeql-action/commit/ccf74c947955fd1cf117aef6a0e4e66191ef6f61"><code>ccf74c9</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2275">#2275</a> from github/update-v3.25.4-4b812a5df</li>
<li><a href="https://github.com/github/codeql-action/commit/4fdf4ac628f024c2f8d027b290dc3ce77e63a6b6"><code>4fdf4ac</code></a> Update changelog for v3.25.4</li>
<li><a href="https://github.com/github/codeql-action/commit/4b812a5dfff9268e215e9aaccfc6dbaed72a8a95"><code>4b812a5</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2270">#2270</a> from github/update-bundle/codeql-bundle-v2.17.2</li>
<li><a href="https://github.com/github/codeql-action/commit/1e21373a75e0c2ecdf26721d40f68b3bff7f2165"><code>1e21373</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2272">#2272</a> from github/marcogario-patch-1</li>
<li><a href="https://github.com/github/codeql-action/commit/4673d41da12ca3ed19dbc6916b2340f2b4b180f8"><code>4673d41</code></a> Build js files</li>
<li><a href="https://github.com/github/codeql-action/commit/65297ef0b0087dc25af1bfc57899aca7d6c7c719"><code>65297ef</code></a> Update link to deprecation changelog</li>
<li><a href="https://github.com/github/codeql-action/commit/84d6ead480f493c32a39f012db4b9dfb02e8868b"><code>84d6ead</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2271">#2271</a> from github/koesie10/remove-cli-sarif-merge-ff</li>
<li><a href="https://github.com/github/codeql-action/commit/b20bf5914d12a58b7cca55f695dfb74b9ebd7446"><code>b20bf59</code></a> Remove CLI SARIF merge feature flag</li>
<li><a href="https://github.com/github/codeql-action/commit/93b8232a3935eba04122a296f9eaa8942292b594"><code>93b8232</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2265">#2265</a> from github/koesie10/deprecate-merge</li>
<li><a href="https://github.com/github/codeql-action/commit/ee63da28475d6ac5a003c16596cdbe9d17ce3168"><code>ee63da2</code></a> Add changelog note</li>
<li>Additional commits viewable in <a href="https://github.com/github/codeql-action/compare/d39d31e687223d841ef683f52467bd88e9b21c14...ccf74c947955fd1cf117aef6a0e4e66191ef6f61">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.25.3&new-version=3.25.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index bc69c9130..10cd34c62 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -36,14 +36,14 @@ jobs:
     - uses: ./tools/github-actions/setup-deps
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14  # v3.25.3
+      uses: github/codeql-action/init@ccf74c947955fd1cf117aef6a0e4e66191ef6f61  # v3.25.4
       with:
         languages: ${{ matrix.language }}
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14  # v3.25.3
+      uses: github/codeql-action/autobuild@ccf74c947955fd1cf117aef6a0e4e66191ef6f61  # v3.25.4
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14  # v3.25.3
+      uses: github/codeql-action/analyze@ccf74c947955fd1cf117aef6a0e4e66191ef6f61  # v3.25.4
       with:
         category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 77c1a3dcb..81b475e5f 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -40,6 +40,6 @@ jobs:
         retention-days: 5
 
     - name: "Upload to code-scanning"
-      uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14  # v3.25.3
+      uses: github/codeql-action/upload-sarif@ccf74c947955fd1cf117aef6a0e4e66191ef6f61  # v3.25.4
       with:
         sarif_file: results.sarif
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 852cc201d..1a2df010d 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -339,8 +339,8 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
 		}
 	}
 
-	for _, ir := range xdsIR {
-		for _, tcp := range ir.TCP {
+	for _, x := range xdsIR {
+		for _, tcp := range x.TCP {
 			for _, r := range tcp.Routes {
 				if strings.HasPrefix(r.Destination.Name, prefix) {
 					r.LoadBalancer = lb
@@ -353,34 +353,37 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
 			}
 		}
 
-		for _, udp := range ir.UDP {
+		for _, udp := range x.UDP {
 			if strings.HasPrefix(udp.Destination.Name, prefix) {
 				udp.LoadBalancer = lb
 				udp.Timeout = to
 			}
 		}
 
-		for _, http := range ir.HTTP {
+		for _, http := range x.HTTP {
 			for _, r := range http.Routes {
 				// Apply if there is a match
 				if strings.HasPrefix(r.Name, prefix) {
-					r.RateLimit = rl
-					r.LoadBalancer = lb
-					r.ProxyProtocol = pp
-					r.HealthCheck = hc
+					r.Traffic = &ir.TrafficFeatures{
+						RateLimit:      rl,
+						LoadBalancer:   lb,
+						ProxyProtocol:  pp,
+						HealthCheck:    hc,
+						CircuitBreaker: cb,
+						FaultInjection: fi,
+						TCPKeepalive:   ka,
+						Retry:          rt,
+					}
+
 					// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
-					r.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
-					r.CircuitBreaker = cb
-					r.FaultInjection = fi
-					r.TCPKeepalive = ka
-					r.Retry = rt
+					r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
 
-					// some timeout setting originate from the route
+					// Some timeout setting originate from the route.
 					if policy.Spec.Timeout != nil {
 						if to, err = t.buildTimeout(policy, r); err != nil {
 							return errors.Wrap(err, "Timeout")
 						}
-						r.Timeout = to
+						r.Traffic.Timeout = to
 					}
 
 					if policy.Spec.UseClientProtocol != nil {
@@ -445,7 +448,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
 	// set by a policy attaching to the route
 	irKey := t.getIRKey(gateway.Gateway)
 	// Should exist since we've validated this
-	ir := xdsIR[irKey]
+	x := xdsIR[irKey]
 
 	policyTarget := irStringKey(policy.Namespace, string(policy.Spec.TargetRef.Name))
 
@@ -455,7 +458,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
 		}
 	}
 
-	for _, tcp := range ir.TCP {
+	for _, tcp := range x.TCP {
 		gatewayName := tcp.Name[0:strings.LastIndex(tcp.Name, "/")]
 		if t.MergeGateways && gatewayName != policyTarget {
 			continue
@@ -481,7 +484,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
 		}
 	}
 
-	for _, udp := range ir.UDP {
+	for _, udp := range x.UDP {
 		gatewayName := udp.Name[0:strings.LastIndex(udp.Name, "/")]
 		if t.MergeGateways && gatewayName != policyTarget {
 			continue
@@ -498,7 +501,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
 		}
 	}
 
-	for _, http := range ir.HTTP {
+	for _, http := range x.HTTP {
 		gatewayName := http.Name[0:strings.LastIndex(http.Name, "/")]
 		if t.MergeGateways && gatewayName != policyTarget {
 			continue
@@ -509,52 +512,29 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
 		for _, r := range http.Routes {
 			// If any of the features are already set, it means that a more specific
 			// policy(targeting xRoute) has already set it, so we skip it.
-			// TODO: zhaohuabing group the features into a struct and check if all of them are set
-			if r.RateLimit != nil || r.LoadBalancer != nil ||
-				r.ProxyProtocol != nil || r.HealthCheck != nil ||
-				r.CircuitBreaker != nil || r.FaultInjection != nil ||
-				r.TCPKeepalive != nil || r.Retry != nil ||
-				r.Timeout != nil {
+			if r.Traffic != nil {
 				continue
 			}
 
-			// Apply if not already set
-			if r.RateLimit == nil {
-				r.RateLimit = rl
-			}
-			if r.LoadBalancer == nil {
-				r.LoadBalancer = lb
-			}
-			if r.ProxyProtocol == nil {
-				r.ProxyProtocol = pp
-			}
-			if r.HealthCheck == nil {
-				r.HealthCheck = hc
-				// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
-				r.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
+			r.Traffic = &ir.TrafficFeatures{
+				RateLimit:      rl,
+				LoadBalancer:   lb,
+				ProxyProtocol:  pp,
+				HealthCheck:    hc,
+				CircuitBreaker: cb,
+				FaultInjection: fi,
+				TCPKeepalive:   ka,
+				Retry:          rt,
 			}
 
-			if r.CircuitBreaker == nil {
-				r.CircuitBreaker = cb
-			}
-			if r.FaultInjection == nil {
-				r.FaultInjection = fi
-			}
-			if r.TCPKeepalive == nil {
-				r.TCPKeepalive = ka
-			}
-			if r.Retry == nil {
-				r.Retry = rt
-			}
+			// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
+			r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
 
 			if policy.Spec.Timeout != nil {
 				if ct, err = t.buildTimeout(policy, r); err != nil {
 					return errors.Wrap(err, "Timeout")
 				}
-
-				if r.Timeout == nil {
-					r.Timeout = ct
-				}
+				r.Traffic.Timeout = ct
 			}
 
 			if policy.Spec.UseClientProtocol != nil {
@@ -1066,13 +1046,17 @@ func (t *Translator) buildTimeout(policy *egv1a1.BackendTrafficPolicy, r *ir.HTT
 
 	// http request timeout is translated during the gateway-api route resource translation
 	// merge route timeout setting with backendtrafficpolicy timeout settings
-	if r != nil && r.Timeout != nil && r.Timeout.HTTP != nil && r.Timeout.HTTP.RequestTimeout != nil {
+	if r != nil &&
+		r.Traffic != nil &&
+		r.Traffic.Timeout != nil &&
+		r.Traffic.Timeout.HTTP != nil &&
+		r.Traffic.Timeout.HTTP.RequestTimeout != nil {
 		if hto == nil {
 			hto = &ir.HTTPTimeout{
-				RequestTimeout: r.Timeout.HTTP.RequestTimeout,
+				RequestTimeout: r.Traffic.Timeout.HTTP.RequestTimeout,
 			}
 		} else {
-			hto.RequestTimeout = r.Timeout.HTTP.RequestTimeout
+			hto.RequestTimeout = r.Traffic.Timeout.HTTP.RequestTimeout
 		}
 	}
 
diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go
index d10331ca2..8abbac6c8 100644
--- a/internal/gatewayapi/route.go
+++ b/internal/gatewayapi/route.go
@@ -248,8 +248,8 @@ func processTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
 		var rto *ir.Timeout
 
 		// Timeout is translated from multiple resources and may already be partially set
-		if irRoute.Timeout != nil {
-			rto = irRoute.Timeout.DeepCopy()
+		if irRoute.Traffic != nil && irRoute.Traffic.Timeout != nil {
+			rto = irRoute.Traffic.Timeout.DeepCopy()
 		} else {
 			rto = &ir.Timeout{}
 		}
@@ -272,7 +272,9 @@ func processTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
 			setRequestTimeout(rto, metav1.Duration{Duration: d})
 		}
 
-		irRoute.Timeout = rto
+		irRoute.Traffic = &ir.TrafficFeatures{
+			Timeout: rto,
+		}
 	}
 }
 
@@ -681,14 +683,18 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route
 					URLRewrite:            routeRoute.URLRewrite,
 					Mirrors:               routeRoute.Mirrors,
 					ExtensionRefs:         routeRoute.ExtensionRefs,
-					Timeout:               routeRoute.Timeout,
-					Retry:                 routeRoute.Retry,
 					IsHTTP2:               routeRoute.IsHTTP2,
 				}
 				// Don't bother copying over the weights unless the route has invalid backends.
 				if routeRoute.BackendWeights.Invalid > 0 {
 					hostRoute.BackendWeights = routeRoute.BackendWeights
 				}
+				if routeRoute.Traffic != nil {
+					hostRoute.Traffic = &ir.TrafficFeatures{
+						Timeout: routeRoute.Traffic.Timeout,
+						Retry:   routeRoute.Traffic.Retry,
+					}
+				}
 				perHostRoutes = append(perHostRoutes, hostRoute)
 			}
 		}
diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go
index c7862d3cc..e68cf3fab 100644
--- a/internal/gatewayapi/securitypolicy.go
+++ b/internal/gatewayapi/securitypolicy.go
@@ -465,7 +465,7 @@ func (t *Translator) translateSecurityPolicyForGateway(
 		for _, r := range h.Routes {
 			// If any of the features are already set, it means that a more specific
 			// policy(targeting xRoute) has already set it, so we skip it.
-			if !r.Security.Empty() {
+			if r.Security != nil {
 				continue
 			}
 
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 126e8f8fb..25e3f5834 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -476,6 +476,24 @@ type HTTPRoute struct {
 	Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"`
 	// Rewrite to be changed for this route.
 	URLRewrite *URLRewrite `json:"urlRewrite,omitempty" yaml:"urlRewrite,omitempty"`
+	// ExtensionRefs holds unstructured resources that were introduced by an extension and used on the HTTPRoute as extensionRef filters
+	ExtensionRefs []*UnstructuredRef `json:"extensionRefs,omitempty" yaml:"extensionRefs,omitempty"`
+	// External Processing extensions
+	ExtProcs []ExtProc `json:"extProc,omitempty" yaml:"extProc,omitempty"`
+	// Wasm extensions
+	Wasms []Wasm `json:"wasm,omitempty" yaml:"wasm,omitempty"`
+
+	// Traffic holds the features associated with BackendTrafficPolicy
+	Traffic *TrafficFeatures `json:"traffic,omitempty" yaml:"traffic,omitempty"`
+	// Security holds the features associated with SecurityPolicy
+	Security *SecurityFeatures `json:"security,omitempty" yaml:"security,omitempty"`
+	// UseClientProtocol enables using the same protocol upstream that was used downstream
+	UseClientProtocol *bool `json:"useClientProtocol,omitempty" yaml:"useClientProtocol,omitempty"`
+}
+
+// TrafficFeatures holds the information associated with the Backend Traffic Policy.
+// +k8s:deepcopy-gen=true
+type TrafficFeatures struct {
 	// RateLimit defines the more specific match conditions as well as limits for ratelimiting
 	// the requests on this route.
 	RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"`
@@ -487,8 +505,6 @@ type HTTPRoute struct {
 	HealthCheck *HealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
 	// FaultInjection defines the schema for injecting faults into HTTP requests.
 	FaultInjection *FaultInjection `json:"faultInjection,omitempty" yaml:"faultInjection,omitempty"`
-	// ExtensionRefs holds unstructured resources that were introduced by an extension and used on the HTTPRoute as extensionRef filters
-	ExtensionRefs []*UnstructuredRef `json:"extensionRefs,omitempty" yaml:"extensionRefs,omitempty"`
 	// Circuit Breaker Settings
 	CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"`
 	// Request and connection timeout settings
@@ -497,15 +513,23 @@ type HTTPRoute struct {
 	TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"`
 	// Retry settings
 	Retry *Retry `json:"retry,omitempty" yaml:"retry,omitempty"`
-	// External Processing extensions
-	ExtProcs []ExtProc `json:"extProc,omitempty" yaml:"extProc,omitempty"`
-	// Wasm extensions
-	Wasms []Wasm `json:"wasm,omitempty" yaml:"wasm,omitempty"`
+}
 
-	// Security holds the features associated with SecurityPolicy
-	Security *SecurityFeatures `json:"security,omitempty" yaml:"security,omitempty"`
-	// UseClientProtocol enables using the same protocol upstream that was used downstream
-	UseClientProtocol *bool `json:"useClientProtocol,omitempty" yaml:"useClientProtocol,omitempty"`
+func (b *TrafficFeatures) Validate() error {
+	var errs error
+
+	if b.LoadBalancer != nil {
+		if err := b.LoadBalancer.Validate(); err != nil {
+			errs = errors.Join(errs, err)
+		}
+	}
+	if b.HealthCheck != nil {
+		if err := b.HealthCheck.Validate(); err != nil {
+			errs = errors.Join(errs, err)
+		}
+	}
+
+	return errs
 }
 
 // SecurityFeatures holds the information associated with the Security Policy.
@@ -523,19 +547,6 @@ type SecurityFeatures struct {
 	ExtAuth *ExtAuth `json:"extAuth,omitempty" yaml:"extAuth,omitempty"`
 }
 
-// Empty returns true if all the features are not set.
-func (s *SecurityFeatures) Empty() bool {
-	if s == nil {
-		return true
-	}
-
-	return s.CORS == nil &&
-		s.JWT == nil &&
-		s.OIDC == nil &&
-		s.BasicAuth == nil &&
-		s.ExtAuth == nil
-}
-
 func (s *SecurityFeatures) Printable() *SecurityFeatures {
 	out := s.DeepCopy()
 	if out.OIDC != nil {
@@ -872,8 +883,8 @@ func (h HTTPRoute) Validate() error {
 			occurred.Insert(header)
 		}
 	}
-	if h.LoadBalancer != nil {
-		if err := h.LoadBalancer.Validate(); err != nil {
+	if h.Traffic != nil {
+		if err := h.Traffic.Validate(); err != nil {
 			errs = errors.Join(errs, err)
 		}
 	}
@@ -882,11 +893,6 @@ func (h HTTPRoute) Validate() error {
 			errs = errors.Join(errs, err)
 		}
 	}
-	if h.HealthCheck != nil {
-		if err := h.HealthCheck.Validate(); err != nil {
-			errs = errors.Join(errs, err)
-		}
-	}
 
 	return errs
 }
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index bd0ce421a..514543c80 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -1021,31 +1021,6 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) {
 		*out = new(URLRewrite)
 		(*in).DeepCopyInto(*out)
 	}
-	if in.RateLimit != nil {
-		in, out := &in.RateLimit, &out.RateLimit
-		*out = new(RateLimit)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.LoadBalancer != nil {
-		in, out := &in.LoadBalancer, &out.LoadBalancer
-		*out = new(LoadBalancer)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.ProxyProtocol != nil {
-		in, out := &in.ProxyProtocol, &out.ProxyProtocol
-		*out = new(ProxyProtocol)
-		**out = **in
-	}
-	if in.HealthCheck != nil {
-		in, out := &in.HealthCheck, &out.HealthCheck
-		*out = new(HealthCheck)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.FaultInjection != nil {
-		in, out := &in.FaultInjection, &out.FaultInjection
-		*out = new(FaultInjection)
-		(*in).DeepCopyInto(*out)
-	}
 	if in.ExtensionRefs != nil {
 		in, out := &in.ExtensionRefs, &out.ExtensionRefs
 		*out = make([]*UnstructuredRef, len(*in))
@@ -1057,26 +1032,6 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) {
 			}
 		}
 	}
-	if in.CircuitBreaker != nil {
-		in, out := &in.CircuitBreaker, &out.CircuitBreaker
-		*out = new(CircuitBreaker)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.Timeout != nil {
-		in, out := &in.Timeout, &out.Timeout
-		*out = new(Timeout)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.TCPKeepalive != nil {
-		in, out := &in.TCPKeepalive, &out.TCPKeepalive
-		*out = new(TCPKeepalive)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.Retry != nil {
-		in, out := &in.Retry, &out.Retry
-		*out = new(Retry)
-		(*in).DeepCopyInto(*out)
-	}
 	if in.ExtProcs != nil {
 		in, out := &in.ExtProcs, &out.ExtProcs
 		*out = make([]ExtProc, len(*in))
@@ -1091,6 +1046,11 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
+	if in.Traffic != nil {
+		in, out := &in.Traffic, &out.Traffic
+		*out = new(TrafficFeatures)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.Security != nil {
 		in, out := &in.Security, &out.Security
 		*out = new(SecurityFeatures)
@@ -2424,6 +2384,66 @@ func (in *Tracing) DeepCopy() *Tracing {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) {
+	*out = *in
+	if in.RateLimit != nil {
+		in, out := &in.RateLimit, &out.RateLimit
+		*out = new(RateLimit)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.LoadBalancer != nil {
+		in, out := &in.LoadBalancer, &out.LoadBalancer
+		*out = new(LoadBalancer)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ProxyProtocol != nil {
+		in, out := &in.ProxyProtocol, &out.ProxyProtocol
+		*out = new(ProxyProtocol)
+		**out = **in
+	}
+	if in.HealthCheck != nil {
+		in, out := &in.HealthCheck, &out.HealthCheck
+		*out = new(HealthCheck)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.FaultInjection != nil {
+		in, out := &in.FaultInjection, &out.FaultInjection
+		*out = new(FaultInjection)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.CircuitBreaker != nil {
+		in, out := &in.CircuitBreaker, &out.CircuitBreaker
+		*out = new(CircuitBreaker)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(Timeout)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.TCPKeepalive != nil {
+		in, out := &in.TCPKeepalive, &out.TCPKeepalive
+		*out = new(TCPKeepalive)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Retry != nil {
+		in, out := &in.Retry, &out.Retry
+		*out = new(Retry)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficFeatures.
+func (in *TrafficFeatures) DeepCopy() *TrafficFeatures {
+	if in == nil {
+		return nil
+	}
+	out := new(TrafficFeatures)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *UDPListener) DeepCopyInto(out *UDPListener) {
 	*out = *in
diff --git a/internal/xds/translator/fault.go b/internal/xds/translator/fault.go
index 46642934c..43fae199c 100644
--- a/internal/xds/translator/fault.go
+++ b/internal/xds/translator/fault.go
@@ -36,7 +36,7 @@ var _ httpFilter = &fault{}
 
 // patchHCM builds and appends the fault Filters to the HTTP Connection Manager
 // if applicable, and it does not already exist.
-// Note: this method creates an fault filter for each route that contains an Fault config.
+// Note: this method creates a fault filter for each route that contains an Fault config.
 func (*fault) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
 	if mgr == nil {
 		return errors.New("hcm is nil")
@@ -99,7 +99,9 @@ func listenerContainsFault(irListener *ir.HTTPListener) bool {
 
 // routeContainsFault returns true if Fault exists for the provided route.
 func routeContainsFault(irRoute *ir.HTTPRoute) bool {
-	if irRoute != nil && irRoute.FaultInjection != nil {
+	if irRoute != nil &&
+		irRoute.Traffic != nil &&
+		irRoute.Traffic.FaultInjection != nil {
 		return true
 	}
 	return false
@@ -118,7 +120,7 @@ func (*fault) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
 	if irRoute == nil {
 		return errors.New("ir route is nil")
 	}
-	if irRoute.FaultInjection == nil {
+	if irRoute.Traffic == nil || irRoute.Traffic.FaultInjection == nil {
 		return nil
 	}
 
@@ -131,34 +133,35 @@ func (*fault) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
 
 	routeCfgProto := &xdshttpfaultv3.HTTPFault{}
 
-	if irRoute.FaultInjection.Delay != nil {
+	delay := irRoute.Traffic.FaultInjection.Delay
+	if delay != nil {
 		routeCfgProto.Delay = &xdsfault.FaultDelay{}
-		if irRoute.FaultInjection.Delay.Percentage != nil {
-			routeCfgProto.Delay.Percentage = translatePercentToFractionalPercent(irRoute.FaultInjection.Delay.Percentage)
+		if delay.Percentage != nil {
+			routeCfgProto.Delay.Percentage = translatePercentToFractionalPercent(delay.Percentage)
 		}
-		if irRoute.FaultInjection.Delay.FixedDelay != nil {
+		if delay.FixedDelay != nil {
 			routeCfgProto.Delay.FaultDelaySecifier = &xdsfault.FaultDelay_FixedDelay{
-				FixedDelay: durationpb.New(irRoute.FaultInjection.Delay.FixedDelay.Duration),
+				FixedDelay: durationpb.New(delay.FixedDelay.Duration),
 			}
 		}
 	}
 
-	if irRoute.FaultInjection.Abort != nil {
+	abort := irRoute.Traffic.FaultInjection.Abort
+	if abort != nil {
 		routeCfgProto.Abort = &xdshttpfaultv3.FaultAbort{}
-		if irRoute.FaultInjection.Abort.Percentage != nil {
-			routeCfgProto.Abort.Percentage = translatePercentToFractionalPercent(irRoute.FaultInjection.Abort.Percentage)
+		if abort.Percentage != nil {
+			routeCfgProto.Abort.Percentage = translatePercentToFractionalPercent(abort.Percentage)
 		}
-		if irRoute.FaultInjection.Abort.HTTPStatus != nil {
+		if abort.HTTPStatus != nil {
 			routeCfgProto.Abort.ErrorType = &xdshttpfaultv3.FaultAbort_HttpStatus{
-				HttpStatus: uint32(*irRoute.FaultInjection.Abort.HTTPStatus),
+				HttpStatus: uint32(*abort.HTTPStatus),
 			}
 		}
-		if irRoute.FaultInjection.Abort.GrpcStatus != nil {
+		if abort.GrpcStatus != nil {
 			routeCfgProto.Abort.ErrorType = &xdshttpfaultv3.FaultAbort_GrpcStatus{
-				GrpcStatus: uint32(*irRoute.FaultInjection.Abort.GrpcStatus),
+				GrpcStatus: uint32(*abort.GrpcStatus),
 			}
 		}
-
 	}
 
 	if routeCfgProto.Delay == nil && routeCfgProto.Abort == nil {
@@ -179,7 +182,7 @@ func (*fault) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
 	return nil
 }
 
-// translatePercentToFractionalPercent translates an v1alpha3 Percent instance
+// translatePercentToFractionalPercent translates a v1alpha3 Percent instance
 // to an envoy.type.FractionalPercent instance.
 func translatePercentToFractionalPercent(p *float32) *xdstype.FractionalPercent {
 	return &xdstype.FractionalPercent{
diff --git a/internal/xds/translator/local_ratelimit.go b/internal/xds/translator/local_ratelimit.go
index 688461e9f..1a0d88d5d 100644
--- a/internal/xds/translator/local_ratelimit.go
+++ b/internal/xds/translator/local_ratelimit.go
@@ -96,7 +96,10 @@ func listenerContainsLocalRateLimit(irListener *ir.HTTPListener) bool {
 }
 
 func routeContainsLocalRateLimit(irRoute *ir.HTTPRoute) bool {
-	if irRoute == nil || irRoute.RateLimit == nil || irRoute.RateLimit.Local == nil {
+	if irRoute == nil ||
+		irRoute.Traffic == nil ||
+		irRoute.Traffic.RateLimit == nil ||
+		irRoute.Traffic.RateLimit.Local == nil {
 		return false
 	}
 
@@ -113,7 +116,7 @@ func (*localRateLimit) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) e
 	routeAction := route.GetRoute()
 
 	// Return early if no rate limit config exists.
-	if irRoute.RateLimit == nil || irRoute.RateLimit.Local == nil || routeAction == nil {
+	if !routeContainsLocalRateLimit(irRoute) || routeAction == nil {
 		return nil
 	}
 
@@ -125,7 +128,7 @@ func (*localRateLimit) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) e
 			route.Name)
 	}
 
-	local := irRoute.RateLimit.Local
+	local := irRoute.Traffic.RateLimit.Local
 
 	rateLimits, descriptors, err := buildRouteLocalRateLimits(local)
 	if err != nil {
diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go
index 02b1c3d60..4534f303f 100644
--- a/internal/xds/translator/ratelimit.go
+++ b/internal/xds/translator/ratelimit.go
@@ -73,6 +73,17 @@ func (t *Translator) patchHCMWithRateLimit(mgr *hcmv3.HttpConnectionManager, irL
 	mgr.HttpFilters = append([]*hcmv3.HttpFilter{rateLimitFilter}, mgr.HttpFilters...)
 }
 
+func routeContainsGlobalRateLimit(irRoute *ir.HTTPRoute) bool {
+	if irRoute == nil ||
+		irRoute.Traffic == nil ||
+		irRoute.Traffic.RateLimit == nil ||
+		irRoute.Traffic.RateLimit.Global == nil {
+		return false
+	}
+
+	return true
+}
+
 // isRateLimitPresent returns true if rate limit config exists for the listener.
 func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool {
 	// Return false if global ratelimiting is disabled.
@@ -81,7 +92,7 @@ func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool {
 	}
 	// Return true if rate limit config exists.
 	for _, route := range irListener.Routes {
-		if route.RateLimit != nil && route.RateLimit.Global != nil {
+		if routeContainsGlobalRateLimit(route) {
 			return true
 		}
 	}
@@ -128,11 +139,11 @@ func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.Ht
 // patchRouteWithRateLimit builds rate limit actions and appends to the route.
 func patchRouteWithRateLimit(xdsRouteAction *routev3.RouteAction, irRoute *ir.HTTPRoute) error { //nolint:unparam
 	// Return early if no rate limit config exists.
-	if irRoute.RateLimit == nil || irRoute.RateLimit.Global == nil || xdsRouteAction == nil {
+	if !routeContainsGlobalRateLimit(irRoute) || xdsRouteAction == nil {
 		return nil
 	}
 
-	rateLimits := buildRouteRateLimits(irRoute.Name, irRoute.RateLimit.Global)
+	rateLimits := buildRouteRateLimits(irRoute.Name, irRoute.Traffic.RateLimit.Global)
 	xdsRouteAction.RateLimits = rateLimits
 	return nil
 }
@@ -287,8 +298,8 @@ func BuildRateLimitServiceConfig(irListener *ir.HTTPListener) *rlsconfv3.RateLim
 	pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, len(irListener.Routes))
 
 	for _, route := range irListener.Routes {
-		if route.RateLimit != nil && route.RateLimit.Global != nil {
-			serviceDescriptors := buildRateLimitServiceDescriptors(route.RateLimit.Global)
+		if routeContainsGlobalRateLimit(route) {
+			serviceDescriptors := buildRateLimitServiceDescriptors(route.Traffic.RateLimit.Global)
 
 			// Get route rule descriptors within each route.
 			//
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index a52371103..bf68ac7df 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -10,8 +10,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/envoyproxy/gateway/internal/utils/protocov"
-
 	corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
 	previoushost "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3"
@@ -20,6 +18,7 @@ import (
 	"google.golang.org/protobuf/types/known/wrapperspb"
 
 	"github.com/envoyproxy/gateway/internal/ir"
+	"github.com/envoyproxy/gateway/internal/utils/protocov"
 )
 
 const (
@@ -99,13 +98,18 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) {
 	}
 
 	// Timeouts
-	if router.GetRoute() != nil && httpRoute.Timeout != nil && httpRoute.Timeout.HTTP != nil &&
-		httpRoute.Timeout.HTTP.RequestTimeout != nil {
-		router.GetRoute().Timeout = durationpb.New(httpRoute.Timeout.HTTP.RequestTimeout.Duration)
+	if router.GetRoute() != nil &&
+		httpRoute.Traffic != nil &&
+		httpRoute.Traffic.Timeout != nil &&
+		httpRoute.Traffic.Timeout.HTTP != nil &&
+		httpRoute.Traffic.Timeout.HTTP.RequestTimeout != nil {
+		router.GetRoute().Timeout = durationpb.New(httpRoute.Traffic.Timeout.HTTP.RequestTimeout.Duration)
 	}
 
 	// Retries
-	if router.GetRoute() != nil && httpRoute.Retry != nil {
+	if router.GetRoute() != nil &&
+		httpRoute.Traffic != nil &&
+		httpRoute.Traffic.Retry != nil {
 		if rp, err := buildRetryPolicy(httpRoute); err == nil {
 			router.GetRoute().RetryPolicy = rp
 		} else {
@@ -259,22 +263,24 @@ func buildXdsWeightedRouteAction(httpRoute *ir.HTTPRoute) *routev3.RouteAction {
 }
 
 func idleTimeout(httpRoute *ir.HTTPRoute) *durationpb.Duration {
-	if httpRoute.Timeout != nil && httpRoute.Timeout.HTTP != nil {
-		if httpRoute.Timeout.HTTP.RequestTimeout != nil {
-			timeout := time.Hour // Default to 1 hour
+	if httpRoute.Traffic != nil &&
+		httpRoute.Traffic.Timeout != nil &&
+		httpRoute.Traffic.Timeout.HTTP != nil &&
+		httpRoute.Traffic.Timeout.HTTP.RequestTimeout != nil {
+		rt := httpRoute.Traffic.Timeout.HTTP.RequestTimeout
+		timeout := time.Hour // Default to 1 hour
 
-			// Ensure is not less than the request timeout
-			if timeout < httpRoute.Timeout.HTTP.RequestTimeout.Duration {
-				timeout = httpRoute.Timeout.HTTP.RequestTimeout.Duration
-			}
-
-			// Disable idle timeout when request timeout is disabled
-			if httpRoute.Timeout.HTTP.RequestTimeout.Duration == 0 {
-				timeout = 0
-			}
+		// Ensure is not less than the request timeout
+		if timeout < rt.Duration {
+			timeout = rt.Duration
+		}
 
-			return durationpb.New(timeout)
+		// Disable idle timeout when request timeout is disabled
+		if rt.Duration == 0 {
+			timeout = 0
 		}
+
+		return durationpb.New(timeout)
 	}
 	return nil
 }
@@ -436,22 +442,27 @@ func buildXdsAddedHeaders(headersToAdd []ir.AddHeader) []*corev3.HeaderValueOpti
 
 func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy {
 	// Return early
-	if httpRoute == nil || httpRoute.LoadBalancer == nil || httpRoute.LoadBalancer.ConsistentHash == nil {
+	if httpRoute == nil ||
+		httpRoute.Traffic == nil ||
+		httpRoute.Traffic.LoadBalancer == nil ||
+		httpRoute.Traffic.LoadBalancer.ConsistentHash == nil {
 		return nil
 	}
 
+	ch := httpRoute.Traffic.LoadBalancer.ConsistentHash
+
 	switch {
-	case httpRoute.LoadBalancer.ConsistentHash.Header != nil:
+	case ch.Header != nil:
 		hashPolicy := &routev3.RouteAction_HashPolicy{
 			PolicySpecifier: &routev3.RouteAction_HashPolicy_Header_{
 				Header: &routev3.RouteAction_HashPolicy_Header{
-					HeaderName: httpRoute.LoadBalancer.ConsistentHash.Header.Name,
+					HeaderName: ch.Header.Name,
 				},
 			},
 		}
 		return []*routev3.RouteAction_HashPolicy{hashPolicy}
-	case httpRoute.LoadBalancer.ConsistentHash.SourceIP != nil:
-		if !*httpRoute.LoadBalancer.ConsistentHash.SourceIP {
+	case ch.SourceIP != nil:
+		if !*ch.SourceIP {
 			return nil
 		}
 		hashPolicy := &routev3.RouteAction_HashPolicy{
@@ -468,68 +479,64 @@ func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy
 }
 
 func buildRetryPolicy(route *ir.HTTPRoute) (*routev3.RetryPolicy, error) {
-	if route.Retry != nil {
-		rr := route.Retry
-		rp := &routev3.RetryPolicy{
-			RetryOn:              retryDefaultRetryOn,
-			RetriableStatusCodes: []uint32{retryDefaultRetriableStatusCode},
-			NumRetries:           &wrapperspb.UInt32Value{Value: retryDefaultNumRetries},
-			RetryHostPredicate: []*routev3.RetryPolicy_RetryHostPredicate{
-				{
-					Name: "envoy.retry_host_predicates.previous_hosts",
-					ConfigType: &routev3.RetryPolicy_RetryHostPredicate_TypedConfig{
-						TypedConfig: protocov.ToAny(&previoushost.PreviousHostsPredicate{}),
-					},
+	rr := route.Traffic.Retry
+	rp := &routev3.RetryPolicy{
+		RetryOn:              retryDefaultRetryOn,
+		RetriableStatusCodes: []uint32{retryDefaultRetriableStatusCode},
+		NumRetries:           &wrapperspb.UInt32Value{Value: retryDefaultNumRetries},
+		RetryHostPredicate: []*routev3.RetryPolicy_RetryHostPredicate{
+			{
+				Name: "envoy.retry_host_predicates.previous_hosts",
+				ConfigType: &routev3.RetryPolicy_RetryHostPredicate_TypedConfig{
+					TypedConfig: protocov.ToAny(&previoushost.PreviousHostsPredicate{}),
 				},
 			},
-			HostSelectionRetryMaxAttempts: 5,
-		}
+		},
+		HostSelectionRetryMaxAttempts: 5,
+	}
 
-		if rr.NumRetries != nil {
-			rp.NumRetries = &wrapperspb.UInt32Value{Value: *rr.NumRetries}
-		}
+	if rr.NumRetries != nil {
+		rp.NumRetries = &wrapperspb.UInt32Value{Value: *rr.NumRetries}
+	}
 
-		if rr.RetryOn != nil {
-			if rr.RetryOn.Triggers != nil && len(rr.RetryOn.Triggers) > 0 {
-				if ro, err := buildRetryOn(rr.RetryOn.Triggers); err == nil {
-					rp.RetryOn = ro
-				} else {
-					return nil, err
-				}
+	if rr.RetryOn != nil {
+		if rr.RetryOn.Triggers != nil && len(rr.RetryOn.Triggers) > 0 {
+			if ro, err := buildRetryOn(rr.RetryOn.Triggers); err == nil {
+				rp.RetryOn = ro
+			} else {
+				return nil, err
 			}
+		}
 
-			if rr.RetryOn.HTTPStatusCodes != nil && len(rr.RetryOn.HTTPStatusCodes) > 0 {
-				rp.RetriableStatusCodes = buildRetryStatusCodes(rr.RetryOn.HTTPStatusCodes)
-			}
+		if rr.RetryOn.HTTPStatusCodes != nil && len(rr.RetryOn.HTTPStatusCodes) > 0 {
+			rp.RetriableStatusCodes = buildRetryStatusCodes(rr.RetryOn.HTTPStatusCodes)
 		}
+	}
 
-		if rr.PerRetry != nil {
-			if rr.PerRetry.Timeout != nil {
-				rp.PerTryTimeout = durationpb.New(rr.PerRetry.Timeout.Duration)
-			}
+	if rr.PerRetry != nil {
+		if rr.PerRetry.Timeout != nil {
+			rp.PerTryTimeout = durationpb.New(rr.PerRetry.Timeout.Duration)
+		}
 
-			if rr.PerRetry.BackOff != nil {
-				bbo := false
-				rbo := &routev3.RetryPolicy_RetryBackOff{}
-				if rr.PerRetry.BackOff.BaseInterval != nil {
-					rbo.BaseInterval = durationpb.New(rr.PerRetry.BackOff.BaseInterval.Duration)
-					bbo = true
-				}
+		if rr.PerRetry.BackOff != nil {
+			bbo := false
+			rbo := &routev3.RetryPolicy_RetryBackOff{}
+			if rr.PerRetry.BackOff.BaseInterval != nil {
+				rbo.BaseInterval = durationpb.New(rr.PerRetry.BackOff.BaseInterval.Duration)
+				bbo = true
+			}
 
-				if rr.PerRetry.BackOff.MaxInterval != nil {
-					rbo.MaxInterval = durationpb.New(rr.PerRetry.BackOff.MaxInterval.Duration)
-					bbo = true
-				}
+			if rr.PerRetry.BackOff.MaxInterval != nil {
+				rbo.MaxInterval = durationpb.New(rr.PerRetry.BackOff.MaxInterval.Duration)
+				bbo = true
+			}
 
-				if bbo {
-					rp.RetryBackOff = rbo
-				}
+			if bbo {
+				rp.RetryBackOff = rbo
 			}
 		}
-		return rp, nil
 	}
-
-	return nil, nil
+	return rp, nil
 }
 
 func buildRetryStatusCodes(codes []ir.HTTPStatus) []uint32 {
diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go
index a88f4e3fc..cab26d1f9 100644
--- a/internal/xds/translator/translator.go
+++ b/internal/xds/translator/translator.go
@@ -643,21 +643,28 @@ func findXdsEndpoint(tCtx *types.ResourceVersionTable, name string) *endpointv3.
 
 // processXdsCluster processes a xds cluster by its endpoint address type.
 func processXdsCluster(tCtx *types.ResourceVersionTable, httpRoute *ir.HTTPRoute, http1Settings *ir.HTTP1Settings, metrics *ir.Metrics) error {
-	if err := addXdsCluster(tCtx, &xdsClusterArgs{
+	clusterArgs := &xdsClusterArgs{
 		name:              httpRoute.Destination.Name,
 		settings:          httpRoute.Destination.Settings,
 		tSocket:           nil,
 		endpointType:      buildEndpointType(httpRoute.Destination.Settings),
-		loadBalancer:      httpRoute.LoadBalancer,
-		proxyProtocol:     httpRoute.ProxyProtocol,
-		circuitBreaker:    httpRoute.CircuitBreaker,
-		healthCheck:       httpRoute.HealthCheck,
 		http1Settings:     http1Settings,
-		timeout:           httpRoute.Timeout,
-		tcpkeepalive:      httpRoute.TCPKeepalive,
 		metrics:           metrics,
 		useClientProtocol: ptr.Deref(httpRoute.UseClientProtocol, false),
-	}); err != nil && !errors.Is(err, ErrXdsClusterExists) {
+	}
+
+	// Populate traffic features.
+	bt := httpRoute.Traffic
+	if bt != nil {
+		clusterArgs.loadBalancer = bt.LoadBalancer
+		clusterArgs.proxyProtocol = bt.ProxyProtocol
+		clusterArgs.circuitBreaker = bt.CircuitBreaker
+		clusterArgs.healthCheck = bt.HealthCheck
+		clusterArgs.timeout = bt.Timeout
+		clusterArgs.tcpkeepalive = bt.TCPKeepalive
+	}
+
+	if err := addXdsCluster(tCtx, clusterArgs); err != nil && !errors.Is(err, ErrXdsClusterExists) {
 		return err
 	}
 


Test patch:
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml
index b6cf93163..c5ad9495b 100755
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-override-replace.out.yaml
@@ -232,14 +232,15 @@ xdsIR:
             weight: 1
         hostname: gateway.envoyproxy.io
         isHTTP2: false
-        loadBalancer:
-          consistentHash:
-            sourceIP: true
         name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
         pathMatch:
           distinct: false
           name: ""
           prefix: /foo
+        traffic:
+          loadBalancer:
+            consistentHash:
+              sourceIP: true
       - backendWeights:
           invalid: 0
           valid: 0
@@ -254,16 +255,17 @@ xdsIR:
             weight: 1
         hostname: gateway.envoyproxy.io
         isHTTP2: false
-        loadBalancer:
-          random: {}
         name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io
         pathMatch:
           distinct: false
           name: ""
           prefix: /bar
-        timeout:
-          http:
-            connectionIdleTimeout: 21s
-            maxConnectionDuration: 22s
-          tcp:
-            connectTimeout: 20s
+        traffic:
+          loadBalancer:
+            random: {}
+          timeout:
+            http:
+              connectionIdleTimeout: 21s
+              maxConnectionDuration: 22s
+            tcp:
+              connectTimeout: 20s
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml
index 41088add2..edbf09d03 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml
@@ -557,6 +557,7 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        traffic: {}
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -584,6 +585,7 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/*
+        traffic: {}
     tcp:
     - address: 0.0.0.0
       name: envoy-gateway/gateway-2/tcp
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml
index 7d7867841..31566cafd 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-fault-injection.out.yaml
@@ -351,16 +351,17 @@ xdsIR:
               port: 8080
             protocol: GRPC
             weight: 1
-        faultInjection:
-          abort:
-            grpcStatus: 14
-            percentage: 100
-          delay:
-            fixedDelay: 5.4s
-            percentage: 80
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
+        traffic:
+          faultInjection:
+            abort:
+              grpcStatus: 14
+              percentage: 100
+            delay:
+              fixedDelay: 5.4s
+              percentage: 80
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -388,10 +389,6 @@ xdsIR:
               port: 8080
             protocol: HTTP
             weight: 1
-        faultInjection:
-          abort:
-            httpStatus: 14
-            percentage: 0.01
         hostname: gateway.envoyproxy.io
         isHTTP2: false
         name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io
@@ -399,6 +396,11 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /route2
+        traffic:
+          faultInjection:
+            abort:
+              httpStatus: 14
+              percentage: 0.01
       - backendWeights:
           invalid: 0
           valid: 0
@@ -411,13 +413,6 @@ xdsIR:
               port: 8080
             protocol: HTTP
             weight: 1
-        faultInjection:
-          abort:
-            httpStatus: 500
-            percentage: 100
-          delay:
-            fixedDelay: 5.4s
-            percentage: 80
         hostname: gateway.envoyproxy.io
         isHTTP2: false
         name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
@@ -425,3 +420,11 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        traffic:
+          faultInjection:
+            abort:
+              httpStatus: 500
+              percentage: 100
+            delay:
+              fixedDelay: 5.4s
+              percentage: 80
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml
index 5711eea32..2f43d16b4 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-use-client-protocol.out.yaml
@@ -156,4 +156,5 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        traffic: {}
         useClientProtocol: true
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml
index aa471f547..b3f265b9b 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml
@@ -266,12 +266,6 @@ xdsIR:
       - backendWeights:
           invalid: 0
           valid: 0
-        circuitBreaker:
-          maxConnections: 2048
-          maxParallelRequests: 4294967295
-          maxParallelRetries: 1024
-          maxPendingRequests: 1
-          maxRequestsPerConnection: 1
         destination:
           name: grpcroute/default/grpcroute-1/rule/0
           settings:
@@ -284,6 +278,13 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
+        traffic:
+          circuitBreaker:
+            maxConnections: 2048
+            maxParallelRequests: 4294967295
+            maxParallelRetries: 1024
+            maxPendingRequests: 1
+            maxRequestsPerConnection: 1
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -302,12 +303,6 @@ xdsIR:
       - backendWeights:
           invalid: 0
           valid: 0
-        circuitBreaker:
-          maxConnections: 42
-          maxParallelRequests: 42
-          maxParallelRetries: 24
-          maxPendingRequests: 42
-          maxRequestsPerConnection: 42
         destination:
           name: httproute/default/httproute-1/rule/0
           settings:
@@ -324,3 +319,10 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        traffic:
+          circuitBreaker:
+            maxConnections: 42
+            maxParallelRequests: 42
+            maxParallelRetries: 24
+            maxPendingRequests: 42
+            maxRequestsPerConnection: 42
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
index b49ec2daf..ee23bed19 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
@@ -483,32 +483,33 @@ xdsIR:
               port: 8080
             protocol: GRPC
             weight: 1
-        healthCheck:
-          active:
-            healthyThreshold: 1
-            http:
-              expectedResponse:
-                binary: RXZlcnl0aGluZyBPSw==
-              expectedStatuses:
-              - 200
-              - 300
-              host: '*'
-              method: GET
-              path: /healthz
-            interval: 3s
-            timeout: 500ms
-            unhealthyThreshold: 3
-          passive:
-            baseEjectionTime: 2m40s
-            consecutive5XxErrors: 5
-            consecutiveGatewayErrors: 0
-            consecutiveLocalOriginFailures: 5
-            interval: 2s
-            maxEjectionPercent: 100
-            splitExternalLocalOriginErrors: false
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
+        traffic:
+          healthCheck:
+            active:
+              healthyThreshold: 1
+              http:
+                expectedResponse:
+                  binary: RXZlcnl0aGluZyBPSw==
+                expectedStatuses:
+                - 200
+                - 300
+                host: '*'
+                method: GET
+                path: /healthz
+              interval: 3s
+              timeout: 500ms
+              unhealthyThreshold: 3
+            passive:
+              baseEjectionTime: 2m40s
+              consecutive5XxErrors: 5
+              consecutiveGatewayErrors: 0
+              consecutiveLocalOriginFailures: 5
+              interval: 2s
+              maxEjectionPercent: 100
+              splitExternalLocalOriginErrors: false
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -536,25 +537,6 @@ xdsIR:
               port: 8080
             protocol: HTTP
             weight: 1
-        healthCheck:
-          active:
-            healthyThreshold: 3
-            interval: 5s
-            tcp:
-              receive:
-                text: pong
-              send:
-                text: ping
-            timeout: 1s
-            unhealthyThreshold: 3
-          passive:
-            baseEjectionTime: 10s
-            consecutive5XxErrors: 5
-            consecutiveGatewayErrors: 0
-            consecutiveLocalOriginFailures: 5
-            interval: 10s
-            maxEjectionPercent: 10
-            splitExternalLocalOriginErrors: false
         hostname: gateway.envoyproxy.io
         isHTTP2: false
         name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io
@@ -562,6 +544,26 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /v2
+        traffic:
+          healthCheck:
+            active:
+              healthyThreshold: 3
+              interval: 5s
+              tcp:
+                receive:
+                  text: pong
+                send:
+                  text: ping
+              timeout: 1s
+              unhealthyThreshold: 3
+            passive:
+              baseEjectionTime: 10s
+              consecutive5XxErrors: 5
+              consecutiveGatewayErrors: 0
+              consecutiveLocalOriginFailures: 5
+              interval: 10s
+              maxEjectionPercent: 10
+              splitExternalLocalOriginErrors: false
       - backendWeights:
           invalid: 0
           valid: 0
@@ -574,25 +576,6 @@ xdsIR:
               port: 8080
             protocol: HTTP
             weight: 1
-        healthCheck:
-          active:
-            healthyThreshold: 1
-            interval: 3s
-            tcp:
-              receive:
-                binary: RXZlcnl0aGluZyBPSw==
-              send:
-                binary: cGluZw==
-            timeout: 1s
-            unhealthyThreshold: 3
-          passive:
-            baseEjectionTime: 2m40s
-            consecutive5XxErrors: 5
-            consecutiveGatewayErrors: 0
-            consecutiveLocalOriginFailures: 5
-            interval: 8ms
-            maxEjectionPercent: 11
-            splitExternalLocalOriginErrors: false
         hostname: gateway.envoyproxy.io
         isHTTP2: false
         name: httproute/default/httproute-3/rule/0/match/0/gateway_envoyproxy_io
@@ -600,6 +583,26 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /v3
+        traffic:
+          healthCheck:
+            active:
+              healthyThreshold: 1
+              interval: 3s
+              tcp:
+                receive:
+                  binary: RXZlcnl0aGluZyBPSw==
+                send:
+                  binary: cGluZw==
+              timeout: 1s
+              unhealthyThreshold: 3
+            passive:
+              baseEjectionTime: 2m40s
+              consecutive5XxErrors: 5
+              consecutiveGatewayErrors: 0
+              consecutiveLocalOriginFailures: 5
+              interval: 8ms
+              maxEjectionPercent: 11
+              splitExternalLocalOriginErrors: false
       - backendWeights:
           invalid: 0
           valid: 0
@@ -612,29 +615,6 @@ xdsIR:
               port: 8080
             protocol: HTTP
             weight: 1
-        healthCheck:
-          active:
-            healthyThreshold: 3
-            http:
-              expectedResponse:
-                text: pong
-              expectedStatuses:
-              - 200
-              - 201
-              host: gateway.envoyproxy.io
-              method: GET
-              path: /healthz
-            interval: 5s
-            timeout: 1s
-            unhealthyThreshold: 3
-          passive:
-            baseEjectionTime: 2m30s
-            consecutive5XxErrors: 5
-            consecutiveGatewayErrors: 0
-            consecutiveLocalOriginFailures: 5
-            interval: 1s
-            maxEjectionPercent: 100
-            splitExternalLocalOriginErrors: false
         hostname: gateway.envoyproxy.io
         isHTTP2: false
         name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
@@ -642,3 +622,27 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        traffic:
+          healthCheck:
+            active:
+              healthyThreshold: 3
+              http:
+                expectedResponse:
+                  text: pong
+                expectedStatuses:
+                - 200
+                - 201
+                host: gateway.envoyproxy.io
+                method: GET
+                path: /healthz
+              interval: 5s
+              timeout: 1s
+              unhealthyThreshold: 3
+            passive:
+              baseEjectionTime: 2m30s
+              consecutive5XxErrors: 5
+              consecutiveGatewayErrors: 0
+              consecutiveLocalOriginFailures: 5
+              interval: 1s
+              maxEjectionPercent: 100
+              splitExternalLocalOriginErrors: false
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml
index d308794c0..6114cd008 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml
@@ -374,9 +374,10 @@ xdsIR:
             weight: 1
         hostname: '*'
         isHTTP2: true
-        loadBalancer:
-          random: {}
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
+        traffic:
+          loadBalancer:
+            random: {}
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -406,15 +407,16 @@ xdsIR:
             weight: 1
         hostname: gateway.envoyproxy.io
         isHTTP2: false
-        loadBalancer:
-          leastRequest:
-            slowStart:
-              window: 5m0s
         name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io
         pathMatch:
           distinct: false
           name: ""
           prefix: /test2
+        traffic:
+          loadBalancer:
+            leastRequest:
+              slowStart:
+                window: 5m0s
       - backendWeights:
           invalid: 0
           valid: 0
@@ -429,11 +431,12 @@ xdsIR:
             weight: 1
         hostname: gateway.envoyproxy.io
         isHTTP2: false
-        loadBalancer:
-          consistentHash:
-            sourceIP: true
         name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
         pathMatch:
           distinct: false
           name: ""
           prefix: /
+        traffic:
+          loadBalancer:
+            consistentHash:
+              sourceIP: true
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml
index dcd818488..c55cf6333 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml
@@ -179,34 +179,35 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        rateLimit:
-          local:
-            default:
-              requests: 4294967295
-              unit: Second
-            rules:
-            - headerMatches:
-              - distinct: false
-                exact: one
-                name: x-user-id
-              - distinct: false
-                exact: foo
-                name: x-org-id
-              limit:
-                requests: 10
-                unit: Hour
-            - cidrMatch:
-                cidr: 192.168.0.0/16
-                distinct: false
-                ipv6: false
-                maskLen: 16
-              headerMatches:
-              - distinct: false
-                exact: two
-                name: x-user-id
-              - distinct: false
-                exact: bar
-                name: x-org-id
-              limit:
-                requests: 10
-                unit: Minute
+        traffic:
+          rateLimit:
+            local:
+              default:
+                requests: 4294967295
+                unit: Second
+              rules:
+              - headerMatches:
+                - distinct: false
+                  exact: one
+                  name: x-user-id
+                - distinct: false
+                  exact: foo
+                  name: x-org-id
+                limit:
+                  requests: 10
+                  unit: Hour
+              - cidrMatch:
+                  cidr: 192.168.0.0/16
+                  distinct: false
+                  ipv6: false
+                  maskLen: 16
+                headerMatches:
+                - distinct: false
+                  exact: two
+                  name: x-user-id
+                - distinct: false
+                  exact: bar
+                  name: x-org-id
+                limit:
+                  requests: 10
+                  unit: Minute
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml
index b331cd6fa..9c9d73f35 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml
@@ -182,34 +182,35 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        rateLimit:
-          local:
-            default:
-              requests: 10
-              unit: Minute
-            rules:
-            - headerMatches:
-              - distinct: false
-                exact: one
-                name: x-user-id
-              - distinct: false
-                exact: foo
-                name: x-org-id
-              limit:
-                requests: 10
-                unit: Hour
-            - cidrMatch:
-                cidr: 192.168.0.0/16
-                distinct: false
-                ipv6: false
-                maskLen: 16
-              headerMatches:
-              - distinct: false
-                exact: two
-                name: x-user-id
-              - distinct: false
-                exact: bar
-                name: x-org-id
-              limit:
+        traffic:
+          rateLimit:
+            local:
+              default:
                 requests: 10
                 unit: Minute
+              rules:
+              - headerMatches:
+                - distinct: false
+                  exact: one
+                  name: x-user-id
+                - distinct: false
+                  exact: foo
+                  name: x-org-id
+                limit:
+                  requests: 10
+                  unit: Hour
+              - cidrMatch:
+                  cidr: 192.168.0.0/16
+                  distinct: false
+                  ipv6: false
+                  maskLen: 16
+                headerMatches:
+                - distinct: false
+                  exact: two
+                  name: x-user-id
+                - distinct: false
+                  exact: bar
+                  name: x-org-id
+                limit:
+                  requests: 10
+                  unit: Minute
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml
index 8e6332a01..92245edd5 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml
@@ -270,8 +270,9 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
-        proxyProtocol:
-          version: V1
+        traffic:
+          proxyProtocol:
+            version: V1
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -306,5 +307,6 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        proxyProtocol:
-          version: V2
+        traffic:
+          proxyProtocol:
+            version: V2
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml
index d3d6c8eb5..7a8ea390f 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml
@@ -290,18 +290,19 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
-        rateLimit:
-          global:
-            rules:
-            - headerMatches:
-              - distinct: false
-                exact: one
-                name: x-user-id
-              - distinct: true
-                name: x-org-id
-              limit:
-                requests: 10
-                unit: Hour
+        traffic:
+          rateLimit:
+            global:
+              rules:
+              - headerMatches:
+                - distinct: false
+                  exact: one
+                  name: x-user-id
+                - distinct: true
+                  name: x-org-id
+                limit:
+                  requests: 10
+                  unit: Hour
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -336,15 +337,16 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        rateLimit:
-          global:
-            rules:
-            - cidrMatch:
-                cidr: 192.168.0.0/16
-                distinct: true
-                ipv6: false
-                maskLen: 16
-              headerMatches: []
-              limit:
-                requests: 20
-                unit: Hour
+        traffic:
+          rateLimit:
+            global:
+              rules:
+              - cidrMatch:
+                  cidr: 192.168.0.0/16
+                  distinct: true
+                  ipv6: false
+                  maskLen: 16
+                headerMatches: []
+                limit:
+                  requests: 20
+                  unit: Hour
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml
index ae83655a6..e2db512f8 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml
@@ -289,15 +289,16 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
-        retry:
-          perRetry:
-            backOff:
-              baseInterval: 100ms
-              maxInterval: 10s
-            timeout: 250ms
-          retryOn:
-            triggers:
-            - cancelled
+        traffic:
+          retry:
+            perRetry:
+              backOff:
+                baseInterval: 100ms
+                maxInterval: 10s
+              timeout: 250ms
+            retryOn:
+              triggers:
+              - cancelled
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -332,17 +333,18 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        retry:
-          numRetries: 5
-          perRetry:
-            backOff:
-              baseInterval: 100ms
-              maxInterval: 10s
-            timeout: 250ms
-          retryOn:
-            httpStatusCodes:
-            - 429
-            - 503
-            triggers:
-            - connect-failure
-            - retriable-status-codes
+        traffic:
+          retry:
+            numRetries: 5
+            perRetry:
+              backOff:
+                baseInterval: 100ms
+                maxInterval: 10s
+              timeout: 250ms
+            retryOn:
+              httpStatusCodes:
+              - 429
+              - 503
+              triggers:
+              - connect-failure
+              - retriable-status-codes
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml
index c9a22a833..ad6bbe269 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml
@@ -182,10 +182,6 @@ xdsIR:
       - backendWeights:
           invalid: 0
           valid: 0
-        circuitBreaker:
-          maxConnections: 2048
-          maxParallelRequests: 4294967295
-          maxPendingRequests: 1
         destination:
           name: httproute/default/httproute-1/rule/0
           settings:
@@ -202,6 +198,11 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        traffic:
+          circuitBreaker:
+            maxConnections: 2048
+            maxParallelRequests: 4294967295
+            maxPendingRequests: 1
       - backendWeights:
           invalid: 0
           valid: 0
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml
index eb796b42f..902cdd313 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml
@@ -274,10 +274,11 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
-        tcpKeepalive:
-          idleTime: 1200
-          interval: 60
-          probes: 3
+        traffic:
+          tcpKeepalive:
+            idleTime: 1200
+            interval: 60
+            probes: 3
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -312,7 +313,8 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        tcpKeepalive:
-          idleTime: 10
-          interval: 1800
-          probes: 6
+        traffic:
+          tcpKeepalive:
+            idleTime: 10
+            interval: 1800
+            probes: 6
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml
index 27ed1f540..b7dc2f6a0 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml
@@ -278,12 +278,13 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
-        timeout:
-          http:
-            connectionIdleTimeout: 16s
-            maxConnectionDuration: 17s
-          tcp:
-            connectTimeout: 15s
+        traffic:
+          timeout:
+            http:
+              connectionIdleTimeout: 16s
+              maxConnectionDuration: 17s
+            tcp:
+              connectTimeout: 15s
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -318,9 +319,10 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        timeout:
-          http:
-            connectionIdleTimeout: 21s
-            maxConnectionDuration: 22s
-          tcp:
-            connectTimeout: 20s
+        traffic:
+          timeout:
+            http:
+              connectionIdleTimeout: 21s
+              maxConnectionDuration: 22s
+            tcp:
+              connectTimeout: 20s
diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml
index b6093872e..3a85ecb7c 100644
--- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml
@@ -163,6 +163,7 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        timeout:
-          http:
-            requestTimeout: 1s
+        traffic:
+          timeout:
+            http:
+              requestTimeout: 1s
diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml
index 2f060c2db..097b37b51 100644
--- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml
@@ -279,12 +279,13 @@ xdsIR:
         hostname: '*'
         isHTTP2: true
         name: grpcroute/default/grpcroute-1/rule/0/match/-1/*
-        timeout:
-          http:
-            connectionIdleTimeout: 16s
-            maxConnectionDuration: 17s
-          tcp:
-            connectTimeout: 15s
+        traffic:
+          timeout:
+            http:
+              connectionIdleTimeout: 16s
+              maxConnectionDuration: 17s
+            tcp:
+              connectTimeout: 15s
   envoy-gateway/gateway-2:
     accessLog:
       text:
@@ -319,9 +320,9 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        timeout:
-          http:
-            maxConnectionDuration: 22s
-            requestTimeout: 1s
-          tcp:
-            connectTimeout: 20s
+        traffic:
+          timeout:
+            http:
+              maxConnectionDuration: 22s
+            tcp:
+              connectTimeout: 20s
diff --git a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml
index f56835ccb..33076734d 100644
--- a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml
@@ -129,6 +129,7 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        timeout:
-          http:
-            requestTimeout: 1s
+        traffic:
+          timeout:
+            http:
+              requestTimeout: 1s
diff --git a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml
index 3d3f73076..49c64cfe6 100644
--- a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml
@@ -129,6 +129,7 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        timeout:
-          http:
-            requestTimeout: 5s
+        traffic:
+          timeout:
+            http:
+              requestTimeout: 5s
diff --git a/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml b/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml
index 674289ecd..62df76b1b 100755
--- a/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml
+++ b/internal/gatewayapi/testdata/merge-with-isolated-policies-2.out.yaml
@@ -546,10 +546,11 @@ xdsIR:
             - x-header-7
             - x-header-8
             maxAge: 33m20s
-        tcpKeepalive:
-          idleTime: 1200
-          interval: 60
-          probes: 3
+        traffic:
+          tcpKeepalive:
+            idleTime: 1200
+            interval: 60
+            probes: 3
       timeout:
         http:
           requestReceivedTimeout: 5s
@@ -598,10 +599,11 @@ xdsIR:
             - x-header-7
             - x-header-8
             maxAge: 33m20s
-        tcpKeepalive:
-          idleTime: 1200
-          interval: 60
-          probes: 3
+        traffic:
+          tcpKeepalive:
+            idleTime: 1200
+            interval: 60
+            probes: 3
       timeout:
         http:
           requestReceivedTimeout: 5s
diff --git a/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml b/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml
index d2bc2693c..9c3438bff 100644
--- a/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml
+++ b/internal/gatewayapi/testdata/merge-with-isolated-policies.out.yaml
@@ -338,10 +338,11 @@ xdsIR:
             - x-header-7
             - x-header-8
             maxAge: 33m20s
-        tcpKeepalive:
-          idleTime: 1200
-          interval: 60
-          probes: 3
+        traffic:
+          tcpKeepalive:
+            idleTime: 1200
+            interval: 60
+            probes: 3
     - address: 0.0.0.0
       hostnames:
       - '*'
diff --git a/internal/xds/translator/route_test.go b/internal/xds/translator/route_test.go
index ed9198b6c..3d7d6a972 100644
--- a/internal/xds/translator/route_test.go
+++ b/internal/xds/translator/route_test.go
@@ -32,23 +32,39 @@ func Test_buildHashPolicy(t *testing.T) {
 			want:      nil,
 		},
 		{
-			name:      "Nil ConsistentHash in LoadBalancer",
-			httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{}},
-			want:      nil,
+			name: "Nil ConsistentHash in LoadBalancer",
+			httpRoute: &ir.HTTPRoute{
+				Traffic: &ir.TrafficFeatures{
+					LoadBalancer: &ir.LoadBalancer{},
+				},
+			},
+			want: nil,
 		},
 		{
-			name:      "ConsistentHash with nil SourceIP and Header",
-			httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{}}},
-			want:      nil,
+			name: "ConsistentHash with nil SourceIP and Header",
+			httpRoute: &ir.HTTPRoute{
+				Traffic: &ir.TrafficFeatures{
+					LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{}},
+				},
+			},
+			want: nil,
 		},
 		{
-			name:      "ConsistentHash with SourceIP set to false",
-			httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: ptr.To(false)}}},
-			want:      nil,
+			name: "ConsistentHash with SourceIP set to false",
+			httpRoute: &ir.HTTPRoute{
+				Traffic: &ir.TrafficFeatures{
+					LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: ptr.To(false)}},
+				},
+			},
+			want: nil,
 		},
 		{
-			name:      "ConsistentHash with SourceIP set to true",
-			httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: ptr.To(true)}}},
+			name: "ConsistentHash with SourceIP set to true",
+			httpRoute: &ir.HTTPRoute{
+				Traffic: &ir.TrafficFeatures{
+					LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: ptr.To(true)}},
+				},
+			},
 			want: []*routev3.RouteAction_HashPolicy{
 				{
 					PolicySpecifier: &routev3.RouteAction_HashPolicy_ConnectionProperties_{
@@ -60,8 +76,12 @@ func Test_buildHashPolicy(t *testing.T) {
 			},
 		},
 		{
-			name:      "ConsistentHash with Header",
-			httpRoute: &ir.HTTPRoute{LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Header: &ir.Header{Name: "name"}}}},
+			name: "ConsistentHash with Header",
+			httpRoute: &ir.HTTPRoute{
+				Traffic: &ir.TrafficFeatures{
+					LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Header: &ir.Header{Name: "name"}}},
+				},
+			},
 			want: []*routev3.RouteAction_HashPolicy{
 				{
 					PolicySpecifier: &routev3.RouteAction_HashPolicy_Header_{
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml
index 0f3f97496..742805ac2 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml
@@ -8,15 +8,16 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - headerMatches:
-        - name: "x-user-id"
-          distinct: true
-        limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - headerMatches:
+          - name: "x-user-id"
+            distinct: true
+          limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml
index 7ff820e7b..4fbb48de3 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml
@@ -8,17 +8,18 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - cidrMatch:
-          cidr: "192.168.0.0/16"
-          ipv6: false
-          maskLen: 16
-          distinct: true
-        limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - cidrMatch:
+            cidr: "192.168.0.0/16"
+            ipv6: false
+            maskLen: 16
+            distinct: true
+          limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml
index ca13b2132..0de76f96e 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml
@@ -8,12 +8,13 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml
index 70531d48a..4e21ae73c 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml
@@ -8,16 +8,17 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - cidrMatch:
-          cidr: "192.168.0.0/16"
-          ipv6: false
-          maskLen: 16
-        limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - cidrMatch:
+            cidr: "192.168.0.0/16"
+            ipv6: false
+            maskLen: 16
+          limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml
index 32792f005..3f5dba6a9 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-masked-remote-address-match-with-same-cidr.yaml
@@ -8,16 +8,17 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - cidrMatch:
-          cidr: "192.168.0.10/32"
-          ipv6: false
-          maskLen: 32
-        limit:
-          requests: 15
-          unit: Hour
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - cidrMatch:
+            cidr: "192.168.0.10/32"
+            ipv6: false
+            maskLen: 32
+          limit:
+            requests: 15
+            unit: Hour
   pathMatch:
     exact: "foo/bar"
   destination:
@@ -27,16 +28,17 @@ routes:
       - host: "1.2.3.4"
         port: 50000
 - name: "second-route"
-  rateLimit:
-    global:
-      rules:
-      - cidrMatch:
-          cidr: "192.168.0.10/32"
-          ipv6: false
-          maskLen: 32
-        limit:
-          requests: 300
-          unit: Hour
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - cidrMatch:
+            cidr: "192.168.0.10/32"
+            ipv6: false
+            maskLen: 32
+          limit:
+            requests: 300
+            unit: Hour
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml
index 67de62abb..bebb4cb14 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml
@@ -8,17 +8,18 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - headerMatches:
-        - name: "x-user-id"
-          exact: "one"
-        - name: "x-user-id"
-          exact: "two"
-        limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - headerMatches:
+          - name: "x-user-id"
+            exact: "one"
+          - name: "x-user-id"
+            exact: "two"
+          limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml
index c5a768c9b..6db5c96e8 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml
@@ -8,27 +8,29 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - headerMatches:
-        - name: "x-user-id"
-          exact: "one"
-        limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - headerMatches:
+          - name: "x-user-id"
+            exact: "one"
+          limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/baz"
 - name: "second-route"
-  rateLimit:
-    global:
-      rules:
-      - headerMatches:
-        - name: "x-user-id"
-          exact: "two"
-        limit:
-          requests: 10
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - headerMatches:
+          - name: "x-user-id"
+            exact: "two"
+          limit:
+            requests: 10
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml
index ef9b8d228..3267a695b 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml
@@ -8,21 +8,22 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - headerMatches:
-        - name: "x-user-id"
-          exact: "one"
-        limit:
-          requests: 5
-          unit: second
-      - headerMatches:
-        - name: "x-user-id"
-          exact: "two"
-        limit:
-          requests: 10
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - headerMatches:
+          - name: "x-user-id"
+            exact: "one"
+          limit:
+            requests: 5
+            unit: second
+        - headerMatches:
+          - name: "x-user-id"
+            exact: "two"
+          limit:
+            requests: 10
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml
index 8fc933e92..a265cab5c 100644
--- a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml
+++ b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml
@@ -8,15 +8,16 @@ path:
   escapedSlashesAction: UnescapeAndRedirect
 routes:
 - name: "first-route"
-  rateLimit:
-    global:
-      rules:
-      - headerMatches:
-        - name: "x-user-id"
-          exact: "one"
-        limit:
-          requests: 5
-          unit: second
+  traffic:
+    rateLimit:
+      global:
+        rules:
+        - headerMatches:
+          - name: "x-user-id"
+            exact: "one"
+          limit:
+            requests: 5
+            unit: second
   pathMatch:
     exact: "foo/bar"
   destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/circuit-breaker.yaml b/internal/xds/translator/testdata/in/xds-ir/circuit-breaker.yaml
index a63297e26..f4dd3bbaa 100644
--- a/internal/xds/translator/testdata/in/xds-ir/circuit-breaker.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/circuit-breaker.yaml
@@ -10,12 +10,13 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    circuitBreaker:
-      maxConnections: 1
-      maxPendingRequests: 1
-      maxParallelRequests: 1
-      maxParallelRetries: 2
-      maxRequestsPerConnection: 10
+    traffic:
+      circuitBreaker:
+        maxConnections: 1
+        maxPendingRequests: 1
+        maxParallelRequests: 1
+        maxParallelRetries: 2
+        maxRequestsPerConnection: 10
     destination:
       name: "first-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/client-timeout.yaml b/internal/xds/translator/testdata/in/xds-ir/client-timeout.yaml
index 06c44b051..6c3cfa3aa 100644
--- a/internal/xds/translator/testdata/in/xds-ir/client-timeout.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/client-timeout.yaml
@@ -20,4 +20,3 @@ http:
       http:
         requestReceivedTimeout: "5s"
         idleTimeout: "10s"
-
diff --git a/internal/xds/translator/testdata/in/xds-ir/fault-injection.yaml b/internal/xds/translator/testdata/in/xds-ir/fault-injection.yaml
index a99b77d5e..39b351eb6 100644
--- a/internal/xds/translator/testdata/in/xds-ir/fault-injection.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/fault-injection.yaml
@@ -10,10 +10,11 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    faultInjection:
-      abort:
-        httpStatus: 500
-        percentage: 0.01
+    traffic:
+      faultInjection:
+        abort:
+          httpStatus: 500
+          percentage: 0.01
     pathMatch:
       exact: "foo/bar"
     destination:
@@ -24,10 +25,11 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    faultInjection:
-      delay:
-        fixedDelay: 5.4s
-        percentage: 80
+    traffic:
+      faultInjection:
+        delay:
+          fixedDelay: 5.4s
+          percentage: 80
     pathMatch:
       exact: "example"
     destination:
@@ -38,13 +40,14 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    faultInjection:
-      abort:
-        httpStatus: 500
-        percentage: 100
-      delay:
-        fixedDelay: 5.005s
-        percentage: 100
+    traffic:
+      faultInjection:
+        abort:
+          httpStatus: 500
+          percentage: 100
+        delay:
+          fixedDelay: 5.005s
+          percentage: 100
     pathMatch:
       exact: "test"
     destination:
@@ -55,13 +58,14 @@ http:
           port: 50000
   - name: "fourth-route"
     hostname: "*"
-    faultInjection:
-      abort:
-        grpcStatus: 14
-        percentage: 100
-      delay:
-        fixedDelay: 5.005s
-        percentage: 100
+    traffic:
+      faultInjection:
+        abort:
+          grpcStatus: 14
+          percentage: 100
+        delay:
+          fixedDelay: 5.005s
+          percentage: 100
     pathMatch:
       exact: "test"
     destination:
@@ -72,7 +76,8 @@ http:
           port: 50000
   - name: "fifth-route"
     hostname: "*"
-    faultInjection:
+    traffic:
+      faultInjection: {}
     pathMatch:
       exact: "test"
     destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/health-check.yaml b/internal/xds/translator/testdata/in/xds-ir/health-check.yaml
index a767bdab2..30ecd2da7 100644
--- a/internal/xds/translator/testdata/in/xds-ir/health-check.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/health-check.yaml
@@ -10,28 +10,29 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    healthCheck:
-      active:
-        timeout: "500ms"
-        interval: "3s"
-        unhealthyThreshold: 3
-        healthyThreshold: 1
-        http:
-          host: "*"
-          path: "/healthz"
-          expectedResponse:
-            text: "ok"
-          expectedStatuses:
-          - 200
-          - 300
-      passive:
-        baseEjectionTime: 180s
-        interval: 2s
-        maxEjectionPercent: 100
-        consecutive5XxErrors: 5
-        consecutiveGatewayErrors: 0
-        consecutiveLocalOriginFailures: 5
-        splitExternalLocalOriginErrors: false
+    traffic:
+      healthCheck:
+        active:
+          timeout: "500ms"
+          interval: "3s"
+          unhealthyThreshold: 3
+          healthyThreshold: 1
+          http:
+            host: "*"
+            path: "/healthz"
+            expectedResponse:
+              text: "ok"
+            expectedStatuses:
+            - 200
+            - 300
+        passive:
+          baseEjectionTime: 180s
+          interval: 2s
+          maxEjectionPercent: 100
+          consecutive5XxErrors: 5
+          consecutiveGatewayErrors: 0
+          consecutiveLocalOriginFailures: 5
+          splitExternalLocalOriginErrors: false
     destination:
       name: "first-route-dest"
       settings:
@@ -40,28 +41,29 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    healthCheck:
-      active:
-        timeout: "1s"
-        interval: "5s"
-        unhealthyThreshold: 3
-        healthyThreshold: 3
-        http:
-          host: "*"
-          path: "/healthz"
-          expectedResponse:
-            binary: "cG9uZw=="
-          expectedStatuses:
-          - 200
-          - 201
-      passive:
-        baseEjectionTime: 180s
-        interval: 1s
-        maxEjectionPercent: 100
-        consecutive5XxErrors: 5
-        consecutiveGatewayErrors: 0
-        consecutiveLocalOriginFailures: 5
-        splitExternalLocalOriginErrors: false
+    traffic:
+      healthCheck:
+        active:
+          timeout: "1s"
+          interval: "5s"
+          unhealthyThreshold: 3
+          healthyThreshold: 3
+          http:
+            host: "*"
+            path: "/healthz"
+            expectedResponse:
+              binary: "cG9uZw=="
+            expectedStatuses:
+            - 200
+            - 201
+        passive:
+          baseEjectionTime: 180s
+          interval: 1s
+          maxEjectionPercent: 100
+          consecutive5XxErrors: 5
+          consecutiveGatewayErrors: 0
+          consecutiveLocalOriginFailures: 5
+          splitExternalLocalOriginErrors: false
     destination:
       name: "second-route-dest"
       settings:
@@ -70,25 +72,26 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    healthCheck:
-      active:
-        timeout: "1s"
-        interval: "5s"
-        unhealthyThreshold: 3
-        healthyThreshold: 3
-        tcp:
-          send:
-            text: "ping"
-          receive:
-            text: "pong"
-      passive:
-        baseEjectionTime: 160s
-        interval: 1s
-        maxEjectionPercent: 100
-        consecutive5XxErrors: 5
-        consecutiveGatewayErrors: 0
-        consecutiveLocalOriginFailures: 5
-        splitExternalLocalOriginErrors: false
+    traffic:
+      healthCheck:
+        active:
+          timeout: "1s"
+          interval: "5s"
+          unhealthyThreshold: 3
+          healthyThreshold: 3
+          tcp:
+            send:
+              text: "ping"
+            receive:
+              text: "pong"
+        passive:
+          baseEjectionTime: 160s
+          interval: 1s
+          maxEjectionPercent: 100
+          consecutive5XxErrors: 5
+          consecutiveGatewayErrors: 0
+          consecutiveLocalOriginFailures: 5
+          splitExternalLocalOriginErrors: false
     destination:
       name: "third-route-dest"
       settings:
@@ -97,25 +100,26 @@ http:
           port: 50000
   - name: "fourth-route"
     hostname: "*"
-    healthCheck:
-      active:
-        timeout: "1s"
-        interval: "5s"
-        unhealthyThreshold: 3
-        healthyThreshold: 3
-        tcp:
-          send:
-            binary: "cGluZw=="
-          receive:
-            binary: "cG9uZw=="
-      passive:
-        baseEjectionTime: 180s
-        interval: 1s
-        maxEjectionPercent: 90
-        consecutive5XxErrors: 5
-        consecutiveGatewayErrors: 0
-        consecutiveLocalOriginFailures: 5
-        splitExternalLocalOriginErrors: true
+    traffic:
+      healthCheck:
+        active:
+          timeout: "1s"
+          interval: "5s"
+          unhealthyThreshold: 3
+          healthyThreshold: 3
+          tcp:
+            send:
+              binary: "cGluZw=="
+            receive:
+              binary: "cG9uZw=="
+        passive:
+          baseEjectionTime: 180s
+          interval: 1s
+          maxEjectionPercent: 90
+          consecutive5XxErrors: 5
+          consecutiveGatewayErrors: 0
+          consecutiveLocalOriginFailures: 5
+          splitExternalLocalOriginErrors: true
     destination:
       name: "fourth-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml
index fdd2b84ef..746d49225 100644
--- a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml
@@ -10,9 +10,10 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    timeout:
-      http:
-        requestTimeout: 5s
+    traffic:
+      timeout:
+        http:
+          requestTimeout: 5s
     headerMatches:
     - name: user
       stringMatch:
@@ -25,9 +26,10 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    timeout:
-      http:
-        requestTimeout: 4000s
+    traffic:
+      timeout:
+        http:
+          requestTimeout: 4000s
     destination:
       name: "second-route-dest"
       settings:
@@ -36,9 +38,10 @@ http:
           port: 50001
   - name: "third-route"
     hostname: "*"
-    timeout:
-      http:
-        requestTimeout: 0s
+    traffic:
+      timeout:
+        http:
+          requestTimeout: 0s
     destination:
       name: "third-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml
index 7dd532ab4..a706244db 100644
--- a/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml
@@ -56,8 +56,9 @@ http:
       - backendWeights:
           invalid: 0
           valid: 0
-        proxyProtocol:
-          version: "V2"
+        traffic:
+          proxyProtocol:
+            version: "V2"
         destination:
           name: httproute/envoy-gateway/httproute-btls-2/rule/0
           settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml
index a4682250a..008b5b9bd 100644
--- a/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml
@@ -10,15 +10,16 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - headerMatches:
-          - name: "x-user-id"
-            exact: "one"
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - headerMatches:
+            - name: "x-user-id"
+              exact: "one"
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "foo/bar"
     destination:
@@ -38,15 +39,16 @@ http:
             uri: https://192.168.1.250/jwt/public-key/jwks.json
   - name: "second-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - headerMatches:
-          - name: "x-user-id"
-            distinct: true
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - headerMatches:
+            - name: "x-user-id"
+              distinct: true
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "example"
     destination:
@@ -57,12 +59,13 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "test"
     destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml b/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml
index 9c76ca46b..2d8ef97ef 100644
--- a/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml
@@ -10,8 +10,9 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    loadBalancer:
-      roundRobin: {}
+    traffic:
+      loadBalancer:
+        roundRobin: {}
     destination:
       name: "first-route-dest"
       settings:
@@ -20,8 +21,9 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    loadBalancer:
-      random: {}
+    traffic:
+      loadBalancer:
+        random: {}
     destination:
       name: "second-route-dest"
       settings:
@@ -30,8 +32,9 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    loadBalancer:
-      leastRequest: {}
+    traffic:
+      loadBalancer:
+        leastRequest: {}
     destination:
       name: "third-route-dest"
       settings:
@@ -40,9 +43,10 @@ http:
           port: 50000
   - name: "fourth-route"
     hostname: "*"
-    loadBalancer:
-      consistentHash:
-        sourceIP: true
+    traffic:
+      loadBalancer:
+        consistentHash:
+          sourceIP: true
     destination:
       name: "fourth-route-dest"
       settings:
@@ -51,10 +55,11 @@ http:
           port: 50000
   - name: "fifth-route"
     hostname: "*"
-    loadBalancer:
-      leastRequest:
-        slowStart:
-          window: 60s
+    traffic:
+      loadBalancer:
+        leastRequest:
+          slowStart:
+            window: 60s
     destination:
       name: "fifth-route-dest"
       settings:
@@ -63,10 +68,11 @@ http:
           port: 50000
   - name: "sixth-route"
     hostname: "*"
-    loadBalancer:
-      roundRobin:
-        slowStart:
-          window: 300s
+    traffic:
+      loadBalancer:
+        roundRobin:
+          slowStart:
+            window: 300s
     destination:
       name: "sixth-route-dest"
       settings:
@@ -75,10 +81,11 @@ http:
           port: 50000
   - name: "seventh-route"
     hostname: "*"
-    loadBalancer:
-      consistentHash:
-        header:
-          name: name
+    traffic:
+      loadBalancer:
+        consistentHash:
+          header:
+            name: name
     destination:
       name: "seventh-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/local-ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/local-ratelimit.yaml
index 9e818b4d1..fb7baf05c 100644
--- a/internal/xds/translator/testdata/in/xds-ir/local-ratelimit.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/local-ratelimit.yaml
@@ -10,20 +10,21 @@ http:
   routes:
   - name: "first-route-ratelimit-single-rule"
     hostname: "*"
-    rateLimit:
-      local:
-        default:
-          requests: 10
-          unit: Minute
-        rules:
-        - headerMatches:
-          - name: x-user-id
-            exact: one
-          - name: x-org-id
-            exact: foo
-          limit:
+    traffic:
+      rateLimit:
+        local:
+          default:
             requests: 10
-            unit: Hour
+            unit: Minute
+          rules:
+          - headerMatches:
+            - name: x-user-id
+              exact: one
+            - name: x-org-id
+              exact: foo
+            limit:
+              requests: 10
+              unit: Hour
     pathMatch:
       exact: "foo/bar"
     destination:
@@ -34,31 +35,32 @@ http:
           port: 50000
   - name: "second-route-ratelimit-multiple-rules"
     hostname: "*"
-    rateLimit:
-      local:
-        default:
-          requests: 10
-          unit: Minute
-        rules:
-        - headerMatches:
-          - name: x-user-id
-            exact: one
-          - name: x-org-id
-            exact: foo
-          limit:
-            requests: 10
-            unit: Hour
-        - cidrMatch:
-            cidr: 192.168.0.0/16
-            maskLen: 16
-          headerMatches:
-          - name: x-user-id
-            exact: two
-          - name: x-org-id
-            exact: bar
-          limit:
+    traffic:
+      rateLimit:
+        local:
+          default:
             requests: 10
             unit: Minute
+          rules:
+          - headerMatches:
+            - name: x-user-id
+              exact: one
+            - name: x-org-id
+              exact: foo
+            limit:
+              requests: 10
+              unit: Hour
+          - cidrMatch:
+              cidr: 192.168.0.0/16
+              maskLen: 16
+            headerMatches:
+            - name: x-user-id
+              exact: two
+            - name: x-org-id
+              exact: bar
+            limit:
+              requests: 10
+              unit: Minute
     pathMatch:
       exact: "example"
     destination:
@@ -69,11 +71,12 @@ http:
           port: 50000
   - name: "third-route-ratelimit-no-rule"
     hostname: "*"
-    rateLimit:
-      local:
-        default:
-          requests: 10
-          unit: Minute
+    traffic:
+      rateLimit:
+        local:
+          default:
+            requests: 10
+            unit: Minute
     pathMatch:
       exact: "test"
     destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml b/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml
index adf21ca8f..47df0026b 100644
--- a/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml
@@ -10,8 +10,9 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    proxyProtocol:
-      version: "V2"
+    traffic:
+      proxyProtocol:
+        version: "V2"
     destination:
       name: "first-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml
index 8d9c2f5b4..271d39cfd 100644
--- a/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml
@@ -10,15 +10,16 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - headerMatches:
-          - name: "x-user-id"
-            exact: "one"
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - headerMatches:
+            - name: "x-user-id"
+              exact: "one"
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "foo/bar"
     destination:
@@ -29,15 +30,16 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - headerMatches:
-          - name: "x-user-id"
-            distinct: true
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - headerMatches:
+            - name: "x-user-id"
+              distinct: true
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "example"
     destination:
@@ -48,12 +50,13 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "test"
     destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-endpoint-stats.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-endpoint-stats.yaml
index 56a4b8d48..32f951172 100644
--- a/internal/xds/translator/testdata/in/xds-ir/ratelimit-endpoint-stats.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-endpoint-stats.yaml
@@ -12,15 +12,16 @@ http:
     routes:
       - name: "first-route"
         hostname: "*"
-        rateLimit:
-          global:
-            rules:
-              - headerMatches:
-                  - name: "x-user-id"
-                    exact: "one"
-                limit:
-                  requests: 5
-                  unit: second
+        traffic:
+          rateLimit:
+            global:
+              rules:
+                - headerMatches:
+                    - name: "x-user-id"
+                      exact: "one"
+                  limit:
+                    requests: 5
+                    unit: second
         pathMatch:
           exact: "foo/bar"
         destination:
@@ -31,15 +32,16 @@ http:
                   port: 50000
       - name: "second-route"
         hostname: "*"
-        rateLimit:
-          global:
-            rules:
-              - headerMatches:
-                  - name: "x-user-id"
-                    distinct: true
-                limit:
-                  requests: 5
-                  unit: second
+        traffic:
+          rateLimit:
+            global:
+              rules:
+                - headerMatches:
+                    - name: "x-user-id"
+                      distinct: true
+                  limit:
+                    requests: 5
+                    unit: second
         pathMatch:
           exact: "example"
         destination:
@@ -50,12 +52,13 @@ http:
                   port: 50000
       - name: "third-route"
         hostname: "*"
-        rateLimit:
-          global:
-            rules:
-              - limit:
-                  requests: 5
-                  unit: second
+        traffic:
+          rateLimit:
+            global:
+              rules:
+                - limit:
+                    requests: 5
+                    unit: second
         pathMatch:
           exact: "test"
         destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml
index 69b3480fc..495fa9b7a 100644
--- a/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml
@@ -10,15 +10,16 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - cidrMatch:
-            cidr: 192.168.0.0/16
-            maskLen: 16
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - cidrMatch:
+              cidr: 192.168.0.0/16
+              maskLen: 16
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "foo/bar"
     destination:
@@ -29,15 +30,16 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - cidrMatch:
-            cidr: 192.168.0.0/24
-            maskLen: 24
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - cidrMatch:
+              cidr: 192.168.0.0/24
+              maskLen: 24
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "example"
     destination:
@@ -48,12 +50,13 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "test"
     destination:
@@ -64,16 +67,17 @@ http:
           port: 50000
   - name: "fourth-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - cidrMatch:
-            cidr: 192.168.0.0/16
-            maskLen: 16
-            distinct: true
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - cidrMatch:
+              cidr: 192.168.0.0/16
+              maskLen: 16
+              distinct: true
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "distinct"
     destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml
index 8d9c2f5b4..271d39cfd 100644
--- a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml
@@ -10,15 +10,16 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - headerMatches:
-          - name: "x-user-id"
-            exact: "one"
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - headerMatches:
+            - name: "x-user-id"
+              exact: "one"
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "foo/bar"
     destination:
@@ -29,15 +30,16 @@ http:
           port: 50000
   - name: "second-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - headerMatches:
-          - name: "x-user-id"
-            distinct: true
-          limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - headerMatches:
+            - name: "x-user-id"
+              distinct: true
+            limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "example"
     destination:
@@ -48,12 +50,13 @@ http:
           port: 50000
   - name: "third-route"
     hostname: "*"
-    rateLimit:
-      global:
-        rules:
-        - limit:
-            requests: 5
-            unit: second
+    traffic:
+      rateLimit:
+        global:
+          rules:
+          - limit:
+              requests: 5
+              unit: second
     pathMatch:
       exact: "test"
     destination:
diff --git a/internal/xds/translator/testdata/in/xds-ir/retry-partial-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/retry-partial-invalid.yaml
index 7f5938fd6..748335672 100644
--- a/internal/xds/translator/testdata/in/xds-ir/retry-partial-invalid.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/retry-partial-invalid.yaml
@@ -10,21 +10,22 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    retry:
-      numRetries: 5
-      retryOn:
-        httpStatusCodes:
-        - 429
-        - 503
-        triggers:
-        - reset
-        - connect-failure
-        - retriable-status-codes
-      perRetry:
-        timeout: 250ms
-        backoff:
-          baseInterval: 100ms
-          maxInterval: 10s
+    traffic:
+      retry:
+        numRetries: 5
+        retryOn:
+          httpStatusCodes:
+          - 429
+          - 503
+          triggers:
+          - reset
+          - connect-failure
+          - retriable-status-codes
+        perRetry:
+          timeout: 250ms
+          backoff:
+            baseInterval: 100ms
+            maxInterval: 10s
     destination:
       name: "first-route-dest"
       settings:
@@ -33,7 +34,8 @@ http:
           port: 50000
   - name: "second-route-defaults"
     hostname: "foo"
-    retry: {}
+    traffic:
+      retry: {}
     destination:
       name: "first-route-dest"
       settings:
@@ -42,10 +44,11 @@ http:
           port: 50000
   - name: "third-route-error"
     hostname: "bar"
-    retry:
-      retryOn:
-        triggers:
-        - this-is-not-a-trigger
+    traffic:
+      retry:
+        retryOn:
+          triggers:
+          - this-is-not-a-trigger
     destination:
       name: "first-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/timeout.yaml b/internal/xds/translator/testdata/in/xds-ir/timeout.yaml
index daee154f0..8abc0af3c 100644
--- a/internal/xds/translator/testdata/in/xds-ir/timeout.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/timeout.yaml
@@ -10,12 +10,13 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    timeout:
-      tcp:
-        connectTimeout: "31s"
-      http:
-        connectionIdleTimeout: "32s"
-        maxConnectionDuration: "33s"
+    traffic:
+      timeout:
+        tcp:
+          connectTimeout: "31s"
+        http:
+          connectionIdleTimeout: "32s"
+          maxConnectionDuration: "33s"
     destination:
       name: "first-route-dest"
       settings:
diff --git a/internal/xds/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml b/internal/xds/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml
index bb7febec8..b00f5e55a 100644
--- a/internal/xds/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml
@@ -10,10 +10,11 @@ http:
   routes:
   - name: "first-route"
     hostname: "*"
-    tcpKeepalive:
-      idleTime: 1200
-      interval: 60
-      probes: 3
+    traffic:
+      tcpKeepalive:
+        idleTime: 1200
+        interval: 60
+        probes: 3
     destination:
       name: "first-route-dest"
       settings: