Skip to content

fix: honor TCPRoute/UDPRoute sectionName and listener port for StreamRoute matching - #445

Open
AlinsRan wants to merge 2 commits into
masterfrom
ee-2802-l4-server-port
Open

fix: honor TCPRoute/UDPRoute sectionName and listener port for StreamRoute matching#445
AlinsRan wants to merge 2 commits into
masterfrom
ee-2802-l4-server-port

Conversation

@AlinsRan

@AlinsRan AlinsRan commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Type of change:

  • Bugfix

What this PR does / why we need it:

Sync from apache/apisix-ingress-controller#2818 (fixes apache/apisix-ingress-controller#2802).

TranslateTCPRoute / TranslateUDPRoute emitted a single StreamRoute with no match criteria (the old // TODO: ... server_port). In APISIX stream mode a StreamRoute without server_port matches every connection on any stream listener, so all L4 routes across all Gateways collided onto one backend — parentRefs.sectionName / per-listener port routing were ignored.

This makes TCPRoute/UDPRoute participate in the existing listener_port_match_mode mechanism (added for HTTPRoute/GRPCRoute in #419):

  • Controllers (tcproute_controller.go, udproute_controller.go): populate tctx.Listeners from the listeners ParseRouteParentRefs matched for each parentRef (honoring sectionName, port, protocol, allowedRoutes), mirroring the HTTPRoute/GRPCRoute path.
  • Translator (tcproute.go, udproute.go): buildL4StreamRoutes sets StreamRoute.server_port from the matched listener port(s), one StreamRoute per port with a port-qualified name/ID, gated by the same shouldInjectServerPortVars.

Rebased on latest master

An earlier revision of this PR carried a self-contained gating helper because EE master did not yet have listener_port_match_mode. Master has since merged port-based routing (#419), so this PR is rebuilt on master and now reuses listener_port_match_mode directly — no duplicated gating, no new config. Diff is line-for-line equivalent to the upstream OSS PR #2818.

Backward compatibility

listener_port_match_mode defaults to off, so L4 behavior is unchanged by default (single portless StreamRoute) — no upgrade risk. Isolation is opt-in: set listener_port_match_mode: auto/explicit and make the Gateway listener port equal APISIX's physical stream_proxy port (9100/9200), same contract as HTTPRoute.

Limitation: server_port isolates across distinct listener ports only; two L4 routes on the same physical port can't be disambiguated (no SNI/host at bare TCP/UDP).

Pre-submission checklist:

  • Test cases added (l4route_serverport_test.go, exercised in auto mode)
  • Backward compatible (default off → unchanged L4 behavior)

Summary by CodeRabbit

  • New Features

    • L4 TCP and UDP routes now include the matched listener port when appropriate.
    • Routes targeting multiple listener ports generate distinct stream routes for each port.
    • Duplicate listener ports are consolidated to avoid redundant routes.
    • Existing portless fallback behavior is preserved when no specific listener port applies.
  • Tests

    • Added coverage for listener targeting, multi-listener routing, deduplication, and fallback scenarios.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 4 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ace6285-15d8-4633-992e-0ed713584a9d

📥 Commits

Reviewing files that changed from the base of the PR and between 3f520d1 and 0271d26.

📒 Files selected for processing (2)
  • internal/adc/translator/l4route_serverport_test.go
  • internal/adc/translator/tcproute.go
📝 Walkthrough

Walkthrough

TCP and UDP reconciliation now passes matched Gateway listeners into translation. Shared L4 route construction injects selected listener ports into StreamRoutes, supports fan-out and deduplication, preserves portless fallback, and adds unit coverage.

Changes

L4 listener port isolation

Layer / File(s) Summary
Propagate matched listeners
internal/controller/tcproute_controller.go, internal/controller/udproute_controller.go
TCP and UDP reconcilers append matched single or multiple Gateway listeners to the translation context.
Construct port-aware StreamRoutes
internal/adc/translator/tcproute.go, internal/adc/translator/udproute.go
Shared construction selects, deduplicates, sorts, and injects listener ports into TCP and UDP StreamRoutes, while retaining portless fallback behavior.
Validate listener-specific routing
internal/adc/translator/l4route_serverport_test.go
Tests cover explicit listener targeting, multi-listener fan-out, duplicate-port removal, fallback behavior, and generated route identity uniqueness.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GatewayProcessing
  participant RouteTranslation
  participant StreamRoutes
  GatewayProcessing->>RouteTranslation: populate matched listeners
  RouteTranslation->>RouteTranslation: select and deduplicate listener ports
  RouteTranslation->>StreamRoutes: create port-specific or portless routes
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning Only unit-level translator tests were added; no end-to-end controller/provider/APISIX flow validates the new listener-port behavior. Add an E2E/integration test that reconciles a Gateway + TCPRoute/UDPRoute and verifies only the targeted listener port routes to the backend.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The TCPRoute changes and controller context updates address the reported listener/sectionName mismatch described in #2802.
Out of Scope Changes check ✅ Passed The UDPRoute updates and new tests align with the stated bugfix scope and appear related to the same StreamRoute matching behavior.
Security Check ✅ Passed No security issues found; changes only adjust listener matching/server_port routing and add tests, with no secret logging, auth, DB, TLS, or ownership changes.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main bugfix: honoring sectionName and listener ports in TCPRoute/UDPRoute StreamRoute matching.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ee-2802-l4-server-port

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-29T08:16:18Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.6.0
implementation:
  contact:
  - https://github.com/apache/apisix-ingress-controller/issues
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    - HTTPRouteInvalidBackendRefUnknownKind
    - HTTPRouteInvalidCrossNamespaceBackendRef
    - HTTPRouteInvalidNonExistentBackendRef
    - HTTPRouteListenerHostnameMatching
    - HTTPRouteMultipleGateways
    - HTTPRouteNoBackendRefs
    statistics:
      Failed: 0
      Passed: 30
      Skipped: 7
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - BackendTLSPolicy
    - BackendTLSPolicySANValidation
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRoute303RedirectStatusCode
    - HTTPRoute307RedirectStatusCode
    - HTTPRoute308RedirectStatusCode
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteCORS
    - HTTPRouteNamedRouteRule
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
    - HTTPRouteRetry
    - HTTPRouteRetryBackendTimeout
    - HTTPRouteRetryConnectionError
    - ListenerSet
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 7 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - GRPCRouteListenerHostnameMatching
    statistics:
      Failed: 0
      Passed: 14
      Skipped: 1
  extended:
    result: success
    statistics:
      Failed: 0
      Passed: 1
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
  name: GATEWAY-GRPC
  summary: Core tests partially succeeded with 1 test skips. Extended tests succeeded.
- core:
    result: partial
    skippedTests:
    - TLSRouteHostnameIntersection
    - TLSRouteInvalidBackendRefNonexistent
    - TLSRouteInvalidBackendRefUnknownKind
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 16
      Skipped: 4
  extended:
    result: partial
    skippedTests:
    - TLSRouteTerminateSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 3
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - TLSRouteModeTerminate
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
    - TLSRouteModeMixed
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 4 test skips. Extended tests partially
    succeeded with 1 test skips.
succeededProvisionalTests:
- GatewayOptionalAddressValue

@AlinsRan AlinsRan self-assigned this Jul 22, 2026
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-29T08:16:02Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.6.0
implementation:
  contact:
  - https://github.com/apache/apisix-ingress-controller/issues
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: partial
    skippedTests:
    - TLSRouteHostnameIntersection
    - TLSRouteInvalidBackendRefNonexistent
    - TLSRouteInvalidBackendRefUnknownKind
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 16
      Skipped: 4
  extended:
    result: partial
    skippedTests:
    - TLSRouteTerminateSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 3
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - TLSRouteModeTerminate
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
    - TLSRouteModeMixed
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 4 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    - HTTPRouteInvalidBackendRefUnknownKind
    - HTTPRouteInvalidCrossNamespaceBackendRef
    - HTTPRouteInvalidNonExistentBackendRef
    - HTTPRouteListenerHostnameMatching
    - HTTPRouteMultipleGateways
    - HTTPRouteNoBackendRefs
    statistics:
      Failed: 0
      Passed: 30
      Skipped: 7
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - BackendTLSPolicy
    - BackendTLSPolicySANValidation
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRoute303RedirectStatusCode
    - HTTPRoute307RedirectStatusCode
    - HTTPRoute308RedirectStatusCode
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteCORS
    - HTTPRouteNamedRouteRule
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
    - HTTPRouteRetry
    - HTTPRouteRetryBackendTimeout
    - HTTPRouteRetryConnectionError
    - ListenerSet
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 7 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - GRPCRouteListenerHostnameMatching
    statistics:
      Failed: 0
      Passed: 14
      Skipped: 1
  extended:
    result: success
    statistics:
      Failed: 0
      Passed: 1
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
  name: GATEWAY-GRPC
  summary: Core tests partially succeeded with 1 test skips. Extended tests succeeded.
succeededProvisionalTests:
- GatewayOptionalAddressValue

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-29T08:35:38Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.6.0
implementation:
  contact:
  - https://github.com/apache/apisix-ingress-controller/issues
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GatewayModifyListeners
    - HTTPRouteMultipleGateways
    - HTTPRouteNoBackendRefs
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 3
      Passed: 33
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - BackendTLSPolicy
    - BackendTLSPolicySANValidation
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRoute303RedirectStatusCode
    - HTTPRoute307RedirectStatusCode
    - HTTPRoute308RedirectStatusCode
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteCORS
    - HTTPRouteNamedRouteRule
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
    - HTTPRouteRetry
    - HTTPRouteRetryBackendTimeout
    - HTTPRouteRetryConnectionError
    - ListenerSet
  name: GATEWAY-HTTP
  summary: Core tests failed with 3 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 1
      Passed: 14
      Skipped: 0
  extended:
    result: success
    statistics:
      Failed: 0
      Passed: 1
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
  name: GATEWAY-GRPC
  summary: Core tests failed with 1 test failures. Extended tests succeeded.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteHostnameIntersection
    - TLSRouteInvalidBackendRefNonexistent
    - TLSRouteInvalidBackendRefUnknownKind
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 5
      Passed: 15
      Skipped: 0
  extended:
    failedTests:
    - TLSRouteTerminateSimpleSameNamespace
    result: failure
    statistics:
      Failed: 1
      Passed: 3
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - TLSRouteModeTerminate
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
    - TLSRouteModeMixed
  name: GATEWAY-TLS
  summary: Core tests failed with 5 test failures. Extended tests failed with 1 test
    failures.
succeededProvisionalTests:
- GatewayOptionalAddressValue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/controller/tcproute_controller.go (1)

291-303: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Zero-value listener leak into tctx.Listeners for rejected parentRefs, in both TCP and UDP reconcilers. Root cause: RouteParentRefContext.Listener (set in internal/controller/utils.go's ParseRouteParentRefs) is never nil — it points to a zero-value gatewayv1.Listener{} even when the parentRef didn't match any listener — so the new else if gateway.Listener != nil fallback added in both reconcilers appends a bogus Listener{Port: 0} whenever a route has a mix of accepted and rejected parentRefs, poisoning the port set the translator uses for server_port isolation.

  • internal/controller/tcproute_controller.go#L291-L303: guard the fallback, e.g. else if gateway.Listener != nil && gateway.Listener.Port != 0.
  • internal/controller/udproute_controller.go#L291-L303: apply the same guard.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/tcproute_controller.go` around lines 291 - 303, Prevent
zero-value listeners from being appended in the gateway listener population
logic: in internal/controller/tcproute_controller.go lines 291-303 and
internal/controller/udproute_controller.go lines 291-303, update the fallback
within the ProcessGatewayProxy loop to require gateway.Listener.Port != 0 in
addition to gateway.Listener != nil. Keep the existing gateway.Listeners
handling unchanged.
internal/controller/utils.go (1)

373-442: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

internal/controller/utils.go: keep collecting all matching listeners

ParseRouteParentRefs is meant to fill RouteParentRefContext.Listeners with every compatible listener for a parentRef, but the unconditional break stops at the first match. For TCP/UDP routes that leaves tctx.Listeners with one port, so multi-port fan-out never kicks in; keep Listener/ListenerName on the first hit, but continue scanning and appending to Listeners.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/utils.go` around lines 373 - 442, Remove the
unconditional break after a successful match in ParseRouteParentRefs so the loop
continues scanning all listeners compatible with the parentRef. Preserve
ListenerName and Listener from the first match, while appending every subsequent
compatible listener to matchedListeners for RouteParentRefContext.Listeners and
multi-port fan-out.

Source: Coding guidelines

🧹 Nitpick comments (2)
internal/adc/translator/tcproute.go (1)

46-52: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded "Gateway" kind literal.

Duplicates the Gateway kind check already expressed via KindGateway in internal/controller/utils.go. Cross-package, so may not be trivially shareable, but worth a local constant to avoid silent divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adc/translator/tcproute.go` around lines 46 - 52, Replace the
hardcoded "Gateway" kind comparison in hasExplicitListenerTarget with a local
named constant representing the Gateway kind, keeping the existing non-Gateway
filtering behavior unchanged and aligned with the KindGateway value used
elsewhere.
internal/adc/translator/l4route_serverport_test.go (1)

62-144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Good translator-level coverage, but doesn't exercise the real ParseRouteParentRefstctx.Listeners wiring.

These tests inject tctx.Listeners directly, so the "multiple listener ports fan out even without explicit targeting" case never runs through ParseRouteParentRefs, where the loop's break (flagged separately in internal/controller/utils.go) actually prevents that scenario from ever populating more than one listener in production. Consider adding a ParseRouteParentRefs-level test for an untargeted parentRef matching multiple compatible listeners on one Gateway to catch this class of regression.

Also applies to: 146-216

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adc/translator/l4route_serverport_test.go` around lines 62 - 144,
Add coverage for the ParseRouteParentRefs path rather than only assigning
tctx.Listeners directly in TestTranslateTCPRouteServerPort. Create an untargeted
parentRef matching multiple compatible listeners on the same Gateway, invoke
ParseRouteParentRefs, and assert that all matching listeners populate the
translation context so TCP route translation fans out across their ports.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/controller/tcproute_controller.go`:
- Around line 291-303: Prevent zero-value listeners from being appended in the
gateway listener population logic: in internal/controller/tcproute_controller.go
lines 291-303 and internal/controller/udproute_controller.go lines 291-303,
update the fallback within the ProcessGatewayProxy loop to require
gateway.Listener.Port != 0 in addition to gateway.Listener != nil. Keep the
existing gateway.Listeners handling unchanged.

In `@internal/controller/utils.go`:
- Around line 373-442: Remove the unconditional break after a successful match
in ParseRouteParentRefs so the loop continues scanning all listeners compatible
with the parentRef. Preserve ListenerName and Listener from the first match,
while appending every subsequent compatible listener to matchedListeners for
RouteParentRefContext.Listeners and multi-port fan-out.

---

Nitpick comments:
In `@internal/adc/translator/l4route_serverport_test.go`:
- Around line 62-144: Add coverage for the ParseRouteParentRefs path rather than
only assigning tctx.Listeners directly in TestTranslateTCPRouteServerPort.
Create an untargeted parentRef matching multiple compatible listeners on the
same Gateway, invoke ParseRouteParentRefs, and assert that all matching
listeners populate the translation context so TCP route translation fans out
across their ports.

In `@internal/adc/translator/tcproute.go`:
- Around line 46-52: Replace the hardcoded "Gateway" kind comparison in
hasExplicitListenerTarget with a local named constant representing the Gateway
kind, keeping the existing non-Gateway filtering behavior unchanged and aligned
with the KindGateway value used elsewhere.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81ff22b5-f4d5-4964-b6ac-317ae84db223

📥 Commits

Reviewing files that changed from the base of the PR and between e2a2ff4 and e356616.

📒 Files selected for processing (9)
  • internal/adc/translator/l4route_serverport_test.go
  • internal/adc/translator/tcproute.go
  • internal/adc/translator/udproute.go
  • internal/controller/context.go
  • internal/controller/tcproute_controller.go
  • internal/controller/udproute_controller.go
  • internal/controller/utils.go
  • test/e2e/gatewayapi/tcproute.go
  • test/e2e/gatewayapi/udproute.go

…Route matching

TranslateTCPRoute/TranslateUDPRoute emitted a single StreamRoute with no
match criteria, so every TCP/UDP connection on any stream listener matched
it and all L4 routes collided onto one backend, ignoring parentRefs.sectionName
and per-listener port.

Make TCPRoute/UDPRoute participate in the existing listener_port_match_mode
(added for HTTPRoute/GRPCRoute in #419): the controllers populate tctx.Listeners
from the listeners ParseRouteParentRefs matched for each parentRef, and the
translator sets StreamRoute.server_port from the matched listener port(s), one
StreamRoute per port, gated by shouldInjectServerPortVars. Default mode is off,
so L4 behavior is unchanged unless the user opts into auto/explicit.

Sync from apache/apisix-ingress-controller#2818 (fixes #2802).
@AlinsRan
AlinsRan force-pushed the ee-2802-l4-server-port branch from 666ee3c to 3f520d1 Compare July 29, 2026 07:12
gateway-api v1.6.0 defines PortNumber as an int32 alias, so the explicit
int32()/PortNumber() conversions are no-ops that fail the unconvert linter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Gateway API TCPRoute ignores parentRefs.sectionName / listener port, causing all TCP listeners to match the same backend

1 participant