@@ -217,19 +217,118 @@ func validClientCertificateRef(ref *gwapiv1.SecretObjectReference) error {
217217 return nil
218218}
219219
220+ // allowedRouteKindsForProtocol returns the route kinds supported by the given listener protocol.
221+ func allowedRouteKindsForProtocol (protocol gwapiv1.ProtocolType , tlsMode * gwapiv1.TLSModeType ) []gwapiv1.Kind {
222+ switch protocol {
223+ case gwapiv1 .HTTPProtocolType , gwapiv1 .HTTPSProtocolType :
224+ return []gwapiv1.Kind {resource .KindHTTPRoute , resource .KindGRPCRoute }
225+ case gwapiv1 .TLSProtocolType :
226+ if tlsMode != nil && * tlsMode == gwapiv1 .TLSModePassthrough {
227+ return []gwapiv1.Kind {resource .KindTLSRoute }
228+ }
229+ // Terminate mode or unspecified defaults to accept both TCP and TLS routes
230+ return []gwapiv1.Kind {resource .KindTCPRoute , resource .KindTLSRoute }
231+ case gwapiv1 .TCPProtocolType :
232+ return []gwapiv1.Kind {resource .KindTCPRoute }
233+ case gwapiv1 .UDPProtocolType :
234+ return []gwapiv1.Kind {resource .KindUDPRoute }
235+ default :
236+ return nil
237+ }
238+ }
239+
240+ // validateProtocolRules validates protocol-specific constraints (hostname, TLS requirements).
241+ // Returns true if all constraints are satisfied, false otherwise.
242+ func validateProtocolRules (listener * ListenerContext ) bool {
243+ switch listener .Protocol {
244+ case gwapiv1 .HTTPProtocolType , gwapiv1 .HTTPSProtocolType , gwapiv1 .TLSProtocolType ,
245+ gwapiv1 .TCPProtocolType , gwapiv1 .UDPProtocolType :
246+ // All supported protocols pass basic validation here.
247+ // Protocol-specific constraints (TLS, hostname) are validated in
248+ // validateTLSConfiguration and validateHostName respectively,
249+ // where they can set proper conditions.
250+ return true
251+
252+ default :
253+ // Unsupported protocol handled separately
254+ return false
255+ }
256+ }
257+
258+ func (t * Translator ) validateListenerSpec (listener * ListenerContext , resources * resource.Resources ) bool {
259+ // Validate listener spec directly without relying on conditions.
260+ // Start with valid assumption and invalidate on failures.
261+
262+ // Phase 1: Validate fundamental rules
263+ specValid := t .validateAllowedNamespaces (listener )
264+ if ! validateProtocolRules (listener ) {
265+ specValid = false
266+ }
267+
268+ // Phase 2: Validate allowed routes based on protocol
269+ if listener .Protocol == gwapiv1 .HTTPProtocolType ||
270+ listener .Protocol == gwapiv1 .HTTPSProtocolType ||
271+ listener .Protocol == gwapiv1 .TCPProtocolType ||
272+ listener .Protocol == gwapiv1 .UDPProtocolType ||
273+ listener .Protocol == gwapiv1 .TLSProtocolType {
274+ var tlsMode * gwapiv1.TLSModeType
275+ if listener .TLS != nil {
276+ tlsMode = listener .TLS .Mode
277+ }
278+ allowedKinds := allowedRouteKindsForProtocol (listener .Protocol , tlsMode )
279+ if ! t .validateAllowedRoutes (listener , allowedKinds ... ) {
280+ specValid = false
281+ }
282+ } else {
283+ // Unsupported protocol
284+ specValid = false
285+ listener .SetSupportedKinds ()
286+ listener .SetCondition (
287+ gwapiv1 .ListenerConditionAccepted ,
288+ metav1 .ConditionFalse ,
289+ gwapiv1 .ListenerReasonUnsupportedProtocol ,
290+ fmt .Sprintf ("Protocol %s is unsupported, must be %s, %s, %s or %s." , listener .Protocol ,
291+ gwapiv1 .HTTPProtocolType , gwapiv1 .HTTPSProtocolType , gwapiv1 .TCPProtocolType , gwapiv1 .UDPProtocolType ),
292+ )
293+ }
294+
295+ // Phase 3: Validate TLS configuration details
296+ if ! t .validateTLSConfiguration (listener , resources ) {
297+ specValid = false
298+ }
299+
300+ // Phase 4: Validate Hostname configuration
301+ if ! t .validateHostName (listener ) {
302+ specValid = false
303+ }
304+
305+ return specValid
306+ }
307+
220308func (t * Translator ) ProcessListeners (gateways []* GatewayContext , xdsIR resource.XdsIRMap , infraIR resource.InfraIRMap , resources * resource.Resources ) {
221309 // Infra IR proxy ports must be unique.
222310 foundPorts := make (map [string ][]* protocolPort )
311+
312+ // Phase 1: Validate each listener's spec independently.
313+ // This must happen before conflict resolution so that invalid listeners
314+ // don't block valid ones during conflict detection.
315+ for _ , gateway := range gateways {
316+ for _ , listener := range gateway .listeners {
317+ listener .specValid = t .validateListenerSpec (listener , resources )
318+ }
319+ }
320+
321+ // Phase 2: Run conflict detection.
322+ // Only listeners that haven't been marked as invalid will participate in conflict resolution.
323+ t .validateConflictedProtocolsListeners (gateways )
223324 t .validateConflictedLayer7Listeners (gateways )
224325 t .validateConflictedLayer4Listeners (gateways , gwapiv1 .TCPProtocolType )
225326 t .validateConflictedLayer4Listeners (gateways , gwapiv1 .UDPProtocolType )
226327 if t .MergeGateways {
227328 t .validateConflictedMergedListeners (gateways )
228329 }
229330
230- // Iterate through all listeners to validate spec
231- // and compute status for each, and add valid ones
232- // to the Xds IR.
331+ // Phase 3: Build IR for valid listeners.
233332 for _ , gateway := range gateways {
234333 irKey := t .getIRKey (gateway .Gateway )
235334
@@ -240,49 +339,12 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
240339 t .processProxyObservability (gateway , xdsIR [irKey ], infraIR [irKey ].Proxy , resources )
241340
242341 for _ , listener := range gateway .listeners {
243- // Process protocol & supported kinds
244- switch listener .Protocol {
245- case gwapiv1 .TLSProtocolType :
246- if listener .TLS != nil {
247- switch * listener .TLS .Mode {
248- case gwapiv1 .TLSModePassthrough :
249- t .validateAllowedRoutes (listener , resource .KindTLSRoute )
250- case gwapiv1 .TLSModeTerminate :
251- t .validateAllowedRoutes (listener , resource .KindTCPRoute , resource .KindTLSRoute )
252- default :
253- t .validateAllowedRoutes (listener , resource .KindTCPRoute , resource .KindTLSRoute )
254- }
255- } else {
256- t .validateAllowedRoutes (listener , resource .KindTCPRoute , resource .KindTLSRoute )
257- }
258- case gwapiv1 .HTTPProtocolType , gwapiv1 .HTTPSProtocolType :
259- t .validateAllowedRoutes (listener , resource .KindHTTPRoute , resource .KindGRPCRoute )
260- case gwapiv1 .TCPProtocolType :
261- t .validateAllowedRoutes (listener , resource .KindTCPRoute )
262- case gwapiv1 .UDPProtocolType :
263- t .validateAllowedRoutes (listener , resource .KindUDPRoute )
264- default :
265- listener .SetSupportedKinds ()
266- listener .SetCondition (
267- gwapiv1 .ListenerConditionAccepted ,
268- metav1 .ConditionFalse ,
269- gwapiv1 .ListenerReasonUnsupportedProtocol ,
270- fmt .Sprintf ("Protocol %s is unsupported, must be %s, %s, %s or %s." , listener .Protocol ,
271- gwapiv1 .HTTPProtocolType , gwapiv1 .HTTPSProtocolType , gwapiv1 .TCPProtocolType , gwapiv1 .UDPProtocolType ),
272- )
273- }
274-
275- // Validate allowed namespaces
276- t .validateAllowedNamespaces (listener )
277-
278- // Process TLS configuration
279- t .validateTLSConfiguration (listener , resources )
280-
281- // Process Hostname configuration
282- t .validateHostName (listener )
283-
284- // Process conditions and check if the listener is ready
342+ // Finalize listener conditions and check readiness.
285343 t .validateListenerConditions (listener )
344+ if ! listener .IsReady () {
345+ // Skip invalid listeners from IR building
346+ continue
347+ }
286348
287349 // Skip listeners with invalid frontend TLS validation as they are not functional.
288350 if listener .frontendTLSValidationInvalid () {
@@ -425,10 +487,18 @@ func checkOverlappingHostnames(httpsListeners []*ListenerContext) {
425487 if overlappingListeners [i ] != nil {
426488 continue
427489 }
490+ // Skip listeners that are already marked as invalid from per-listener validation
491+ if hasInvalidCondition (httpsListeners [i ]) {
492+ continue
493+ }
428494 for j := i + 1 ; j < len (httpsListeners ); j ++ {
429495 if overlappingListeners [j ] != nil {
430496 continue
431497 }
498+ // Skip listeners that are already marked as invalid from per-listener validation
499+ if hasInvalidCondition (httpsListeners [j ]) {
500+ continue
501+ }
432502 if httpsListeners [i ].Port != httpsListeners [j ].Port {
433503 continue
434504 }
@@ -507,10 +577,18 @@ func checkOverlappingCertificates(httpsListeners []*ListenerContext) {
507577 if overlappingListeners [i ] != nil {
508578 continue
509579 }
580+ // Skip listeners that are already marked as invalid from per-listener validation
581+ if hasInvalidCondition (httpsListeners [i ]) {
582+ continue
583+ }
510584 for j := i + 1 ; j < len (httpsListeners ); j ++ {
511585 if overlappingListeners [j ] != nil {
512586 continue
513587 }
588+ // Skip listeners that are already marked as invalid from per-listener validation
589+ if hasInvalidCondition (httpsListeners [j ]) {
590+ continue
591+ }
514592 if httpsListeners [i ].Port != httpsListeners [j ].Port {
515593 continue
516594 }
0 commit comments