github.com/cilium/cilium@v1.16.2/pkg/envoy/xds_server.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package envoy
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net"
    10  	"os"
    11  	"slices"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  
    16  	cilium "github.com/cilium/proxy/go/cilium/api"
    17  	envoy_mysql_proxy "github.com/cilium/proxy/go/contrib/envoy/extensions/filters/network/mysql_proxy/v3"
    18  	envoy_config_cluster "github.com/cilium/proxy/go/envoy/config/cluster/v3"
    19  	envoy_config_core "github.com/cilium/proxy/go/envoy/config/core/v3"
    20  	envoy_config_endpoint "github.com/cilium/proxy/go/envoy/config/endpoint/v3"
    21  	envoy_config_listener "github.com/cilium/proxy/go/envoy/config/listener/v3"
    22  	envoy_config_route "github.com/cilium/proxy/go/envoy/config/route/v3"
    23  	envoy_extensions_filters_http_router_v3 "github.com/cilium/proxy/go/envoy/extensions/filters/http/router/v3"
    24  	envoy_upstream_codec "github.com/cilium/proxy/go/envoy/extensions/filters/http/upstream_codec/v3"
    25  	envoy_extensions_listener_tls_inspector_v3 "github.com/cilium/proxy/go/envoy/extensions/filters/listener/tls_inspector/v3"
    26  	envoy_config_http "github.com/cilium/proxy/go/envoy/extensions/filters/network/http_connection_manager/v3"
    27  	envoy_mongo_proxy "github.com/cilium/proxy/go/envoy/extensions/filters/network/mongo_proxy/v3"
    28  	envoy_config_tcp "github.com/cilium/proxy/go/envoy/extensions/filters/network/tcp_proxy/v3"
    29  	envoy_config_tls "github.com/cilium/proxy/go/envoy/extensions/transport_sockets/tls/v3"
    30  	envoy_type_matcher "github.com/cilium/proxy/go/envoy/type/matcher/v3"
    31  	"github.com/cilium/proxy/pkg/policy/api/kafka"
    32  	"github.com/sirupsen/logrus"
    33  	"google.golang.org/protobuf/proto"
    34  	"google.golang.org/protobuf/types/known/anypb"
    35  	"google.golang.org/protobuf/types/known/durationpb"
    36  	"google.golang.org/protobuf/types/known/wrapperspb"
    37  
    38  	"github.com/cilium/cilium/pkg/bpf"
    39  	"github.com/cilium/cilium/pkg/completion"
    40  	"github.com/cilium/cilium/pkg/crypto/certificatemanager"
    41  	"github.com/cilium/cilium/pkg/endpointstate"
    42  	_ "github.com/cilium/cilium/pkg/envoy/resource"
    43  	"github.com/cilium/cilium/pkg/envoy/xds"
    44  	"github.com/cilium/cilium/pkg/lock"
    45  	"github.com/cilium/cilium/pkg/logging/logfields"
    46  	"github.com/cilium/cilium/pkg/option"
    47  	"github.com/cilium/cilium/pkg/policy"
    48  	"github.com/cilium/cilium/pkg/policy/api"
    49  	"github.com/cilium/cilium/pkg/promise"
    50  	"github.com/cilium/cilium/pkg/proxy/endpoint"
    51  	"github.com/cilium/cilium/pkg/time"
    52  	"github.com/cilium/cilium/pkg/u8proto"
    53  )
    54  
    55  var (
    56  	// allowAllPortNetworkPolicy is a PortNetworkPolicy that allows all traffic
    57  	// to any L4 port.
    58  	allowAllTCPPortNetworkPolicy = &cilium.PortNetworkPolicy{
    59  		// Allow all TCP traffic to any port.
    60  		Protocol: envoy_config_core.SocketAddress_TCP,
    61  	}
    62  	allowAllPortNetworkPolicy = []*cilium.PortNetworkPolicy{
    63  		// Allow all TCP traffic to any port.
    64  		allowAllTCPPortNetworkPolicy,
    65  		// Allow all UDP/SCTP traffic to any port.
    66  		// UDP/SCTP rules not sent to Envoy for now.
    67  	}
    68  )
    69  
    70  const (
    71  	CiliumXDSClusterName = "xds-grpc-cilium"
    72  
    73  	adminClusterName      = "/envoy-admin"
    74  	egressClusterName     = "egress-cluster"
    75  	egressTLSClusterName  = "egress-cluster-tls"
    76  	ingressClusterName    = "ingress-cluster"
    77  	ingressTLSClusterName = "ingress-cluster-tls"
    78  	metricsListenerName   = "envoy-prometheus-metrics-listener"
    79  	adminListenerName     = "envoy-admin-listener"
    80  )
    81  
    82  type Listener struct {
    83  	// must hold the xdsServer.mutex when accessing 'count'
    84  	count uint
    85  
    86  	// mutex is needed when accessing the fields below.
    87  	// xdsServer.mutex is not needed, but if taken it must be taken before 'mutex'
    88  	mutex   lock.RWMutex
    89  	acked   bool
    90  	nacked  bool
    91  	waiters []*completion.Completion
    92  }
    93  
    94  // XDSServer provides a high-lever interface to manage resources published using the xDS gRPC API.
    95  type XDSServer interface {
    96  	// AddListener adds a listener to a running Envoy proxy.
    97  	AddListener(name string, kind policy.L7ParserType, port uint16, isIngress bool, mayUseOriginalSourceAddr bool, wg *completion.WaitGroup)
    98  	// AddAdminListener adds an Admin API listener to Envoy.
    99  	AddAdminListener(port uint16, wg *completion.WaitGroup)
   100  	// AddMetricsListener adds a prometheus metrics listener to Envoy.
   101  	AddMetricsListener(port uint16, wg *completion.WaitGroup)
   102  	// RemoveListener removes an existing Envoy Listener.
   103  	RemoveListener(name string, wg *completion.WaitGroup) xds.AckingResourceMutatorRevertFunc
   104  
   105  	// UpsertEnvoyResources inserts or updates Envoy resources in 'resources' to the xDS cache,
   106  	// from where they will be delivered to Envoy via xDS streaming gRPC.
   107  	UpsertEnvoyResources(ctx context.Context, resources Resources) error
   108  	// UpdateEnvoyResources removes any resources in 'old' that are not
   109  	// present in 'new' and then adds or updates all resources in 'new'.
   110  	// Envoy does not support changing the listening port of an existing
   111  	// listener, so if the port changes we have to delete the old listener
   112  	// and then add the new one with the new port number.
   113  	UpdateEnvoyResources(ctx context.Context, old, new Resources) error
   114  	// DeleteEnvoyResources deletes all Envoy resources in 'resources'.
   115  	DeleteEnvoyResources(ctx context.Context, resources Resources) error
   116  
   117  	// GetNetworkPolicies returns the current version of the network policies with the given names.
   118  	// If resourceNames is empty, all resources are returned.
   119  	//
   120  	// Only used for testing
   121  	GetNetworkPolicies(resourceNames []string) (map[string]*cilium.NetworkPolicy, error)
   122  	// UpdateNetworkPolicy adds or updates a network policy in the set published to L7 proxies.
   123  	// When the proxy acknowledges the network policy update, it will result in
   124  	// a subsequent call to the endpoint's OnProxyPolicyUpdate() function.
   125  	UpdateNetworkPolicy(ep endpoint.EndpointUpdater, vis *policy.VisibilityPolicy, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error)
   126  	// RemoveNetworkPolicy removes network policies relevant to the specified
   127  	// endpoint from the set published to L7 proxies, and stops listening for
   128  	// acks for policies on this endpoint.
   129  	RemoveNetworkPolicy(ep endpoint.EndpointInfoSource)
   130  	// RemoveAllNetworkPolicies removes all network policies from the set published
   131  	// to L7 proxies.
   132  	RemoveAllNetworkPolicies()
   133  }
   134  
   135  type xdsServer struct {
   136  	// socketPath is the path to the gRPC UNIX domain socket.
   137  	socketPath string
   138  
   139  	// accessLogPath is the path to the L7 access logs
   140  	accessLogPath string
   141  
   142  	config xdsServerConfig
   143  
   144  	// mutex protects accesses to the configuration resources below.
   145  	mutex lock.RWMutex
   146  
   147  	// listenerMutator publishes listener updates to Envoy proxies.
   148  	// Manages it's own locking
   149  	listenerMutator xds.AckingResourceMutator
   150  
   151  	// routeMutator publishes route updates to Envoy proxies.
   152  	// Manages it's own locking
   153  	routeMutator xds.AckingResourceMutator
   154  
   155  	// clusterMutator publishes cluster updates to Envoy proxies.
   156  	// Manages it's own locking
   157  	clusterMutator xds.AckingResourceMutator
   158  
   159  	// endpointMutator publishes endpoint updates to Envoy proxies.
   160  	// Manages it's own locking
   161  	endpointMutator xds.AckingResourceMutator
   162  
   163  	// secretMutator publishes secret updates to Envoy proxies.
   164  	// Manages it's own locking
   165  	secretMutator xds.AckingResourceMutator
   166  
   167  	// listeners is the set of names of listeners that have been added by
   168  	// calling AddListener.
   169  	// mutex must be held when accessing this.
   170  	// Value holds the number of redirects using the listener named by the key.
   171  	listeners map[string]*Listener
   172  
   173  	// proxyListeners is the count of redirection proxy listeners in 'listeners'.
   174  	// When this is zero, cilium should not wait for NACKs/ACKs from envoy.
   175  	// This value is different from len(listeners) due to non-proxy listeners
   176  	// (e.g., prometheus listener)
   177  	proxyListeners int
   178  
   179  	// networkPolicyCache publishes network policy configuration updates to
   180  	// Envoy proxies.
   181  	networkPolicyCache *xds.Cache
   182  
   183  	// NetworkPolicyMutator wraps networkPolicyCache to publish policy
   184  	// updates to Envoy proxies.
   185  	// Exported for testing only!
   186  	NetworkPolicyMutator xds.AckingResourceMutator
   187  
   188  	// stopFunc contains the function which stops the xDS gRPC server.
   189  	stopFunc context.CancelFunc
   190  
   191  	// IPCache is used for tracking IP->Identity mappings and propagating
   192  	// them to the proxy via NPHDS in the cases described
   193  	ipCache IPCacheEventSource
   194  
   195  	restorerPromise promise.Promise[endpointstate.Restorer]
   196  
   197  	localEndpointStore *LocalEndpointStore
   198  }
   199  
   200  func toAny(pb proto.Message) *anypb.Any {
   201  	a, err := anypb.New(pb)
   202  	if err != nil {
   203  		panic(err.Error())
   204  	}
   205  	return a
   206  }
   207  
   208  type xdsServerConfig struct {
   209  	envoySocketDir                string
   210  	proxyGID                      int
   211  	httpRequestTimeout            int
   212  	httpIdleTimeout               int
   213  	httpMaxGRPCTimeout            int
   214  	httpRetryCount                int
   215  	httpRetryTimeout              int
   216  	httpNormalizePath             bool
   217  	useFullTLSContext             bool
   218  	proxyXffNumTrustedHopsIngress uint32
   219  	proxyXffNumTrustedHopsEgress  uint32
   220  }
   221  
   222  // newXDSServer creates a new xDS GRPC server.
   223  func newXDSServer(restorerPromise promise.Promise[endpointstate.Restorer], ipCache IPCacheEventSource, localEndpointStore *LocalEndpointStore, config xdsServerConfig) (*xdsServer, error) {
   224  	return &xdsServer{
   225  		restorerPromise:    restorerPromise,
   226  		listeners:          make(map[string]*Listener),
   227  		ipCache:            ipCache,
   228  		localEndpointStore: localEndpointStore,
   229  
   230  		socketPath:    getXDSSocketPath(config.envoySocketDir),
   231  		accessLogPath: getAccessLogSocketPath(config.envoySocketDir),
   232  		config:        config,
   233  	}, nil
   234  }
   235  
   236  // start configures and starts the xDS GRPC server.
   237  func (s *xdsServer) start() error {
   238  	socketListener, err := s.newSocketListener()
   239  	if err != nil {
   240  		return fmt.Errorf("failed to create socket listener: %w", err)
   241  	}
   242  
   243  	resourceConfig := s.initializeXdsConfigs()
   244  
   245  	s.stopFunc = s.startXDSGRPCServer(socketListener, resourceConfig)
   246  
   247  	return nil
   248  }
   249  
   250  func (s *xdsServer) initializeXdsConfigs() map[string]*xds.ResourceTypeConfiguration {
   251  	ldsCache := xds.NewCache()
   252  	ldsMutator := xds.NewAckingResourceMutatorWrapper(ldsCache)
   253  	ldsConfig := &xds.ResourceTypeConfiguration{
   254  		Source:      ldsCache,
   255  		AckObserver: ldsMutator,
   256  	}
   257  
   258  	rdsCache := xds.NewCache()
   259  	rdsMutator := xds.NewAckingResourceMutatorWrapper(rdsCache)
   260  	rdsConfig := &xds.ResourceTypeConfiguration{
   261  		Source:      rdsCache,
   262  		AckObserver: rdsMutator,
   263  	}
   264  
   265  	cdsCache := xds.NewCache()
   266  	cdsMutator := xds.NewAckingResourceMutatorWrapper(cdsCache)
   267  	cdsConfig := &xds.ResourceTypeConfiguration{
   268  		Source:      cdsCache,
   269  		AckObserver: cdsMutator,
   270  	}
   271  
   272  	edsCache := xds.NewCache()
   273  	edsMutator := xds.NewAckingResourceMutatorWrapper(edsCache)
   274  	edsConfig := &xds.ResourceTypeConfiguration{
   275  		Source:      edsCache,
   276  		AckObserver: edsMutator,
   277  	}
   278  
   279  	sdsCache := xds.NewCache()
   280  	sdsMutator := xds.NewAckingResourceMutatorWrapper(sdsCache)
   281  	sdsConfig := &xds.ResourceTypeConfiguration{
   282  		Source:      sdsCache,
   283  		AckObserver: sdsMutator,
   284  	}
   285  
   286  	npdsCache := xds.NewCache()
   287  	npdsMutator := xds.NewAckingResourceMutatorWrapper(npdsCache)
   288  	npdsConfig := &xds.ResourceTypeConfiguration{
   289  		Source:      npdsCache,
   290  		AckObserver: npdsMutator,
   291  	}
   292  
   293  	nphdsCache := newNPHDSCache(s.ipCache)
   294  	nphdsConfig := &xds.ResourceTypeConfiguration{
   295  		Source:      nphdsCache,
   296  		AckObserver: &nphdsCache,
   297  	}
   298  
   299  	s.listenerMutator = ldsMutator
   300  	s.routeMutator = rdsMutator
   301  	s.clusterMutator = cdsMutator
   302  	s.endpointMutator = edsMutator
   303  	s.secretMutator = sdsMutator
   304  	s.networkPolicyCache = npdsCache
   305  	s.NetworkPolicyMutator = npdsMutator
   306  
   307  	resourceConfig := map[string]*xds.ResourceTypeConfiguration{
   308  		ListenerTypeURL:           ldsConfig,
   309  		RouteTypeURL:              rdsConfig,
   310  		ClusterTypeURL:            cdsConfig,
   311  		EndpointTypeURL:           edsConfig,
   312  		SecretTypeURL:             sdsConfig,
   313  		NetworkPolicyTypeURL:      npdsConfig,
   314  		NetworkPolicyHostsTypeURL: nphdsConfig,
   315  	}
   316  	return resourceConfig
   317  }
   318  
   319  func (s *xdsServer) newSocketListener() (*net.UnixListener, error) {
   320  	// Remove/Unlink the old unix domain socket, if any.
   321  	_ = os.Remove(s.socketPath)
   322  
   323  	socketListener, err := net.ListenUnix("unix", &net.UnixAddr{Name: s.socketPath, Net: "unix"})
   324  	if err != nil {
   325  		return nil, fmt.Errorf("failed to open xDS listen socket at %s: %w", s.socketPath, err)
   326  	}
   327  
   328  	// Make the socket accessible by owner and group only.
   329  	if err = os.Chmod(s.socketPath, 0660); err != nil {
   330  		return nil, fmt.Errorf("failed to change mode of xDS listen socket at %s: %w", s.socketPath, err)
   331  	}
   332  	// Change the group to ProxyGID allowing access from any process from that group.
   333  	if err = os.Chown(s.socketPath, -1, s.config.proxyGID); err != nil {
   334  		log.WithError(err).Warningf("Envoy: Failed to change the group of xDS listen socket at %s", s.socketPath)
   335  	}
   336  	return socketListener, nil
   337  }
   338  
   339  func (s *xdsServer) stop() {
   340  	if s.stopFunc != nil {
   341  		s.stopFunc()
   342  	}
   343  	if s.socketPath != "" {
   344  		_ = os.Remove(s.socketPath)
   345  	}
   346  }
   347  
   348  func GetCiliumHttpFilter() *envoy_config_http.HttpFilter {
   349  	return &envoy_config_http.HttpFilter{
   350  		Name: "cilium.l7policy",
   351  		ConfigType: &envoy_config_http.HttpFilter_TypedConfig{
   352  			TypedConfig: toAny(&cilium.L7Policy{
   353  				AccessLogPath:  getAccessLogSocketPath(GetSocketDir(option.Config.RunDir)),
   354  				Denied_403Body: option.Config.HTTP403Message,
   355  			}),
   356  		},
   357  	}
   358  }
   359  
   360  func GetUpstreamCodecFilter() *envoy_config_http.HttpFilter {
   361  	return &envoy_config_http.HttpFilter{
   362  		Name: "envoy.filters.http.upstream_codec",
   363  		ConfigType: &envoy_config_http.HttpFilter_TypedConfig{
   364  			TypedConfig: toAny(&envoy_upstream_codec.UpstreamCodec{}),
   365  		},
   366  	}
   367  }
   368  
   369  func (s *xdsServer) getHttpFilterChainProto(clusterName string, tls bool, isIngress bool) *envoy_config_listener.FilterChain {
   370  
   371  	requestTimeout := int64(s.config.httpRequestTimeout) // seconds
   372  	idleTimeout := int64(s.config.httpIdleTimeout)       // seconds
   373  	maxGRPCTimeout := int64(s.config.httpMaxGRPCTimeout) // seconds
   374  	numRetries := uint32(s.config.httpRetryCount)
   375  	retryTimeout := int64(s.config.httpRetryTimeout) // seconds
   376  	xffNumTrustedHops := s.config.proxyXffNumTrustedHopsEgress
   377  	if isIngress {
   378  		xffNumTrustedHops = s.config.proxyXffNumTrustedHopsIngress
   379  	}
   380  
   381  	hcmConfig := &envoy_config_http.HttpConnectionManager{
   382  		StatPrefix:        "proxy",
   383  		UseRemoteAddress:  &wrapperspb.BoolValue{Value: true},
   384  		SkipXffAppend:     true,
   385  		XffNumTrustedHops: xffNumTrustedHops,
   386  		HttpFilters: []*envoy_config_http.HttpFilter{
   387  			GetCiliumHttpFilter(),
   388  			{
   389  				Name: "envoy.filters.http.router",
   390  				ConfigType: &envoy_config_http.HttpFilter_TypedConfig{
   391  					TypedConfig: toAny(&envoy_extensions_filters_http_router_v3.Router{}),
   392  				},
   393  			},
   394  		},
   395  		StreamIdleTimeout: &durationpb.Duration{}, // 0 == disabled
   396  		RouteSpecifier: &envoy_config_http.HttpConnectionManager_RouteConfig{
   397  			RouteConfig: &envoy_config_route.RouteConfiguration{
   398  				VirtualHosts: []*envoy_config_route.VirtualHost{{
   399  					Name:    "default_route",
   400  					Domains: []string{"*"},
   401  					Routes: []*envoy_config_route.Route{{
   402  						Match: &envoy_config_route.RouteMatch{
   403  							PathSpecifier: &envoy_config_route.RouteMatch_Prefix{Prefix: "/"},
   404  							Grpc:          &envoy_config_route.RouteMatch_GrpcRouteMatchOptions{},
   405  						},
   406  						Action: &envoy_config_route.Route_Route{
   407  							Route: &envoy_config_route.RouteAction{
   408  								ClusterSpecifier: &envoy_config_route.RouteAction_Cluster{
   409  									Cluster: clusterName,
   410  								},
   411  								Timeout: &durationpb.Duration{Seconds: requestTimeout},
   412  								MaxStreamDuration: &envoy_config_route.RouteAction_MaxStreamDuration{
   413  									GrpcTimeoutHeaderMax: &durationpb.Duration{Seconds: maxGRPCTimeout},
   414  								},
   415  								RetryPolicy: &envoy_config_route.RetryPolicy{
   416  									RetryOn:       "5xx",
   417  									NumRetries:    &wrapperspb.UInt32Value{Value: numRetries},
   418  									PerTryTimeout: &durationpb.Duration{Seconds: retryTimeout},
   419  								},
   420  							},
   421  						},
   422  					}, {
   423  						Match: &envoy_config_route.RouteMatch{
   424  							PathSpecifier: &envoy_config_route.RouteMatch_Prefix{Prefix: "/"},
   425  						},
   426  						Action: &envoy_config_route.Route_Route{
   427  							Route: &envoy_config_route.RouteAction{
   428  								ClusterSpecifier: &envoy_config_route.RouteAction_Cluster{
   429  									Cluster: clusterName,
   430  								},
   431  								Timeout: &durationpb.Duration{Seconds: requestTimeout},
   432  								// IdleTimeout: &durationpb.Duration{Seconds: idleTimeout},
   433  								RetryPolicy: &envoy_config_route.RetryPolicy{
   434  									RetryOn:       "5xx",
   435  									NumRetries:    &wrapperspb.UInt32Value{Value: numRetries},
   436  									PerTryTimeout: &durationpb.Duration{Seconds: retryTimeout},
   437  								},
   438  							},
   439  						},
   440  					}},
   441  				}},
   442  			},
   443  		},
   444  	}
   445  
   446  	if s.config.httpNormalizePath {
   447  		hcmConfig.NormalizePath = &wrapperspb.BoolValue{Value: true}
   448  		hcmConfig.MergeSlashes = true
   449  		hcmConfig.PathWithEscapedSlashesAction = envoy_config_http.HttpConnectionManager_UNESCAPE_AND_REDIRECT
   450  	}
   451  
   452  	// Idle timeout can only be specified if non-zero
   453  	if idleTimeout > 0 {
   454  		hcmConfig.GetRouteConfig().VirtualHosts[0].Routes[1].GetRoute().IdleTimeout = &durationpb.Duration{Seconds: idleTimeout}
   455  	}
   456  
   457  	chain := &envoy_config_listener.FilterChain{
   458  		Filters: []*envoy_config_listener.Filter{{
   459  			Name: "cilium.network",
   460  			ConfigType: &envoy_config_listener.Filter_TypedConfig{
   461  				TypedConfig: toAny(&cilium.NetworkFilter{}),
   462  			},
   463  		}, {
   464  			Name: "envoy.filters.network.http_connection_manager",
   465  			ConfigType: &envoy_config_listener.Filter_TypedConfig{
   466  				TypedConfig: toAny(hcmConfig),
   467  			},
   468  		}},
   469  	}
   470  
   471  	if tls {
   472  		chain.FilterChainMatch = &envoy_config_listener.FilterChainMatch{
   473  			TransportProtocol: "tls",
   474  		}
   475  		chain.TransportSocket = &envoy_config_core.TransportSocket{
   476  			Name: "cilium.tls_wrapper",
   477  			ConfigType: &envoy_config_core.TransportSocket_TypedConfig{
   478  				TypedConfig: toAny(&cilium.DownstreamTlsWrapperContext{}),
   479  			},
   480  		}
   481  	}
   482  
   483  	return chain
   484  }
   485  
   486  // getTcpFilterChainProto creates a TCP filter chain with the Cilium network filter.
   487  // By default, the returned chain can be used with the Cilium Go extensions L7 parsers
   488  // in 'proxylib' directory in the Cilium repo.
   489  // When optional 'filterName' is given, it is configured as the first filter in the chain
   490  // and 'proxylib' is not configured. In this case the returned filter chain is only used
   491  // if the applicable network policy specifies 'filterName' as the L7 parser.
   492  func (s *xdsServer) getTcpFilterChainProto(clusterName string, filterName string, config *anypb.Any, tls bool) *envoy_config_listener.FilterChain {
   493  	var filters []*envoy_config_listener.Filter
   494  
   495  	// 1. Add the filter 'filterName' to the beginning of the TCP chain with optional 'config', if needed.
   496  	if filterName != "" {
   497  		filter := &envoy_config_listener.Filter{Name: filterName}
   498  		if config != nil {
   499  			filter.ConfigType = &envoy_config_listener.Filter_TypedConfig{
   500  				TypedConfig: config,
   501  			}
   502  		}
   503  		filters = append(filters, filter)
   504  	}
   505  
   506  	// 2. Add Cilium Network filter.
   507  	var ciliumConfig *cilium.NetworkFilter
   508  	if filterName == "" {
   509  		// Use proxylib by default
   510  		ciliumConfig = &cilium.NetworkFilter{
   511  			Proxylib: "libcilium.so",
   512  			ProxylibParams: map[string]string{
   513  				"access-log-path": s.accessLogPath,
   514  				"xds-path":        s.socketPath,
   515  			},
   516  		}
   517  	} else {
   518  		// Envoy metadata logging requires accesslog path
   519  		ciliumConfig = &cilium.NetworkFilter{
   520  			AccessLogPath: s.accessLogPath,
   521  		}
   522  	}
   523  	filters = append(filters, &envoy_config_listener.Filter{
   524  		Name: "cilium.network",
   525  		ConfigType: &envoy_config_listener.Filter_TypedConfig{
   526  			TypedConfig: toAny(ciliumConfig),
   527  		},
   528  	})
   529  
   530  	// 3. Add the TCP proxy filter.
   531  	filters = append(filters, &envoy_config_listener.Filter{
   532  		Name: "envoy.filters.network.tcp_proxy",
   533  		ConfigType: &envoy_config_listener.Filter_TypedConfig{
   534  			TypedConfig: toAny(&envoy_config_tcp.TcpProxy{
   535  				StatPrefix: "tcp_proxy",
   536  				ClusterSpecifier: &envoy_config_tcp.TcpProxy_Cluster{
   537  					Cluster: clusterName,
   538  				},
   539  			}),
   540  		},
   541  	})
   542  
   543  	chain := &envoy_config_listener.FilterChain{
   544  		Filters: filters,
   545  	}
   546  
   547  	if tls {
   548  		chain.FilterChainMatch = &envoy_config_listener.FilterChainMatch{
   549  			TransportProtocol: "tls",
   550  		}
   551  		chain.TransportSocket = &envoy_config_core.TransportSocket{
   552  			Name: "cilium.tls_wrapper",
   553  			ConfigType: &envoy_config_core.TransportSocket_TypedConfig{
   554  				TypedConfig: toAny(&cilium.DownstreamTlsWrapperContext{}),
   555  			},
   556  		}
   557  	} else {
   558  		chain.FilterChainMatch = &envoy_config_listener.FilterChainMatch{
   559  			// must have transport match for non-TLS,
   560  			// otherwise TLS inspector will be automatically inserted
   561  			TransportProtocol: "raw_buffer",
   562  		}
   563  	}
   564  
   565  	if filterName != "" {
   566  		// Add filter chain match for 'filterName' so that connections for which policy says to use this L7
   567  		// are handled by this filter chain.
   568  		chain.FilterChainMatch.ApplicationProtocols = []string{filterName}
   569  	}
   570  
   571  	return chain
   572  }
   573  
   574  func getPublicListenerAddress(port uint16, ipv4, ipv6 bool) *envoy_config_core.Address {
   575  	listenerAddr := "0.0.0.0"
   576  	if ipv6 {
   577  		listenerAddr = "::"
   578  	}
   579  	return &envoy_config_core.Address{
   580  		Address: &envoy_config_core.Address_SocketAddress{
   581  			SocketAddress: &envoy_config_core.SocketAddress{
   582  				Protocol:      envoy_config_core.SocketAddress_TCP,
   583  				Address:       listenerAddr,
   584  				Ipv4Compat:    ipv4 && ipv6,
   585  				PortSpecifier: &envoy_config_core.SocketAddress_PortValue{PortValue: uint32(port)},
   586  			},
   587  		},
   588  	}
   589  }
   590  
   591  func GetLocalListenerAddresses(port uint16, ipv4, ipv6 bool) (*envoy_config_core.Address, []*envoy_config_listener.AdditionalAddress) {
   592  	addresses := []*envoy_config_core.Address_SocketAddress{}
   593  
   594  	if ipv4 {
   595  		addresses = append(addresses, &envoy_config_core.Address_SocketAddress{
   596  			SocketAddress: &envoy_config_core.SocketAddress{
   597  				Protocol:      envoy_config_core.SocketAddress_TCP,
   598  				Address:       "127.0.0.1",
   599  				PortSpecifier: &envoy_config_core.SocketAddress_PortValue{PortValue: uint32(port)},
   600  			},
   601  		})
   602  	}
   603  
   604  	if ipv6 {
   605  		addresses = append(addresses, &envoy_config_core.Address_SocketAddress{
   606  			SocketAddress: &envoy_config_core.SocketAddress{
   607  				Protocol:      envoy_config_core.SocketAddress_TCP,
   608  				Address:       "::1",
   609  				PortSpecifier: &envoy_config_core.SocketAddress_PortValue{PortValue: uint32(port)},
   610  			},
   611  		})
   612  	}
   613  
   614  	var additionalAddress []*envoy_config_listener.AdditionalAddress
   615  
   616  	if len(addresses) > 1 {
   617  		additionalAddress = append(additionalAddress, &envoy_config_listener.AdditionalAddress{
   618  			Address: &envoy_config_core.Address{
   619  				Address: addresses[1],
   620  			},
   621  		})
   622  	}
   623  
   624  	return &envoy_config_core.Address{
   625  		Address: addresses[0],
   626  	}, additionalAddress
   627  }
   628  
   629  func (s *xdsServer) AddAdminListener(port uint16, wg *completion.WaitGroup) {
   630  	if port == 0 {
   631  		return // 0 == disabled
   632  	}
   633  	log.WithField(logfields.Port, port).Debug("Envoy: AddAdminListener")
   634  
   635  	s.addListener(adminListenerName, func() *envoy_config_listener.Listener {
   636  		hcmConfig := &envoy_config_http.HttpConnectionManager{
   637  			StatPrefix:       adminListenerName,
   638  			UseRemoteAddress: &wrapperspb.BoolValue{Value: true},
   639  			SkipXffAppend:    true,
   640  			HttpFilters: []*envoy_config_http.HttpFilter{{
   641  				Name: "envoy.filters.http.router",
   642  				ConfigType: &envoy_config_http.HttpFilter_TypedConfig{
   643  					TypedConfig: toAny(&envoy_extensions_filters_http_router_v3.Router{}),
   644  				},
   645  			}},
   646  			StreamIdleTimeout: &durationpb.Duration{}, // 0 == disabled
   647  			RouteSpecifier: &envoy_config_http.HttpConnectionManager_RouteConfig{
   648  				RouteConfig: &envoy_config_route.RouteConfiguration{
   649  					VirtualHosts: []*envoy_config_route.VirtualHost{{
   650  						Name:    "admin_listener_route",
   651  						Domains: []string{"*"},
   652  						Routes: []*envoy_config_route.Route{{
   653  							Match: &envoy_config_route.RouteMatch{
   654  								PathSpecifier: &envoy_config_route.RouteMatch_Prefix{Prefix: "/"},
   655  							},
   656  							Action: &envoy_config_route.Route_Route{
   657  								Route: &envoy_config_route.RouteAction{
   658  									ClusterSpecifier: &envoy_config_route.RouteAction_Cluster{
   659  										Cluster: adminClusterName,
   660  									},
   661  								},
   662  							},
   663  						}},
   664  					}},
   665  				},
   666  			},
   667  		}
   668  
   669  		addr, additionalAddr := GetLocalListenerAddresses(port, option.Config.IPv4Enabled(), option.Config.IPv6Enabled())
   670  		listenerConf := &envoy_config_listener.Listener{
   671  			Name:                adminListenerName,
   672  			Address:             addr,
   673  			AdditionalAddresses: additionalAddr,
   674  			FilterChains: []*envoy_config_listener.FilterChain{{
   675  				Filters: []*envoy_config_listener.Filter{{
   676  					Name: "envoy.filters.network.http_connection_manager",
   677  					ConfigType: &envoy_config_listener.Filter_TypedConfig{
   678  						TypedConfig: toAny(hcmConfig),
   679  					},
   680  				}},
   681  			}},
   682  		}
   683  
   684  		return listenerConf
   685  	}, wg, func(err error) {
   686  		if err != nil {
   687  			log.WithField(logfields.Port, port).WithError(err).Debug("Envoy: Adding admin listener failed")
   688  			// Remove the added listener in case of a failure
   689  			s.removeListener(adminListenerName, nil, false)
   690  		} else {
   691  			log.WithField(logfields.Port, port).Info("Envoy: Listening for Admin API")
   692  		}
   693  	}, false)
   694  }
   695  
   696  func (s *xdsServer) AddMetricsListener(port uint16, wg *completion.WaitGroup) {
   697  	if port == 0 {
   698  		return // 0 == disabled
   699  	}
   700  	log.WithField(logfields.Port, port).Debug("Envoy: AddMetricsListener")
   701  
   702  	s.addListener(metricsListenerName, func() *envoy_config_listener.Listener {
   703  		hcmConfig := &envoy_config_http.HttpConnectionManager{
   704  			StatPrefix:       metricsListenerName,
   705  			UseRemoteAddress: &wrapperspb.BoolValue{Value: true},
   706  			SkipXffAppend:    true,
   707  			HttpFilters: []*envoy_config_http.HttpFilter{{
   708  				Name: "envoy.filters.http.router",
   709  				ConfigType: &envoy_config_http.HttpFilter_TypedConfig{
   710  					TypedConfig: toAny(&envoy_extensions_filters_http_router_v3.Router{}),
   711  				},
   712  			}},
   713  			StreamIdleTimeout: &durationpb.Duration{}, // 0 == disabled
   714  			RouteSpecifier: &envoy_config_http.HttpConnectionManager_RouteConfig{
   715  				RouteConfig: &envoy_config_route.RouteConfiguration{
   716  					VirtualHosts: []*envoy_config_route.VirtualHost{{
   717  						Name:    "prometheus_metrics_route",
   718  						Domains: []string{"*"},
   719  						Routes: []*envoy_config_route.Route{{
   720  							Match: &envoy_config_route.RouteMatch{
   721  								PathSpecifier: &envoy_config_route.RouteMatch_Prefix{Prefix: "/metrics"},
   722  							},
   723  							Action: &envoy_config_route.Route_Route{
   724  								Route: &envoy_config_route.RouteAction{
   725  									ClusterSpecifier: &envoy_config_route.RouteAction_Cluster{
   726  										Cluster: adminClusterName,
   727  									},
   728  									PrefixRewrite: "/stats/prometheus",
   729  								},
   730  							},
   731  						}},
   732  					}},
   733  				},
   734  			},
   735  		}
   736  
   737  		listenerConf := &envoy_config_listener.Listener{
   738  			Name:    metricsListenerName,
   739  			Address: getPublicListenerAddress(port, option.Config.IPv4Enabled(), option.Config.IPv6Enabled()),
   740  			FilterChains: []*envoy_config_listener.FilterChain{{
   741  				Filters: []*envoy_config_listener.Filter{{
   742  					Name: "envoy.filters.network.http_connection_manager",
   743  					ConfigType: &envoy_config_listener.Filter_TypedConfig{
   744  						TypedConfig: toAny(hcmConfig),
   745  					},
   746  				}},
   747  			}},
   748  		}
   749  
   750  		return listenerConf
   751  	}, wg, func(err error) {
   752  		if err != nil {
   753  			log.WithField(logfields.Port, port).WithError(err).Debug("Envoy: Adding metrics listener failed")
   754  			// Remove the added listener in case of a failure
   755  			s.removeListener(metricsListenerName, nil, false)
   756  		} else {
   757  			log.WithField(logfields.Port, port).Info("Envoy: Listening for prometheus metrics")
   758  		}
   759  	}, false)
   760  }
   761  
   762  // addListener either reuses an existing listener with 'name', or creates a new one.
   763  // 'listenerConf()' is only called if a new listener is being created.
   764  func (s *xdsServer) addListener(name string, listenerConf func() *envoy_config_listener.Listener, wg *completion.WaitGroup, cb func(err error), isProxyListener bool) {
   765  	s.mutex.Lock()
   766  	defer s.mutex.Unlock()
   767  
   768  	listener := s.listeners[name]
   769  	if listener == nil {
   770  		listener = &Listener{}
   771  		s.listeners[name] = listener
   772  		if isProxyListener {
   773  			s.proxyListeners++
   774  		}
   775  	}
   776  	listener.count++
   777  	listener.mutex.Lock() // needed for other than 'count'
   778  	if listener.count > 1 && !listener.nacked {
   779  		log.Debugf("Envoy: Reusing listener: %s", name)
   780  		call := true
   781  		if !listener.acked {
   782  			// Listener not acked yet, add a completion to the waiter's list
   783  			log.Debugf("Envoy: Waiting for a non-acknowledged reused listener: %s", name)
   784  			listener.waiters = append(listener.waiters, wg.AddCompletionWithCallback(cb))
   785  			call = false
   786  		}
   787  		listener.mutex.Unlock()
   788  
   789  		// call the callback with nil error if the listener was acked already
   790  		if call && cb != nil {
   791  			cb(nil)
   792  		}
   793  		return
   794  	}
   795  	// Try again after a NACK, potentially with a different port number, etc.
   796  	if listener.nacked {
   797  		listener.acked = false
   798  		listener.nacked = false
   799  	}
   800  	listener.mutex.Unlock() // Listener locked again in callbacks below
   801  
   802  	listenerConfig := listenerConf()
   803  	if option.Config.EnableBPFTProxy {
   804  		// Envoy since 1.20.0 uses SO_REUSEPORT on listeners by default.
   805  		// BPF TPROXY is currently not compatible with SO_REUSEPORT, so disable it.
   806  		// Note that this may degrade Envoy performance.
   807  		listenerConfig.EnableReusePort = &wrapperspb.BoolValue{Value: false}
   808  	}
   809  	if err := listenerConfig.Validate(); err != nil {
   810  		log.Errorf("Envoy: Could not validate Listener (%s): %s", err, listenerConfig.String())
   811  		if cb != nil {
   812  			cb(err)
   813  		}
   814  		return
   815  	}
   816  
   817  	s.listenerMutator.Upsert(ListenerTypeURL, name, listenerConfig, []string{"127.0.0.1"}, wg,
   818  		func(err error) {
   819  			// listener might have already been removed, so we can't look again
   820  			// but we still need to complete all the completions in case
   821  			// someone is still waiting!
   822  			listener.mutex.Lock()
   823  			if err == nil {
   824  				// Allow future users to not need to wait
   825  				listener.acked = true
   826  			} else {
   827  				// Prevent further reuse of a failed listener
   828  				listener.nacked = true
   829  			}
   830  			// Pass the completion result to all the additional waiters.
   831  			for _, waiter := range listener.waiters {
   832  				_ = waiter.Complete(err)
   833  			}
   834  			listener.waiters = nil
   835  			listener.mutex.Unlock()
   836  
   837  			if cb != nil {
   838  				cb(err)
   839  			}
   840  		})
   841  }
   842  
   843  // upsertListener either updates an existing LDS listener with 'name', or creates a new one.
   844  func (s *xdsServer) upsertListener(name string, listenerConf *envoy_config_listener.Listener, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   845  	s.mutex.Lock()
   846  	defer s.mutex.Unlock()
   847  	// 'callback' is not called if there is no change and this configuration has already been acked.
   848  	return s.listenerMutator.Upsert(ListenerTypeURL, name, listenerConf, []string{"127.0.0.1"}, wg, callback)
   849  }
   850  
   851  // deleteListener deletes an LDS Envoy Listener.
   852  func (s *xdsServer) deleteListener(name string, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   853  	s.mutex.Lock()
   854  	defer s.mutex.Unlock()
   855  	// 'callback' is not called if there is no change and this configuration has already been acked.
   856  	return s.listenerMutator.Delete(ListenerTypeURL, name, []string{"127.0.0.1"}, wg, callback)
   857  }
   858  
   859  // upsertRoute either updates an existing RDS route with 'name', or creates a new one.
   860  func (s *xdsServer) upsertRoute(name string, conf *envoy_config_route.RouteConfiguration, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   861  	s.mutex.Lock()
   862  	defer s.mutex.Unlock()
   863  	// 'callback' is not called if there is no change and this configuration has already been acked.
   864  	return s.routeMutator.Upsert(RouteTypeURL, name, conf, []string{"127.0.0.1"}, wg, callback)
   865  }
   866  
   867  // deleteRoute deletes an RDS Route.
   868  func (s *xdsServer) deleteRoute(name string, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   869  	s.mutex.Lock()
   870  	defer s.mutex.Unlock()
   871  	// 'callback' is not called if there is no change and this configuration has already been acked.
   872  	return s.routeMutator.Delete(RouteTypeURL, name, []string{"127.0.0.1"}, wg, callback)
   873  }
   874  
   875  // upsertCluster either updates an existing CDS cluster with 'name', or creates a new one.
   876  func (s *xdsServer) upsertCluster(name string, conf *envoy_config_cluster.Cluster, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   877  	s.mutex.Lock()
   878  	defer s.mutex.Unlock()
   879  	// 'callback' is not called if there is no change and this configuration has already been acked.
   880  	return s.clusterMutator.Upsert(ClusterTypeURL, name, conf, []string{"127.0.0.1"}, wg, callback)
   881  }
   882  
   883  // deleteCluster deletes an CDS cluster.
   884  func (s *xdsServer) deleteCluster(name string, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   885  	s.mutex.Lock()
   886  	defer s.mutex.Unlock()
   887  	// 'callback' is not called if there is no change and this configuration has already been acked.
   888  	return s.clusterMutator.Delete(ClusterTypeURL, name, []string{"127.0.0.1"}, wg, callback)
   889  }
   890  
   891  // upsertEndpoint either updates an existing EDS endpoint with 'name', or creates a new one.
   892  func (s *xdsServer) upsertEndpoint(name string, conf *envoy_config_endpoint.ClusterLoadAssignment, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   893  	s.mutex.Lock()
   894  	defer s.mutex.Unlock()
   895  	// 'callback' is not called if there is no change and this configuration has already been acked.
   896  	return s.endpointMutator.Upsert(EndpointTypeURL, name, conf, []string{"127.0.0.1"}, wg, callback)
   897  }
   898  
   899  // deleteEndpoint deletes an EDS endpoint.
   900  func (s *xdsServer) deleteEndpoint(name string, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   901  	s.mutex.Lock()
   902  	defer s.mutex.Unlock()
   903  	// 'callback' is not called if there is no change and this configuration has already been acked.
   904  	return s.endpointMutator.Delete(EndpointTypeURL, name, []string{"127.0.0.1"}, wg, callback)
   905  }
   906  
   907  // upsertSecret either updates an existing SDS secret with 'name', or creates a new one.
   908  func (s *xdsServer) upsertSecret(name string, conf *envoy_config_tls.Secret, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   909  	s.mutex.Lock()
   910  	defer s.mutex.Unlock()
   911  	// 'callback' is not called if there is no change and this configuration has already been acked.
   912  	return s.secretMutator.Upsert(SecretTypeURL, name, conf, []string{"127.0.0.1"}, wg, callback)
   913  }
   914  
   915  // deleteSecret deletes an SDS secret.
   916  func (s *xdsServer) deleteSecret(name string, wg *completion.WaitGroup, callback func(error)) xds.AckingResourceMutatorRevertFunc {
   917  	s.mutex.Lock()
   918  	defer s.mutex.Unlock()
   919  	// 'callback' is not called if there is no change and this configuration has already been acked.
   920  	return s.secretMutator.Delete(SecretTypeURL, name, []string{"127.0.0.1"}, wg, callback)
   921  }
   922  
   923  func getListenerFilter(isIngress bool, useOriginalSourceAddr bool, proxyPort uint16) *envoy_config_listener.ListenerFilter {
   924  	conf := &cilium.BpfMetadata{
   925  		IsIngress:                isIngress,
   926  		UseOriginalSourceAddress: useOriginalSourceAddr,
   927  		BpfRoot:                  bpf.BPFFSRoot(),
   928  		IsL7Lb:                   false,
   929  		ProxyId:                  uint32(proxyPort),
   930  	}
   931  
   932  	return &envoy_config_listener.ListenerFilter{
   933  		Name: "cilium.bpf_metadata",
   934  		ConfigType: &envoy_config_listener.ListenerFilter_TypedConfig{
   935  			TypedConfig: toAny(conf),
   936  		},
   937  	}
   938  }
   939  
   940  func (s *xdsServer) getListenerConf(name string, kind policy.L7ParserType, port uint16, isIngress bool, mayUseOriginalSourceAddr bool) *envoy_config_listener.Listener {
   941  	clusterName := egressClusterName
   942  	tlsClusterName := egressTLSClusterName
   943  
   944  	if isIngress {
   945  		clusterName = ingressClusterName
   946  		tlsClusterName = ingressTLSClusterName
   947  	}
   948  
   949  	addr, additionalAddr := GetLocalListenerAddresses(port, option.Config.IPv4Enabled(), option.Config.IPv6Enabled())
   950  	listenerConf := &envoy_config_listener.Listener{
   951  		Name:                name,
   952  		Address:             addr,
   953  		AdditionalAddresses: additionalAddr,
   954  		// FilterChains: []*envoy_config_listener.FilterChain
   955  		ListenerFilters: []*envoy_config_listener.ListenerFilter{
   956  			// Always insert tls_inspector as the first filter
   957  			{
   958  				Name: "envoy.filters.listener.tls_inspector",
   959  				ConfigType: &envoy_config_listener.ListenerFilter_TypedConfig{
   960  					TypedConfig: toAny(&envoy_extensions_listener_tls_inspector_v3.TlsInspector{}),
   961  				},
   962  			},
   963  			getListenerFilter(isIngress, mayUseOriginalSourceAddr, port),
   964  		},
   965  	}
   966  
   967  	// Add filter chains
   968  	if kind == policy.ParserTypeHTTP {
   969  		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getHttpFilterChainProto(clusterName, false, isIngress))
   970  
   971  		// Add a TLS variant
   972  		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getHttpFilterChainProto(tlsClusterName, true, isIngress))
   973  	} else {
   974  		// Default TCP chain, takes care of all parsers in proxylib
   975  		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName, "", nil, false))
   976  
   977  		// Add a TLS variant
   978  		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(tlsClusterName, "", nil, true))
   979  
   980  		// Experimental TCP chain for MySQL 5.x
   981  		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName,
   982  			"envoy.filters.network.mysql_proxy", toAny(&envoy_mysql_proxy.MySQLProxy{
   983  				StatPrefix: "mysql",
   984  			}), false))
   985  
   986  		// Experimental TCP chain for MongoDB
   987  		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName,
   988  			"envoy.filters.network.mongo_proxy", toAny(&envoy_mongo_proxy.MongoProxy{
   989  				StatPrefix:          "mongo",
   990  				EmitDynamicMetadata: true,
   991  			}), false))
   992  	}
   993  	return listenerConf
   994  }
   995  
   996  func (s *xdsServer) AddListener(name string, kind policy.L7ParserType, port uint16, isIngress bool, mayUseOriginalSourceAddr bool, wg *completion.WaitGroup) {
   997  	log.Debugf("Envoy: %s AddListener %s (mayUseOriginalSourceAddr: %v)", kind, name, mayUseOriginalSourceAddr)
   998  
   999  	s.addListener(name, func() *envoy_config_listener.Listener {
  1000  		return s.getListenerConf(name, kind, port, isIngress, mayUseOriginalSourceAddr)
  1001  	}, wg, nil, true)
  1002  }
  1003  
  1004  func (s *xdsServer) RemoveListener(name string, wg *completion.WaitGroup) xds.AckingResourceMutatorRevertFunc {
  1005  	return s.removeListener(name, wg, true)
  1006  }
  1007  
  1008  // removeListener removes an existing Envoy Listener.
  1009  func (s *xdsServer) removeListener(name string, wg *completion.WaitGroup, isProxyListener bool) xds.AckingResourceMutatorRevertFunc {
  1010  	log.Debugf("Envoy: RemoveListener %s", name)
  1011  
  1012  	var listenerRevertFunc xds.AckingResourceMutatorRevertFunc
  1013  
  1014  	s.mutex.Lock()
  1015  	listener, ok := s.listeners[name]
  1016  	if ok && listener != nil {
  1017  		listener.count--
  1018  		if listener.count == 0 {
  1019  			if isProxyListener {
  1020  				s.proxyListeners--
  1021  			}
  1022  			delete(s.listeners, name)
  1023  			listenerRevertFunc = s.listenerMutator.Delete(ListenerTypeURL, name, []string{"127.0.0.1"}, wg, nil)
  1024  		}
  1025  	} else {
  1026  		// Bail out if this listener does not exist
  1027  		log.Fatalf("Envoy: Attempt to remove non-existent listener: %s", name)
  1028  	}
  1029  	s.mutex.Unlock()
  1030  
  1031  	return func(completion *completion.Completion) {
  1032  		s.mutex.Lock()
  1033  		if listenerRevertFunc != nil {
  1034  			listenerRevertFunc(completion)
  1035  			if isProxyListener {
  1036  				s.proxyListeners++
  1037  			}
  1038  		}
  1039  		listener.count++
  1040  		s.listeners[name] = listener
  1041  		s.mutex.Unlock()
  1042  	}
  1043  }
  1044  
  1045  func getL7Rules(l7Rules []api.PortRuleL7, l7Proto string) *cilium.L7NetworkPolicyRules {
  1046  	allowRules := make([]*cilium.L7NetworkPolicyRule, 0, len(l7Rules))
  1047  	denyRules := make([]*cilium.L7NetworkPolicyRule, 0, len(l7Rules))
  1048  	useEnvoyMetadataMatcher := false
  1049  	if strings.HasPrefix(l7Proto, "envoy.") {
  1050  		useEnvoyMetadataMatcher = true
  1051  	}
  1052  	for _, l7 := range l7Rules {
  1053  		if useEnvoyMetadataMatcher {
  1054  			envoyFilterName := l7Proto
  1055  			rule := &cilium.L7NetworkPolicyRule{MetadataRule: make([]*envoy_type_matcher.MetadataMatcher, 0, len(l7))}
  1056  			denyRule := false
  1057  			for k, v := range l7 {
  1058  				switch k {
  1059  				case "action":
  1060  					switch v {
  1061  					case "deny":
  1062  						denyRule = true
  1063  					}
  1064  				default:
  1065  					// map key to path segments and value to value matcher
  1066  					// For now only one path segment is allowed
  1067  					segments := strings.Split(k, "/")
  1068  					var path []*envoy_type_matcher.MetadataMatcher_PathSegment
  1069  					for _, key := range segments {
  1070  						path = append(path, &envoy_type_matcher.MetadataMatcher_PathSegment{
  1071  							Segment: &envoy_type_matcher.MetadataMatcher_PathSegment_Key{Key: key},
  1072  						})
  1073  					}
  1074  					var value *envoy_type_matcher.ValueMatcher
  1075  					if len(v) == 0 {
  1076  						value = &envoy_type_matcher.ValueMatcher{
  1077  							MatchPattern: &envoy_type_matcher.ValueMatcher_PresentMatch{
  1078  								PresentMatch: true,
  1079  							},
  1080  						}
  1081  					} else {
  1082  						value = &envoy_type_matcher.ValueMatcher{
  1083  							MatchPattern: &envoy_type_matcher.ValueMatcher_ListMatch{
  1084  								ListMatch: &envoy_type_matcher.ListMatcher{
  1085  									MatchPattern: &envoy_type_matcher.ListMatcher_OneOf{
  1086  										OneOf: &envoy_type_matcher.ValueMatcher{
  1087  											MatchPattern: &envoy_type_matcher.ValueMatcher_StringMatch{
  1088  												StringMatch: &envoy_type_matcher.StringMatcher{
  1089  													MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
  1090  														Exact: v,
  1091  													},
  1092  													IgnoreCase: false,
  1093  												},
  1094  											},
  1095  										},
  1096  									},
  1097  								},
  1098  							},
  1099  						}
  1100  					}
  1101  					rule.MetadataRule = append(rule.MetadataRule, &envoy_type_matcher.MetadataMatcher{
  1102  						Filter: envoyFilterName,
  1103  						Path:   path,
  1104  						Value:  value,
  1105  					})
  1106  				}
  1107  			}
  1108  			if denyRule {
  1109  				denyRules = append(denyRules, rule)
  1110  			} else {
  1111  				allowRules = append(allowRules, rule)
  1112  			}
  1113  		} else {
  1114  			// proxylib go extension key/value policy
  1115  			rule := &cilium.L7NetworkPolicyRule{Rule: make(map[string]string, len(l7))}
  1116  			for k, v := range l7 {
  1117  				rule.Rule[k] = v
  1118  			}
  1119  			allowRules = append(allowRules, rule)
  1120  		}
  1121  	}
  1122  
  1123  	rules := &cilium.L7NetworkPolicyRules{}
  1124  	if len(allowRules) > 0 {
  1125  		rules.L7AllowRules = allowRules
  1126  	}
  1127  	if len(denyRules) > 0 {
  1128  		rules.L7DenyRules = denyRules
  1129  	}
  1130  	return rules
  1131  }
  1132  
  1133  func getKafkaL7Rules(l7Rules []kafka.PortRule) *cilium.KafkaNetworkPolicyRules {
  1134  	allowRules := make([]*cilium.KafkaNetworkPolicyRule, 0, len(l7Rules))
  1135  	for _, kr := range l7Rules {
  1136  		rule := &cilium.KafkaNetworkPolicyRule{
  1137  			ApiVersion: kr.GetAPIVersion(),
  1138  			ApiKeys:    kr.GetAPIKeys(),
  1139  			ClientId:   kr.ClientID,
  1140  			Topic:      kr.Topic,
  1141  		}
  1142  		allowRules = append(allowRules, rule)
  1143  	}
  1144  
  1145  	rules := &cilium.KafkaNetworkPolicyRules{}
  1146  	if len(allowRules) > 0 {
  1147  		rules.KafkaRules = allowRules
  1148  	}
  1149  	return rules
  1150  }
  1151  
  1152  func getSecretString(secretManager certificatemanager.SecretManager, hdr *api.HeaderMatch, ns string) (string, error) {
  1153  	value := ""
  1154  	var err error
  1155  	if hdr.Secret != nil {
  1156  		if secretManager == nil {
  1157  			err = fmt.Errorf("HeaderMatches: Nil secretManager")
  1158  		} else {
  1159  			value, err = secretManager.GetSecretString(context.TODO(), hdr.Secret, ns)
  1160  		}
  1161  	}
  1162  	// Only use Value if secret was not obtained
  1163  	if value == "" && hdr.Value != "" {
  1164  		value = hdr.Value
  1165  		if err != nil {
  1166  			log.WithError(err).Debug("HeaderMatches: Using a default value due to k8s secret not being available")
  1167  			err = nil
  1168  		}
  1169  	}
  1170  
  1171  	return value, err
  1172  }
  1173  
  1174  func getHTTPRule(secretManager certificatemanager.SecretManager, h *api.PortRuleHTTP, ns string) (*cilium.HttpNetworkPolicyRule, bool) {
  1175  	// Count the number of header matches we need
  1176  	cnt := len(h.Headers) + len(h.HeaderMatches)
  1177  	if h.Path != "" {
  1178  		cnt++
  1179  	}
  1180  	if h.Method != "" {
  1181  		cnt++
  1182  	}
  1183  	if h.Host != "" {
  1184  		cnt++
  1185  	}
  1186  
  1187  	headers := make([]*envoy_config_route.HeaderMatcher, 0, cnt)
  1188  	if h.Path != "" {
  1189  		headers = append(headers, &envoy_config_route.HeaderMatcher{
  1190  			Name: ":path",
  1191  			HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{
  1192  				StringMatch: &envoy_type_matcher.StringMatcher{
  1193  					MatchPattern: &envoy_type_matcher.StringMatcher_SafeRegex{
  1194  						SafeRegex: &envoy_type_matcher.RegexMatcher{
  1195  							Regex: h.Path,
  1196  						},
  1197  					},
  1198  				},
  1199  			},
  1200  		})
  1201  	}
  1202  	if h.Method != "" {
  1203  		headers = append(headers, &envoy_config_route.HeaderMatcher{
  1204  			Name: ":method",
  1205  			HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{
  1206  				StringMatch: &envoy_type_matcher.StringMatcher{
  1207  					MatchPattern: &envoy_type_matcher.StringMatcher_SafeRegex{
  1208  						SafeRegex: &envoy_type_matcher.RegexMatcher{
  1209  							Regex: h.Method,
  1210  						},
  1211  					},
  1212  				},
  1213  			},
  1214  		})
  1215  	}
  1216  	if h.Host != "" {
  1217  		headers = append(headers, &envoy_config_route.HeaderMatcher{
  1218  			Name: ":authority",
  1219  			HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{
  1220  				StringMatch: &envoy_type_matcher.StringMatcher{
  1221  					MatchPattern: &envoy_type_matcher.StringMatcher_SafeRegex{
  1222  						SafeRegex: &envoy_type_matcher.RegexMatcher{
  1223  							Regex: h.Host,
  1224  						},
  1225  					},
  1226  				},
  1227  			},
  1228  		})
  1229  	}
  1230  	for _, hdr := range h.Headers {
  1231  		strs := strings.SplitN(hdr, " ", 2)
  1232  		if len(strs) == 2 {
  1233  			// Remove ':' in "X-Key: true"
  1234  			key := strings.TrimRight(strs[0], ":")
  1235  			// Header presence and matching (literal) value needed.
  1236  			headers = append(headers, &envoy_config_route.HeaderMatcher{
  1237  				Name: key,
  1238  				HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{
  1239  					StringMatch: &envoy_type_matcher.StringMatcher{
  1240  						MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
  1241  							Exact: strs[1],
  1242  						},
  1243  					},
  1244  				},
  1245  			})
  1246  		} else {
  1247  			// Only header presence needed
  1248  			headers = append(headers, &envoy_config_route.HeaderMatcher{
  1249  				Name:                 strs[0],
  1250  				HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_PresentMatch{PresentMatch: true},
  1251  			})
  1252  		}
  1253  	}
  1254  
  1255  	headerMatches := make([]*cilium.HeaderMatch, 0, len(h.HeaderMatches))
  1256  	for _, hdr := range h.HeaderMatches {
  1257  		var mismatch_action cilium.HeaderMatch_MismatchAction
  1258  		switch hdr.Mismatch {
  1259  		case api.MismatchActionLog:
  1260  			mismatch_action = cilium.HeaderMatch_CONTINUE_ON_MISMATCH
  1261  		case api.MismatchActionAdd:
  1262  			mismatch_action = cilium.HeaderMatch_ADD_ON_MISMATCH
  1263  		case api.MismatchActionDelete:
  1264  			mismatch_action = cilium.HeaderMatch_DELETE_ON_MISMATCH
  1265  		case api.MismatchActionReplace:
  1266  			mismatch_action = cilium.HeaderMatch_REPLACE_ON_MISMATCH
  1267  		default:
  1268  			mismatch_action = cilium.HeaderMatch_FAIL_ON_MISMATCH
  1269  		}
  1270  		// Fetch the secret
  1271  		value, err := getSecretString(secretManager, hdr, ns)
  1272  		if err != nil {
  1273  			log.WithError(err).Warning("Failed fetching K8s Secret, header match will fail")
  1274  			// Envoy treats an empty exact match value as matching ANY value; adding
  1275  			// InvertMatch: true here will cause this rule to NEVER match.
  1276  			headers = append(headers, &envoy_config_route.HeaderMatcher{
  1277  				Name: hdr.Name,
  1278  				HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{
  1279  					StringMatch: &envoy_type_matcher.StringMatcher{
  1280  						MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
  1281  							Exact: "",
  1282  						},
  1283  					},
  1284  				},
  1285  				InvertMatch: true,
  1286  			})
  1287  		} else {
  1288  			// Header presence and matching (literal) value needed.
  1289  			if mismatch_action == cilium.HeaderMatch_FAIL_ON_MISMATCH {
  1290  				if value != "" {
  1291  					headers = append(headers, &envoy_config_route.HeaderMatcher{
  1292  						Name: hdr.Name,
  1293  						HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{
  1294  							StringMatch: &envoy_type_matcher.StringMatcher{
  1295  								MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
  1296  									Exact: value,
  1297  								},
  1298  							},
  1299  						},
  1300  					})
  1301  				} else {
  1302  					// Only header presence needed
  1303  					headers = append(headers, &envoy_config_route.HeaderMatcher{
  1304  						Name:                 hdr.Name,
  1305  						HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_PresentMatch{PresentMatch: true},
  1306  					})
  1307  				}
  1308  			} else {
  1309  				log.Debugf("HeaderMatches: Adding %s", hdr.Name)
  1310  				headerMatches = append(headerMatches, &cilium.HeaderMatch{
  1311  					MismatchAction: mismatch_action,
  1312  					Name:           hdr.Name,
  1313  					Value:          value,
  1314  				})
  1315  			}
  1316  		}
  1317  	}
  1318  	if len(headers) == 0 {
  1319  		headers = nil
  1320  	} else {
  1321  		SortHeaderMatchers(headers)
  1322  	}
  1323  	if len(headerMatches) == 0 {
  1324  		headerMatches = nil
  1325  	} else {
  1326  		// Optimally we should sort the headerMatches to avoid
  1327  		// updating the policy if only the order of the rules
  1328  		// has changed. Right now, when 'headerMatches' is a
  1329  		// slice (rather than a map) the order only changes if
  1330  		// the order of the rules in the imported policies
  1331  		// changes, so there is minimal likelihood of
  1332  		// unnecessary policy updates.
  1333  
  1334  		// SortHeaderMatches(headerMatches)
  1335  	}
  1336  
  1337  	return &cilium.HttpNetworkPolicyRule{Headers: headers, HeaderMatches: headerMatches}, len(headerMatches) == 0
  1338  }
  1339  
  1340  var CiliumXDSConfigSource = &envoy_config_core.ConfigSource{
  1341  	ResourceApiVersion: envoy_config_core.ApiVersion_V3,
  1342  	ConfigSourceSpecifier: &envoy_config_core.ConfigSource_ApiConfigSource{
  1343  		ApiConfigSource: &envoy_config_core.ApiConfigSource{
  1344  			ApiType:                   envoy_config_core.ApiConfigSource_GRPC,
  1345  			TransportApiVersion:       envoy_config_core.ApiVersion_V3,
  1346  			SetNodeOnFirstMessageOnly: true,
  1347  			GrpcServices: []*envoy_config_core.GrpcService{
  1348  				{
  1349  					TargetSpecifier: &envoy_config_core.GrpcService_EnvoyGrpc_{
  1350  						EnvoyGrpc: &envoy_config_core.GrpcService_EnvoyGrpc{
  1351  							ClusterName: CiliumXDSClusterName,
  1352  						},
  1353  					},
  1354  				},
  1355  			},
  1356  		},
  1357  	},
  1358  }
  1359  
  1360  // toEnvoyFullTLSContext converts a "policy" TLS context (i.e., from a CiliumNetworkPolicy or
  1361  // CiliumClusterwideNetworkPolicy) into a "cilium envoy" TLS context (i.e., for the Cilium proxy plugin for Envoy).
  1362  //
  1363  // Deprecated: This includes both the CA *and* the cert/key. You almost certainly don't want this. Use
  1364  // toEnvoyOriginatingTLSContext or toEnvoyTerminatingTLSContext instead.
  1365  // x-ref: https://github.com/cilium/cilium/issues/31761
  1366  func toEnvoyFullTLSContext(tls *policy.TLSContext) *cilium.TLSContext {
  1367  	return &cilium.TLSContext{
  1368  		TrustedCa:        tls.TrustedCA,
  1369  		CertificateChain: tls.CertificateChain,
  1370  		PrivateKey:       tls.PrivateKey,
  1371  	}
  1372  }
  1373  
  1374  // toEnvoyOriginatingTLSContext converts a "policy" TLS context (i.e., from a CiliumNetworkPolicy or
  1375  // CiliumClusterwideNetworkPolicy) for originating TLS (i.e., verifying TLS connections from *outside*) into a "cilium
  1376  // envoy" TLS context (i.e., for the Cilium proxy plugin for Envoy).
  1377  func toEnvoyOriginatingTLSContext(tls *policy.TLSContext) *cilium.TLSContext {
  1378  	return &cilium.TLSContext{
  1379  		TrustedCa: tls.TrustedCA,
  1380  	}
  1381  }
  1382  
  1383  // toEnvoyTerminatingTLSContext converts a "policy" TLS context (i.e., from a CiliumNetworkPolicy or
  1384  // CiliumClusterwideNetworkPolicy) for terminating TLS (i.e., providing a valid cert to clients *inside*) into a "cilium
  1385  // envoy" TLS context (i.e., for the Cilium proxy plugin for Envoy).
  1386  func toEnvoyTerminatingTLSContext(tls *policy.TLSContext) *cilium.TLSContext {
  1387  	return &cilium.TLSContext{
  1388  		CertificateChain: tls.CertificateChain,
  1389  		PrivateKey:       tls.PrivateKey,
  1390  	}
  1391  }
  1392  
  1393  func GetEnvoyHTTPRules(secretManager certificatemanager.SecretManager, l7Rules *api.L7Rules, ns string) (*cilium.HttpNetworkPolicyRules, bool) {
  1394  	if len(l7Rules.HTTP) > 0 { // Just cautious. This should never be false.
  1395  		// Assume none of the rules have side-effects so that rule evaluation can
  1396  		// be stopped as soon as the first allowing rule is found. 'canShortCircuit'
  1397  		// is set to 'false' below if any rules with side effects are encountered,
  1398  		// causing all the applicable rules to be evaluated instead.
  1399  		canShortCircuit := true
  1400  		httpRules := make([]*cilium.HttpNetworkPolicyRule, 0, len(l7Rules.HTTP))
  1401  		for _, l7 := range l7Rules.HTTP {
  1402  			var cs bool
  1403  			rule, cs := getHTTPRule(secretManager, &l7, ns)
  1404  			httpRules = append(httpRules, rule)
  1405  			if !cs {
  1406  				canShortCircuit = false
  1407  			}
  1408  		}
  1409  		SortHTTPNetworkPolicyRules(httpRules)
  1410  		return &cilium.HttpNetworkPolicyRules{
  1411  			HttpRules: httpRules,
  1412  		}, canShortCircuit
  1413  	}
  1414  	return nil, true
  1415  }
  1416  
  1417  func getPortNetworkPolicyRule(sel policy.CachedSelector, wildcard bool, l7Parser policy.L7ParserType, l7Rules *policy.PerSelectorPolicy, useFullTLSContext bool) (*cilium.PortNetworkPolicyRule, bool) {
  1418  	r := &cilium.PortNetworkPolicyRule{}
  1419  
  1420  	// Optimize the policy if the endpoint selector is a wildcard by
  1421  	// keeping remote policies list empty to match all remote policies.
  1422  	if !wildcard {
  1423  		selections := sel.GetSelections()
  1424  
  1425  		// No remote policies would match this rule. Discard it.
  1426  		if len(selections) == 0 {
  1427  			return nil, true
  1428  		}
  1429  
  1430  		r.RemotePolicies = selections.AsUint32Slice()
  1431  	}
  1432  
  1433  	if l7Rules == nil {
  1434  		// L3/L4 only rule, everything in L7 is allowed && no TLS
  1435  		return r, true
  1436  	}
  1437  
  1438  	if l7Rules.IsDeny {
  1439  		r.Deny = true
  1440  		return r, false
  1441  	}
  1442  
  1443  	if useFullTLSContext {
  1444  		// Intentionally retain compatibility with older, buggy behaviour. In this mode the full contents of the
  1445  		// Kubernetes secret identified by TLS context is copied into the downstream/upstream TLS context. For the
  1446  		// downstream (i.e., in-cluster pod) this could include a CA bundle if a ca.crt key is present in the secret,
  1447  		// which may incorrectly lead Envoy to require client certificates.
  1448  		if l7Rules.TerminatingTLS != nil {
  1449  			r.DownstreamTlsContext = toEnvoyFullTLSContext(l7Rules.TerminatingTLS)
  1450  		}
  1451  		if l7Rules.OriginatingTLS != nil {
  1452  			r.UpstreamTlsContext = toEnvoyFullTLSContext(l7Rules.OriginatingTLS)
  1453  		}
  1454  	} else {
  1455  		if l7Rules.TerminatingTLS != nil {
  1456  			r.DownstreamTlsContext = toEnvoyTerminatingTLSContext(l7Rules.TerminatingTLS)
  1457  		}
  1458  		if l7Rules.OriginatingTLS != nil {
  1459  			r.UpstreamTlsContext = toEnvoyOriginatingTLSContext(l7Rules.OriginatingTLS)
  1460  		}
  1461  	}
  1462  	if len(l7Rules.ServerNames) > 0 {
  1463  		r.ServerNames = make([]string, 0, len(l7Rules.ServerNames))
  1464  		for sni := range l7Rules.ServerNames {
  1465  			r.ServerNames = append(r.ServerNames, sni)
  1466  		}
  1467  		sort.Strings(r.ServerNames)
  1468  	}
  1469  
  1470  	// Assume none of the rules have side-effects so that rule evaluation can
  1471  	// be stopped as soon as the first allowing rule is found. 'canShortCircuit'
  1472  	// is set to 'false' below if any rules with side effects are encountered,
  1473  	// causing all the applicable rules to be evaluated instead.
  1474  	canShortCircuit := true
  1475  	switch l7Parser {
  1476  	case policy.ParserTypeHTTP:
  1477  		// 'r.L7' is an interface which must not be set to a typed 'nil',
  1478  		// so check if we have any rules
  1479  		if len(l7Rules.HTTP) > 0 {
  1480  			// Use L7 rules computed earlier?
  1481  			var httpRules *cilium.HttpNetworkPolicyRules
  1482  			if l7Rules.EnvoyHTTPRules != nil {
  1483  				httpRules = l7Rules.EnvoyHTTPRules
  1484  				canShortCircuit = l7Rules.CanShortCircuit
  1485  			} else {
  1486  				httpRules, canShortCircuit = GetEnvoyHTTPRules(nil, &l7Rules.L7Rules, "")
  1487  			}
  1488  			r.L7 = &cilium.PortNetworkPolicyRule_HttpRules{
  1489  				HttpRules: httpRules,
  1490  			}
  1491  		}
  1492  
  1493  	case policy.ParserTypeKafka:
  1494  		// Kafka is implemented as an Envoy Go Extension
  1495  		if len(l7Rules.Kafka) > 0 {
  1496  			// L7 rules are not sorted
  1497  			r.L7Proto = l7Parser.String()
  1498  			r.L7 = &cilium.PortNetworkPolicyRule_KafkaRules{
  1499  				KafkaRules: getKafkaL7Rules(l7Rules.Kafka),
  1500  			}
  1501  		}
  1502  
  1503  	case policy.ParserTypeDNS:
  1504  		// TODO: Support DNS. For now, just ignore any DNS L7 rule.
  1505  
  1506  	default:
  1507  		// Assume unknown parser types use a Key-Value Pair policy
  1508  		if len(l7Rules.L7) > 0 {
  1509  			// L7 rules are not sorted
  1510  			r.L7Proto = l7Parser.String()
  1511  			r.L7 = &cilium.PortNetworkPolicyRule_L7Rules{
  1512  				L7Rules: getL7Rules(l7Rules.L7, r.L7Proto),
  1513  			}
  1514  		}
  1515  	}
  1516  
  1517  	return r, canShortCircuit
  1518  }
  1519  
  1520  // getWildcardNetworkPolicyRule returns the rule for port 0, which
  1521  // will be considered after port-specific rules.
  1522  func getWildcardNetworkPolicyRule(selectors policy.L7DataMap) *cilium.PortNetworkPolicyRule {
  1523  	// selections are pre-sorted, so sorting is only needed if merging selections from multiple selectors
  1524  	if len(selectors) == 1 {
  1525  		for sel := range selectors {
  1526  			if sel.IsWildcard() {
  1527  				return &cilium.PortNetworkPolicyRule{}
  1528  			}
  1529  			selections := sel.GetSelections()
  1530  			if len(selections) == 0 {
  1531  				// No remote policies would match this rule. Discard it.
  1532  				return nil
  1533  			}
  1534  			return &cilium.PortNetworkPolicyRule{
  1535  				RemotePolicies: selections.AsUint32Slice(),
  1536  			}
  1537  		}
  1538  	}
  1539  
  1540  	// Get selections for each selector and count how many there are
  1541  	sels := make([][]uint32, 0, len(selectors))
  1542  	wildcardFound := false
  1543  	var count int
  1544  	for sel, l7 := range selectors {
  1545  		if sel.IsWildcard() {
  1546  			wildcardFound = true
  1547  			break
  1548  		}
  1549  
  1550  		if l7.IsRedirect() {
  1551  			// Issue a warning if this port-0 rule is a redirect.
  1552  			// Deny rules don't support L7 therefore for the deny case
  1553  			// l7.IsRedirect() will always return false.
  1554  			log.Warningf("L3-only rule for selector %v surprisingly requires proxy redirection (%v)!", sel, *l7)
  1555  		}
  1556  
  1557  		selections := sel.GetSelections()
  1558  		if len(selections) == 0 {
  1559  			continue
  1560  		}
  1561  		count += len(selections)
  1562  		sels = append(sels, selections.AsUint32Slice())
  1563  	}
  1564  
  1565  	var remotePolicies []uint32
  1566  
  1567  	if wildcardFound {
  1568  		// Optimize the policy if the endpoint selector is a wildcard by
  1569  		// keeping remote policies list empty to match all remote policies.
  1570  	} else if count == 0 {
  1571  		// No remote policies would match this rule. Discard it.
  1572  		return nil
  1573  	} else {
  1574  		// allocate slice and copy selected identities
  1575  		remotePolicies = make([]uint32, 0, count)
  1576  		for _, selections := range sels {
  1577  			remotePolicies = append(remotePolicies, selections...)
  1578  		}
  1579  		slices.Sort(remotePolicies)
  1580  		remotePolicies = slices.Compact(remotePolicies)
  1581  	}
  1582  	return &cilium.PortNetworkPolicyRule{
  1583  		RemotePolicies: remotePolicies,
  1584  	}
  1585  }
  1586  
  1587  func getDirectionNetworkPolicy(ep endpoint.EndpointUpdater, l4Policy policy.L4PolicyMap, policyEnforced bool, useFullTLSContext bool, vis policy.DirectionalVisibilityPolicy, dir string) []*cilium.PortNetworkPolicy {
  1588  	// TODO: integrate visibility with enforced policy
  1589  	if !policyEnforced {
  1590  		PerPortPolicies := make([]*cilium.PortNetworkPolicy, 0, len(vis)+1)
  1591  		// Always allow all ports
  1592  		PerPortPolicies = append(PerPortPolicies, allowAllTCPPortNetworkPolicy)
  1593  		for _, visMeta := range vis {
  1594  			// Set up rule with 'L7Proto' as needed for proxylib parsers
  1595  			if visMeta.Proto == u8proto.TCP && visMeta.Parser != policy.ParserTypeHTTP && visMeta.Parser != policy.ParserTypeDNS {
  1596  				PerPortPolicies = append(PerPortPolicies, &cilium.PortNetworkPolicy{
  1597  					Port:     uint32(visMeta.Port),
  1598  					Protocol: envoy_config_core.SocketAddress_TCP,
  1599  					Rules: []*cilium.PortNetworkPolicyRule{
  1600  						{
  1601  							L7Proto: visMeta.Parser.String(),
  1602  						},
  1603  					},
  1604  				})
  1605  			}
  1606  		}
  1607  		return SortPortNetworkPolicies(PerPortPolicies)
  1608  	}
  1609  
  1610  	if l4Policy == nil || l4Policy.Len() == 0 {
  1611  		return nil
  1612  	}
  1613  
  1614  	PerPortPolicies := make([]*cilium.PortNetworkPolicy, 0, l4Policy.Len())
  1615  	l4Policy.ForEach(func(l4 *policy.L4Filter) bool {
  1616  		var protocol envoy_config_core.SocketAddress_Protocol
  1617  		switch l4.Protocol {
  1618  		case api.ProtoTCP:
  1619  			protocol = envoy_config_core.SocketAddress_TCP
  1620  		case api.ProtoUDP, api.ProtoSCTP:
  1621  			// UDP/SCTP rules not sent to Envoy for now.
  1622  			return true
  1623  		}
  1624  
  1625  		port := l4.Port
  1626  		if port == 0 && l4.PortName != "" {
  1627  			port = ep.GetNamedPort(l4.Ingress, l4.PortName, uint8(l4.U8Proto))
  1628  			if port == 0 {
  1629  				return true // Skip if a named port can not be resolved (yet)
  1630  			}
  1631  		}
  1632  
  1633  		rules := make([]*cilium.PortNetworkPolicyRule, 0, len(l4.PerSelectorPolicies))
  1634  		allowAll := false
  1635  
  1636  		// Assume none of the rules have side-effects so that rule evaluation can
  1637  		// be stopped as soon as the first allowing rule is found. 'canShortCircuit'
  1638  		// is set to 'false' below if any rules with side effects are encountered,
  1639  		// causing all the applicable rules to be evaluated instead.
  1640  		canShortCircuit := true
  1641  
  1642  		if port == 0 {
  1643  			// L3-only rule, must generate L7 allow-all in case there are other
  1644  			// port-specific rules. Otherwise traffic from allowed remotes could be dropped.
  1645  			rule := getWildcardNetworkPolicyRule(l4.PerSelectorPolicies)
  1646  			if rule != nil {
  1647  				log.WithFields(logrus.Fields{
  1648  					logfields.EndpointID:       ep.GetID(),
  1649  					logfields.TrafficDirection: dir,
  1650  					logfields.Port:             port,
  1651  					logfields.PolicyID:         rule.RemotePolicies,
  1652  				}).Debug("Wildcard PortNetworkPolicyRule matching remote IDs")
  1653  
  1654  				if len(rule.RemotePolicies) == 0 {
  1655  					// Got an allow-all rule, which can short-circuit all of
  1656  					// the other rules.
  1657  					allowAll = true
  1658  				}
  1659  				rules = append(rules, rule)
  1660  			}
  1661  		} else {
  1662  			nSelectors := len(l4.PerSelectorPolicies)
  1663  			for sel, l7 := range l4.PerSelectorPolicies {
  1664  				// A single selector is effectively a wildcard, as bpf passes through
  1665  				// only allowed l3. If there are multiple selectors for this l4-filter
  1666  				// then the proxy may need to drop some allowed l3 due to l7 rules potentially
  1667  				// being different between the selectors.
  1668  				wildcard := nSelectors == 1 || sel.IsWildcard()
  1669  				rule, cs := getPortNetworkPolicyRule(sel, wildcard, l4.L7Parser, l7, useFullTLSContext)
  1670  				if rule != nil {
  1671  					if !cs {
  1672  						canShortCircuit = false
  1673  					}
  1674  
  1675  					log.WithFields(logrus.Fields{
  1676  						logfields.EndpointID:       ep.GetID(),
  1677  						logfields.TrafficDirection: dir,
  1678  						logfields.Port:             port,
  1679  						logfields.PolicyID:         rule.RemotePolicies,
  1680  						logfields.ServerNames:      rule.ServerNames,
  1681  					}).Debug("PortNetworkPolicyRule matching remote IDs")
  1682  
  1683  					if len(rule.RemotePolicies) == 0 && rule.L7 == nil && rule.DownstreamTlsContext == nil && rule.UpstreamTlsContext == nil && len(rule.ServerNames) == 0 {
  1684  						// Got an allow-all rule, which can short-circuit all of
  1685  						// the other rules.
  1686  						allowAll = true
  1687  					}
  1688  					rules = append(rules, rule)
  1689  				}
  1690  			}
  1691  		}
  1692  		// Short-circuit rules if a rule allows all and all other rules can be short-circuited
  1693  		if allowAll && canShortCircuit {
  1694  			log.Debug("Short circuiting HTTP rules due to rule allowing all and no other rules needing attention")
  1695  			rules = nil
  1696  		}
  1697  
  1698  		// No rule for this port matches any remote identity.
  1699  		// This means that no traffic was explicitly allowed for this port.
  1700  		// In this case, just don't generate any PortNetworkPolicy for this
  1701  		// port.
  1702  		if !allowAll && len(rules) == 0 {
  1703  			return true
  1704  		}
  1705  
  1706  		// NPDS supports port ranges.
  1707  		PerPortPolicies = append(PerPortPolicies, &cilium.PortNetworkPolicy{
  1708  			Port:     uint32(port),
  1709  			EndPort:  uint32(l4.EndPort),
  1710  			Protocol: protocol,
  1711  			Rules:    SortPortNetworkPolicyRules(rules),
  1712  		})
  1713  		return true
  1714  	})
  1715  
  1716  	if len(PerPortPolicies) == 0 {
  1717  		return nil
  1718  	}
  1719  
  1720  	return SortPortNetworkPolicies(PerPortPolicies)
  1721  }
  1722  
  1723  // getNetworkPolicy converts a network policy into a cilium.NetworkPolicy.
  1724  func getNetworkPolicy(ep endpoint.EndpointUpdater, vis *policy.VisibilityPolicy, ips []string, l4Policy *policy.L4Policy,
  1725  	ingressPolicyEnforced, egressPolicyEnforced, useFullTLSContext bool,
  1726  ) *cilium.NetworkPolicy {
  1727  	p := &cilium.NetworkPolicy{
  1728  		EndpointIps:      ips,
  1729  		EndpointId:       ep.GetID(),
  1730  		ConntrackMapName: ep.ConntrackNameLocked(),
  1731  	}
  1732  
  1733  	var visIngress policy.DirectionalVisibilityPolicy
  1734  	var visEgress policy.DirectionalVisibilityPolicy
  1735  	if vis != nil {
  1736  		visIngress = vis.Ingress
  1737  		visEgress = vis.Egress
  1738  	}
  1739  	var ingressMap policy.L4PolicyMap
  1740  	var egressMap policy.L4PolicyMap
  1741  	if l4Policy != nil {
  1742  		ingressMap = l4Policy.Ingress.PortRules
  1743  		egressMap = l4Policy.Egress.PortRules
  1744  	}
  1745  	p.IngressPerPortPolicies = getDirectionNetworkPolicy(ep, ingressMap, ingressPolicyEnforced, useFullTLSContext, visIngress, "ingress")
  1746  	p.EgressPerPortPolicies = getDirectionNetworkPolicy(ep, egressMap, egressPolicyEnforced, useFullTLSContext, visEgress, "egress")
  1747  
  1748  	return p
  1749  }
  1750  
  1751  // return the Envoy proxy node IDs that need to ACK the policy.
  1752  func getNodeIDs(ep endpoint.EndpointUpdater, policy *policy.L4Policy) []string {
  1753  	nodeIDs := make([]string, 0, 1)
  1754  
  1755  	// Host proxy uses "127.0.0.1" as the nodeID
  1756  	nodeIDs = append(nodeIDs, "127.0.0.1")
  1757  	// Require additional ACK from proxylib if policy has proxylib redirects
  1758  	// Note that if a previous policy had a proxylib redirect and this one does not,
  1759  	// we only wait for the ACK from the main Envoy node ID.
  1760  	if policy.HasProxylibRedirect() {
  1761  		// Proxylib uses "127.0.0.2" as the nodeID
  1762  		nodeIDs = append(nodeIDs, "127.0.0.2")
  1763  	}
  1764  	return nodeIDs
  1765  }
  1766  
  1767  func (s *xdsServer) UpdateNetworkPolicy(ep endpoint.EndpointUpdater, vis *policy.VisibilityPolicy, policy *policy.L4Policy,
  1768  	ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup,
  1769  ) (error, func() error) {
  1770  	s.mutex.Lock()
  1771  	defer s.mutex.Unlock()
  1772  
  1773  	var ips []string
  1774  	if ipv6 := ep.GetIPv6Address(); ipv6 != "" {
  1775  		ips = append(ips, ipv6)
  1776  	}
  1777  	if ipv4 := ep.GetIPv4Address(); ipv4 != "" {
  1778  		ips = append(ips, ipv4)
  1779  	}
  1780  	if len(ips) == 0 {
  1781  		// It looks like the "host EP" (identity == 1) has no IPs, so it is possible to find
  1782  		// there are no IPs here. In this case just skip without updating a policy, as
  1783  		// policies are always keyed by an IP.
  1784  		//
  1785  		// TODO: When L7 policy support for the host is needed, all host IPs should be
  1786  		// considered here?
  1787  		log.WithField(logfields.EndpointID, ep.GetID()).Debug("Endpoint has no IP addresses")
  1788  		return nil, func() error { return nil }
  1789  	}
  1790  
  1791  	networkPolicy := getNetworkPolicy(ep, vis, ips, policy, ingressPolicyEnforced, egressPolicyEnforced, s.config.useFullTLSContext)
  1792  
  1793  	// First, validate the policy
  1794  	err := networkPolicy.Validate()
  1795  	if err != nil {
  1796  		return fmt.Errorf("error validating generated NetworkPolicy for Endpoint %d: %w", ep.GetID(), err), nil
  1797  	}
  1798  
  1799  	nodeIDs := getNodeIDs(ep, policy)
  1800  
  1801  	// If there are no listeners configured, the local node's Envoy proxy won't
  1802  	// query for network policies and therefore will never ACK them, and we'd
  1803  	// wait forever.
  1804  	if s.proxyListeners == 0 {
  1805  		wg = nil
  1806  	}
  1807  
  1808  	// When successful, push policy into the cache.
  1809  	var callback func(error)
  1810  	if policy != nil {
  1811  		policyRevision := policy.Revision
  1812  		callback = func(err error) {
  1813  			if err == nil {
  1814  				go ep.OnProxyPolicyUpdate(policyRevision)
  1815  			}
  1816  		}
  1817  	}
  1818  	epID := ep.GetID()
  1819  	resourceName := strconv.FormatUint(epID, 10)
  1820  	revertFunc := s.NetworkPolicyMutator.Upsert(NetworkPolicyTypeURL, resourceName, networkPolicy, nodeIDs, wg, callback)
  1821  	revertUpdatedNetworkPolicyEndpoints := make(map[string]endpoint.EndpointUpdater, len(ips))
  1822  	for _, ip := range ips {
  1823  		revertUpdatedNetworkPolicyEndpoints[ip] = s.localEndpointStore.getLocalEndpoint(ip)
  1824  		s.localEndpointStore.setLocalEndpoint(ip, ep)
  1825  	}
  1826  
  1827  	return nil, func() error {
  1828  		log.Debug("Reverting xDS network policy update")
  1829  
  1830  		s.mutex.Lock()
  1831  		defer s.mutex.Unlock()
  1832  
  1833  		for ip, ep := range revertUpdatedNetworkPolicyEndpoints {
  1834  			if ep == nil {
  1835  				s.localEndpointStore.removeLocalEndpoint(ip)
  1836  			} else {
  1837  				s.localEndpointStore.setLocalEndpoint(ip, ep)
  1838  			}
  1839  		}
  1840  
  1841  		// Don't wait for an ACK for the reverted xDS updates.
  1842  		// This is best-effort.
  1843  		revertFunc(nil)
  1844  
  1845  		log.Debug("Finished reverting xDS network policy update")
  1846  
  1847  		return nil
  1848  	}
  1849  }
  1850  
  1851  func (s *xdsServer) RemoveNetworkPolicy(ep endpoint.EndpointInfoSource) {
  1852  	s.mutex.Lock()
  1853  	defer s.mutex.Unlock()
  1854  
  1855  	epID := ep.GetID()
  1856  	resourceName := strconv.FormatUint(epID, 10)
  1857  	s.networkPolicyCache.Delete(NetworkPolicyTypeURL, resourceName)
  1858  
  1859  	ip := ep.GetIPv6Address()
  1860  	if ip != "" {
  1861  		s.localEndpointStore.removeLocalEndpoint(ip)
  1862  	}
  1863  	ip = ep.GetIPv4Address()
  1864  	if ip != "" {
  1865  		s.localEndpointStore.removeLocalEndpoint(ip)
  1866  		// Delete node resources held in the cache for the endpoint
  1867  		s.NetworkPolicyMutator.DeleteNode(ip)
  1868  	}
  1869  }
  1870  
  1871  func (s *xdsServer) RemoveAllNetworkPolicies() {
  1872  	s.networkPolicyCache.Clear(NetworkPolicyTypeURL)
  1873  }
  1874  
  1875  func (s *xdsServer) GetNetworkPolicies(resourceNames []string) (map[string]*cilium.NetworkPolicy, error) {
  1876  	resources, err := s.networkPolicyCache.GetResources(NetworkPolicyTypeURL, 0, "", resourceNames)
  1877  	if err != nil {
  1878  		return nil, err
  1879  	}
  1880  	networkPolicies := make(map[string]*cilium.NetworkPolicy, len(resources.Resources))
  1881  	for _, res := range resources.Resources {
  1882  		networkPolicy := res.(*cilium.NetworkPolicy)
  1883  		for _, ip := range networkPolicy.EndpointIps {
  1884  			networkPolicies[ip] = networkPolicy
  1885  		}
  1886  	}
  1887  	return networkPolicies, nil
  1888  }
  1889  
  1890  // Resources contains all Envoy resources parsed from a CiliumEnvoyConfig CRD
  1891  type Resources struct {
  1892  	Listeners []*envoy_config_listener.Listener
  1893  	Secrets   []*envoy_config_tls.Secret
  1894  	Routes    []*envoy_config_route.RouteConfiguration
  1895  	Clusters  []*envoy_config_cluster.Cluster
  1896  	Endpoints []*envoy_config_endpoint.ClusterLoadAssignment
  1897  
  1898  	// Callback functions that are called if the corresponding Listener change was successfully acked by Envoy
  1899  	PortAllocationCallbacks map[string]func(context.Context) error
  1900  }
  1901  
  1902  // ListenersAddedOrDeleted returns 'true' if a listener is added or removed when updating from 'old'
  1903  // to 'new'
  1904  func (old *Resources) ListenersAddedOrDeleted(new *Resources) bool {
  1905  	// Typically the number of listeners in a CEC is small (e.g, one), so it should be OK to
  1906  	// scan the slices like here
  1907  	for _, nl := range new.Listeners {
  1908  		found := false
  1909  		for _, ol := range old.Listeners {
  1910  			if ol.Name == nl.Name {
  1911  				found = true
  1912  				break
  1913  			}
  1914  		}
  1915  		if !found {
  1916  			return true // a listener was added
  1917  		}
  1918  	}
  1919  	for _, ol := range old.Listeners {
  1920  		found := false
  1921  		for _, nl := range new.Listeners {
  1922  			if nl.Name == ol.Name {
  1923  				found = true
  1924  				break
  1925  			}
  1926  		}
  1927  		if !found {
  1928  			return true // a listener was removed
  1929  		}
  1930  	}
  1931  	return false
  1932  }
  1933  
  1934  func (s *xdsServer) UpsertEnvoyResources(ctx context.Context, resources Resources) error {
  1935  	if option.Config.Debug {
  1936  		msg := ""
  1937  		sep := ""
  1938  		if len(resources.Listeners) > 0 {
  1939  			msg += fmt.Sprintf("%d listeners", len(resources.Listeners))
  1940  			sep = ", "
  1941  		}
  1942  		if len(resources.Routes) > 0 {
  1943  			msg += fmt.Sprintf("%s%d routes", sep, len(resources.Routes))
  1944  			sep = ", "
  1945  		}
  1946  		if len(resources.Clusters) > 0 {
  1947  			msg += fmt.Sprintf("%s%d clusters", sep, len(resources.Clusters))
  1948  			sep = ", "
  1949  		}
  1950  		if len(resources.Endpoints) > 0 {
  1951  			msg += fmt.Sprintf("%s%d endpoints", sep, len(resources.Endpoints))
  1952  			sep = ", "
  1953  		}
  1954  		if len(resources.Secrets) > 0 {
  1955  			msg += fmt.Sprintf("%s%d secrets", sep, len(resources.Secrets))
  1956  		}
  1957  
  1958  		log.Debugf("UpsertEnvoyResources: Upserting %s...", msg)
  1959  	}
  1960  	var wg *completion.WaitGroup
  1961  	// Listener config may fail if it refers to a cluster that has not been added yet, so we
  1962  	// must wait for Envoy to ACK cluster config before adding Listeners to be sure Listener
  1963  	// config does not fail for this reason.
  1964  	// Enable wait before new Listeners are added if clusters are also added.
  1965  	if len(resources.Listeners) > 0 && len(resources.Clusters) > 0 {
  1966  		wg = completion.NewWaitGroup(ctx)
  1967  	}
  1968  	var revertFuncs xds.AckingResourceMutatorRevertFuncList
  1969  	// Do not wait for the addition of routes, clusters, endpoints, routes,
  1970  	// or secrets as there are no guarantees that these additions will be
  1971  	// acked. For example, if the listener referring to was already deleted
  1972  	// earlier, there are no references to the deleted resources anymore,
  1973  	// in which case we could wait forever for the ACKs. This could also
  1974  	// happen if there is no listener referring to these named
  1975  	// resources to begin with.
  1976  	// If both listeners and clusters are added then wait for clusters.
  1977  	for _, r := range resources.Secrets {
  1978  		log.Debugf("Envoy upsertSecret %s", r.Name)
  1979  		revertFuncs = append(revertFuncs, s.upsertSecret(r.Name, r, nil, nil))
  1980  	}
  1981  	for _, r := range resources.Endpoints {
  1982  		log.Debugf("Envoy upsertEndpoint %s %v", r.ClusterName, r)
  1983  		revertFuncs = append(revertFuncs, s.upsertEndpoint(r.ClusterName, r, nil, nil))
  1984  	}
  1985  	for _, r := range resources.Clusters {
  1986  		log.Debugf("Envoy upsertCluster %s %v", r.Name, r)
  1987  		revertFuncs = append(revertFuncs, s.upsertCluster(r.Name, r, wg, nil))
  1988  	}
  1989  	for _, r := range resources.Routes {
  1990  		log.Debugf("Envoy upsertRoute %s %v", r.Name, r)
  1991  		revertFuncs = append(revertFuncs, s.upsertRoute(r.Name, r, nil, nil))
  1992  	}
  1993  	// Wait before new Listeners are added if clusters were also added above.
  1994  	if wg != nil {
  1995  		start := time.Now()
  1996  		log.Debug("UpsertEnvoyResources: Waiting for cluster updates to complete...")
  1997  		err := wg.Wait()
  1998  		log.Debugf("UpsertEnvoyResources: Wait time for cluster updates %v (err: %s)", time.Since(start), err)
  1999  
  2000  		// revert all changes in case of failure
  2001  		if err != nil {
  2002  			revertFuncs.Revert(nil)
  2003  			log.Debug("UpsertEnvoyResources: Finished reverting failed xDS transactions")
  2004  			return err
  2005  		}
  2006  		wg = nil
  2007  	}
  2008  	// Wait only if new Listeners are added, as they will always be acked.
  2009  	// (unreferenced routes or endpoints (and maybe clusters) are not ACKed or NACKed).
  2010  	if len(resources.Listeners) > 0 {
  2011  		wg = completion.NewWaitGroup(ctx)
  2012  	}
  2013  	for _, r := range resources.Listeners {
  2014  		log.Debugf("Envoy upsertListener %s %v", r.Name, r)
  2015  		listenerName := r.Name
  2016  		revertFuncs = append(revertFuncs, s.upsertListener(r.Name, r, wg,
  2017  			// this callback is not called if there is no change
  2018  			func(err error) {
  2019  				if err == nil && resources.PortAllocationCallbacks[listenerName] != nil {
  2020  					if callbackErr := resources.PortAllocationCallbacks[listenerName](ctx); callbackErr != nil {
  2021  						log.WithError(callbackErr).Warn("Failure in port allocation callback")
  2022  					}
  2023  				}
  2024  			}))
  2025  	}
  2026  	if wg != nil {
  2027  		start := time.Now()
  2028  		log.Debug("UpsertEnvoyResources: Waiting for proxy updates to complete...")
  2029  		err := wg.Wait()
  2030  		log.Debugf("UpsertEnvoyResources: Wait time for proxy updates %v (err: %s)", time.Since(start), err)
  2031  
  2032  		// revert all changes in case of failure
  2033  		if err != nil {
  2034  			revertFuncs.Revert(nil)
  2035  			log.Debug("UpsertEnvoyResources: Finished reverting failed xDS transactions")
  2036  		}
  2037  		return err
  2038  	}
  2039  	return nil
  2040  }
  2041  
  2042  func (s *xdsServer) UpdateEnvoyResources(ctx context.Context, old, new Resources) error {
  2043  	waitForDelete := false
  2044  	var wg *completion.WaitGroup
  2045  	var revertFuncs xds.AckingResourceMutatorRevertFuncList
  2046  	// Wait only if new Listeners are added, as they will always be acked.
  2047  	// (unreferenced routes or endpoints (and maybe clusters) are not ACKed or NACKed).
  2048  	if len(new.Listeners) > 0 {
  2049  		wg = completion.NewWaitGroup(ctx)
  2050  	}
  2051  	// Delete old listeners not added in 'new' or if old and new listener have different ports
  2052  	var deleteListeners []*envoy_config_listener.Listener
  2053  	for _, oldListener := range old.Listeners {
  2054  		found := false
  2055  		port := uint32(0)
  2056  		if addr := oldListener.Address.GetSocketAddress(); addr != nil {
  2057  			port = addr.GetPortValue()
  2058  		}
  2059  		for _, newListener := range new.Listeners {
  2060  			if newListener.Name == oldListener.Name {
  2061  				if addr := newListener.Address.GetSocketAddress(); addr != nil && addr.GetPortValue() != port {
  2062  					log.Debugf("UpdateEnvoyResources: %s port changing from %d to %d...", newListener.Name, port, addr.GetPortValue())
  2063  					waitForDelete = true
  2064  				} else {
  2065  					// port is not changing, remove from new.PortAllocations to prevent acking an already acked port.
  2066  					delete(new.PortAllocationCallbacks, newListener.Name)
  2067  					found = true
  2068  				}
  2069  				break
  2070  			}
  2071  		}
  2072  		if !found {
  2073  			deleteListeners = append(deleteListeners, oldListener)
  2074  		}
  2075  	}
  2076  	log.Debugf("UpdateEnvoyResources: Deleting %d, Upserting %d listeners...", len(deleteListeners), len(new.Listeners))
  2077  	for _, listener := range deleteListeners {
  2078  		listenerName := listener.Name
  2079  		revertFuncs = append(revertFuncs, s.deleteListener(listener.Name, wg,
  2080  			func(err error) {
  2081  				if err == nil && old.PortAllocationCallbacks[listenerName] != nil {
  2082  					if callbackErr := old.PortAllocationCallbacks[listenerName](ctx); callbackErr != nil {
  2083  						log.WithError(callbackErr).Warn("Failure in port allocation callback")
  2084  					}
  2085  				}
  2086  			}))
  2087  	}
  2088  
  2089  	// Do not wait for the deletion of routes, clusters, endpoints, or
  2090  	// secrets as there are no quarantees that these deletions will be
  2091  	// acked. For example, if the listener referring to was already deleted
  2092  	// earlier, there are no references to the deleted resources any more,
  2093  	// in which case we could wait forever for the ACKs. This could also
  2094  	// happen if there is no listener referring to these other named
  2095  	// resources to begin with.
  2096  
  2097  	// Delete old routes not added in 'new'
  2098  	var deleteRoutes []*envoy_config_route.RouteConfiguration
  2099  	for _, oldRoute := range old.Routes {
  2100  		found := false
  2101  		for _, newRoute := range new.Routes {
  2102  			if newRoute.Name == oldRoute.Name {
  2103  				found = true
  2104  			}
  2105  		}
  2106  		if !found {
  2107  			deleteRoutes = append(deleteRoutes, oldRoute)
  2108  		}
  2109  	}
  2110  	log.Debugf("UpdateEnvoyResources: Deleting %d, Upserting %d routes...", len(deleteRoutes), len(new.Routes))
  2111  	for _, route := range deleteRoutes {
  2112  		revertFuncs = append(revertFuncs, s.deleteRoute(route.Name, nil, nil))
  2113  	}
  2114  
  2115  	// Delete old clusters not added in 'new'
  2116  	var deleteClusters []*envoy_config_cluster.Cluster
  2117  	for _, oldCluster := range old.Clusters {
  2118  		found := false
  2119  		for _, newCluster := range new.Clusters {
  2120  			if newCluster.Name == oldCluster.Name {
  2121  				found = true
  2122  			}
  2123  		}
  2124  		if !found {
  2125  			deleteClusters = append(deleteClusters, oldCluster)
  2126  		}
  2127  	}
  2128  	log.Debugf("UpdateEnvoyResources: Deleting %d, Upserting %d clusters...", len(deleteClusters), len(new.Clusters))
  2129  	for _, cluster := range deleteClusters {
  2130  		revertFuncs = append(revertFuncs, s.deleteCluster(cluster.Name, nil, nil))
  2131  	}
  2132  
  2133  	// Delete old endpoints not added in 'new'
  2134  	var deleteEndpoints []*envoy_config_endpoint.ClusterLoadAssignment
  2135  	for _, oldEndpoint := range old.Endpoints {
  2136  		found := false
  2137  		for _, newEndpoint := range new.Endpoints {
  2138  			if newEndpoint.ClusterName == oldEndpoint.ClusterName {
  2139  				found = true
  2140  			}
  2141  		}
  2142  		if !found {
  2143  			deleteEndpoints = append(deleteEndpoints, oldEndpoint)
  2144  		}
  2145  	}
  2146  	log.Debugf("UpdateEnvoyResources: Deleting %d, Upserting %d endpoints...", len(deleteEndpoints), len(new.Endpoints))
  2147  	for _, endpoint := range deleteEndpoints {
  2148  		revertFuncs = append(revertFuncs, s.deleteEndpoint(endpoint.ClusterName, nil, nil))
  2149  	}
  2150  
  2151  	// Delete old secrets not added in 'new'
  2152  	var deleteSecrets []*envoy_config_tls.Secret
  2153  	for _, oldSecret := range old.Secrets {
  2154  		found := false
  2155  		for _, newSecret := range new.Secrets {
  2156  			if newSecret.Name == oldSecret.Name {
  2157  				found = true
  2158  			}
  2159  		}
  2160  		if !found {
  2161  			deleteSecrets = append(deleteSecrets, oldSecret)
  2162  		}
  2163  	}
  2164  	log.Debugf("UpdateEnvoyResources: Deleting %d, Upserting %d secrets...", len(deleteSecrets), len(new.Secrets))
  2165  	for _, secret := range deleteSecrets {
  2166  		revertFuncs = append(revertFuncs, s.deleteSecret(secret.Name, nil, nil))
  2167  	}
  2168  
  2169  	// Have to wait for deletes to complete before adding new listeners if a listener's port number is changed.
  2170  	if wg != nil && waitForDelete {
  2171  		start := time.Now()
  2172  		log.Debug("UpdateEnvoyResources: Waiting for proxy deletes to complete...")
  2173  		err := wg.Wait()
  2174  		if err != nil {
  2175  			log.Debug("UpdateEnvoyResources: delete failed: ", err)
  2176  		}
  2177  		log.Debug("UpdateEnvoyResources: Wait time for proxy deletes: ", time.Since(start))
  2178  		// new wait group for adds
  2179  		wg = completion.NewWaitGroup(ctx)
  2180  	}
  2181  
  2182  	// Add new Secrets
  2183  	for _, r := range new.Secrets {
  2184  		revertFuncs = append(revertFuncs, s.upsertSecret(r.Name, r, nil, nil))
  2185  	}
  2186  	// Add new Endpoints
  2187  	for _, r := range new.Endpoints {
  2188  		revertFuncs = append(revertFuncs, s.upsertEndpoint(r.ClusterName, r, nil, nil))
  2189  	}
  2190  	// Add new Clusters
  2191  	for _, r := range new.Clusters {
  2192  		revertFuncs = append(revertFuncs, s.upsertCluster(r.Name, r, wg, nil))
  2193  	}
  2194  	// Add new Routes
  2195  	for _, r := range new.Routes {
  2196  		revertFuncs = append(revertFuncs, s.upsertRoute(r.Name, r, nil, nil))
  2197  	}
  2198  	if wg != nil && len(new.Clusters) > 0 {
  2199  		start := time.Now()
  2200  		log.Debug("UpdateEnvoyResources: Waiting for cluster updates to complete...")
  2201  		err := wg.Wait()
  2202  		if err != nil {
  2203  			log.Debug("UpdateEnvoyResources: cluster update failed: ", err)
  2204  		}
  2205  		log.Debug("UpdateEnvoyResources: Wait time for cluster updates: ", time.Since(start))
  2206  		// new wait group for adds
  2207  		wg = completion.NewWaitGroup(ctx)
  2208  	}
  2209  	// Add new Listeners
  2210  	for _, r := range new.Listeners {
  2211  		listenerName := r.Name
  2212  		revertFuncs = append(revertFuncs, s.upsertListener(r.Name, r, wg,
  2213  			// this callback is not called if there is no change
  2214  			func(err error) {
  2215  				if err == nil && new.PortAllocationCallbacks[listenerName] != nil {
  2216  					if callbackErr := new.PortAllocationCallbacks[listenerName](ctx); callbackErr != nil {
  2217  						log.WithError(callbackErr).Warn("Failure in port allocation callback")
  2218  					}
  2219  				}
  2220  			}))
  2221  	}
  2222  
  2223  	if wg != nil {
  2224  		start := time.Now()
  2225  		log.Debug("UpdateEnvoyResources: Waiting for proxy updates to complete...")
  2226  		err := wg.Wait()
  2227  		log.Debugf("UpdateEnvoyResources: Wait time for proxy updates %v (err: %s)", time.Since(start), err)
  2228  
  2229  		// revert all changes in case of failure
  2230  		if err != nil {
  2231  			revertFuncs.Revert(nil)
  2232  			log.Debug("UpdateEnvoyResources: Finished reverting failed xDS transactions")
  2233  		}
  2234  		return err
  2235  	}
  2236  	return nil
  2237  }
  2238  
  2239  func (s *xdsServer) DeleteEnvoyResources(ctx context.Context, resources Resources) error {
  2240  	log.Debugf("DeleteEnvoyResources: Deleting %d listeners, %d routes, %d clusters, %d endpoints, and %d secrets...",
  2241  		len(resources.Listeners), len(resources.Routes), len(resources.Clusters), len(resources.Endpoints), len(resources.Secrets))
  2242  	var wg *completion.WaitGroup
  2243  	var revertFuncs xds.AckingResourceMutatorRevertFuncList
  2244  	// Wait only if new Listeners are added, as they will always be acked.
  2245  	// (unreferenced routes or endpoints (and maybe clusters) are not ACKed or NACKed).
  2246  	if len(resources.Listeners) > 0 {
  2247  		wg = completion.NewWaitGroup(ctx)
  2248  	}
  2249  	for _, r := range resources.Listeners {
  2250  		listenerName := r.Name
  2251  		revertFuncs = append(revertFuncs, s.deleteListener(r.Name, wg,
  2252  			func(err error) {
  2253  				if err == nil && resources.PortAllocationCallbacks[listenerName] != nil {
  2254  					if callbackErr := resources.PortAllocationCallbacks[listenerName](ctx); callbackErr != nil {
  2255  						log.WithError(callbackErr).Warn("Failure in port allocation callback")
  2256  					}
  2257  				}
  2258  			}))
  2259  	}
  2260  
  2261  	// Do not wait for the deletion of routes, clusters, or endpoints, as
  2262  	// there are no guarantees that these deletions will be acked. For
  2263  	// example, if the listener referring to was already deleted earlier,
  2264  	// there are no references to the deleted resources anymore, in which
  2265  	// case we could wait forever for the ACKs. This could also happen if
  2266  	// there is no listener referring to other named resources to
  2267  	// begin with.
  2268  	for _, r := range resources.Routes {
  2269  		revertFuncs = append(revertFuncs, s.deleteRoute(r.Name, nil, nil))
  2270  	}
  2271  	for _, r := range resources.Clusters {
  2272  		revertFuncs = append(revertFuncs, s.deleteCluster(r.Name, nil, nil))
  2273  	}
  2274  	for _, r := range resources.Endpoints {
  2275  		revertFuncs = append(revertFuncs, s.deleteEndpoint(r.ClusterName, nil, nil))
  2276  	}
  2277  	for _, r := range resources.Secrets {
  2278  		revertFuncs = append(revertFuncs, s.deleteSecret(r.Name, nil, nil))
  2279  	}
  2280  
  2281  	if wg != nil {
  2282  		start := time.Now()
  2283  		log.Debug("DeleteEnvoyResources: Waiting for proxy updates to complete...")
  2284  		err := wg.Wait()
  2285  		log.Debugf("DeleteEnvoyResources: Wait time for proxy updates %v (err: %s)", time.Since(start), err)
  2286  
  2287  		// revert all changes in case of failure
  2288  		if err != nil {
  2289  			revertFuncs.Revert(nil)
  2290  			log.Debug("DeleteEnvoyResources: Finished reverting failed xDS transactions")
  2291  		}
  2292  		return err
  2293  	}
  2294  	return nil
  2295  }