func buildListener()

in pilot/pkg/networking/core/v1alpha3/listener.go [1216:1376]


func buildListener(opts buildListenerOpts, trafficDirection core.TrafficDirection) *listener.Listener {
	filterChains := make([]*listener.FilterChain, 0, len(opts.filterChainOpts))
	listenerFiltersMap := make(map[string]bool)
	var listenerFilters []*listener.ListenerFilter

	// add a TLS inspector if we need to detect ServerName or ALPN
	// (this is not applicable for QUIC listeners)
	needTLSInspector := false
	if opts.transport == istionetworking.TransportProtocolTCP {
		for _, chain := range opts.filterChainOpts {
			needsALPN := chain.tlsContext != nil && chain.tlsContext.CommonTlsContext != nil && len(chain.tlsContext.CommonTlsContext.AlpnProtocols) > 0
			if len(chain.sniHosts) > 0 || needsALPN {
				needTLSInspector = true
				break
			}
		}
	}

	if opts.proxy.GetInterceptionMode() == model.InterceptionTproxy && trafficDirection == core.TrafficDirection_INBOUND {
		listenerFiltersMap[wellknown.OriginalSource] = true
		listenerFilters = append(listenerFilters, xdsfilters.OriginalSrc)
	}

	// We add a TLS inspector when http inspector is needed for outbound only. This
	// is because if we ever set ALPN in the match without
	// transport_protocol=raw_buffer, Envoy will automatically inject a tls
	// inspector: https://github.com/envoyproxy/envoy/issues/13601. This leads to
	// excessive logging and loss of control over the config For inbound this is not
	// needed, since we are explicitly setting transport protocol in every single
	// match. We can do this for outbound as well, at which point this could be
	// removed, but have not yet
	if opts.transport == istionetworking.TransportProtocolTCP &&
		(needTLSInspector || (opts.class == istionetworking.ListenerClassSidecarOutbound && opts.needHTTPInspector)) {
		listenerFiltersMap[wellknown.TlsInspector] = true
		listenerFilters = append(listenerFilters, xdsfilters.TLSInspector)
	}

	// TODO: For now we assume that only HTTP/3 is used over QUIC. Revisit this in the future
	if opts.needHTTPInspector && opts.transport == istionetworking.TransportProtocolTCP {
		listenerFiltersMap[wellknown.HttpInspector] = true
		listenerFilters = append(listenerFilters, xdsfilters.HTTPInspector)
	}

	for _, chain := range opts.filterChainOpts {
		for _, filter := range chain.listenerFilters {
			if _, exist := listenerFiltersMap[filter.Name]; !exist {
				listenerFiltersMap[filter.Name] = true
				listenerFilters = append(listenerFilters, filter)
			}
		}
		match := &listener.FilterChainMatch{}
		needMatch := false
		if chain.match != nil {
			needMatch = true
			match = chain.match
		}
		if len(chain.sniHosts) > 0 {
			fullWildcardFound := false
			for _, h := range chain.sniHosts {
				if h == "*" {
					fullWildcardFound = true
					// If we have a host with *, it effectively means match anything, i.e.
					// no SNI based matching for this host.
					break
				}
			}
			if !fullWildcardFound {
				chain.sniHosts = append([]string{}, chain.sniHosts...)
				sort.Stable(sort.StringSlice(chain.sniHosts))
				match.ServerNames = chain.sniHosts
			}
		}
		if len(chain.destinationCIDRs) > 0 {
			chain.destinationCIDRs = append([]string{}, chain.destinationCIDRs...)
			sort.Stable(sort.StringSlice(chain.destinationCIDRs))
			for _, d := range chain.destinationCIDRs {
				cidr := util.ConvertAddressToCidr(d)
				if cidr != nil && cidr.AddressPrefix != constants.UnspecifiedIP {
					match.PrefixRanges = append(match.PrefixRanges, cidr)
				}
			}
		}

		if !needMatch && filterChainMatchEmpty(match) {
			match = nil
		}
		var transportSocket *core.TransportSocket
		switch opts.transport {
		case istionetworking.TransportProtocolTCP:
			transportSocket = buildDownstreamTLSTransportSocket(chain.tlsContext)
		case istionetworking.TransportProtocolQUIC:
			transportSocket = buildDownstreamQUICTransportSocket(chain.tlsContext)
		}
		filterChains = append(filterChains, &listener.FilterChain{
			FilterChainMatch: match,
			TransportSocket:  transportSocket,
		})
	}

	var res *listener.Listener
	switch opts.transport {
	case istionetworking.TransportProtocolTCP:
		var bindToPort *wrappers.BoolValue
		var connectionBalance *listener.Listener_ConnectionBalanceConfig
		if !opts.bindToPort {
			bindToPort = proto.BoolFalse
		}

		// only use to exact_balance for tcp outbound listeners; virtualOutbound listener should
		// not have this set per Envoy docs for redirected listeners
		if opts.proxy.Metadata.OutboundListenerExactBalance && trafficDirection == core.TrafficDirection_OUTBOUND {
			connectionBalance = &listener.Listener_ConnectionBalanceConfig{
				BalanceType: &listener.Listener_ConnectionBalanceConfig_ExactBalance_{
					ExactBalance: &listener.Listener_ConnectionBalanceConfig_ExactBalance{},
				},
			}
		}

		res = &listener.Listener{
			// TODO: need to sanitize the opts.bind if its a UDS socket, as it could have colons, that envoy doesn't like
			Name:                    getListenerName(opts.bind, opts.port.Port, istionetworking.TransportProtocolTCP),
			Address:                 util.BuildAddress(opts.bind, uint32(opts.port.Port)),
			TrafficDirection:        trafficDirection,
			ListenerFilters:         listenerFilters,
			FilterChains:            filterChains,
			BindToPort:              bindToPort,
			ConnectionBalanceConfig: connectionBalance,
		}

		if opts.proxy.Type != model.Router {
			res.ListenerFiltersTimeout = opts.push.Mesh.ProtocolDetectionTimeout
			if res.ListenerFiltersTimeout != nil {
				res.ContinueOnListenerFiltersTimeout = true
			}
		}
	case istionetworking.TransportProtocolQUIC:
		// TODO: switch on TransportProtocolQUIC is in too many places now. Once this is a bit
		//       mature, refactor some of these to an interface so that they kick off the process
		//       of building listener, filter chains, serializing etc based on transport protocol
		listenerName := getListenerName(opts.bind, opts.port.Port, istionetworking.TransportProtocolQUIC)
		log.Debugf("buildListener: building UDP/QUIC listener %s", listenerName)
		res = &listener.Listener{
			Name:             listenerName,
			Address:          util.BuildNetworkAddress(opts.bind, uint32(opts.port.Port), istionetworking.TransportProtocolQUIC),
			TrafficDirection: trafficDirection,
			FilterChains:     filterChains,
			UdpListenerConfig: &listener.UdpListenerConfig{
				// TODO: Maybe we should add options in MeshConfig to
				//       configure QUIC options - it should look similar
				//       to the H2 protocol options.
				QuicOptions:            &listener.QuicProtocolOptions{},
				DownstreamSocketConfig: &core.UdpSocketConfig{},
			},
			EnableReusePort: proto.BoolTrue,
		}
	}

	accessLogBuilder.setListenerAccessLog(opts.push, opts.proxy, res, opts.class)

	return res
}