github.com/imran-kn/cilium-fork@v1.6.9/pkg/envoy/server.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package envoy
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"net"
    22  	"os"
    23  	"path/filepath"
    24  	"sort"
    25  	"strings"
    26  	"syscall"
    27  	"time"
    28  
    29  	"github.com/cilium/cilium/pkg/bpf"
    30  	"github.com/cilium/cilium/pkg/completion"
    31  	"github.com/cilium/cilium/pkg/envoy/xds"
    32  	"github.com/cilium/cilium/pkg/identity"
    33  	"github.com/cilium/cilium/pkg/lock"
    34  	"github.com/cilium/cilium/pkg/option"
    35  	"github.com/cilium/cilium/pkg/policy"
    36  	"github.com/cilium/cilium/pkg/policy/api"
    37  	"github.com/cilium/cilium/pkg/proxy/logger"
    38  
    39  	"github.com/cilium/proxy/go/cilium/api"
    40  	envoy_api_v2 "github.com/cilium/proxy/go/envoy/api/v2"
    41  	envoy_api_v2_core "github.com/cilium/proxy/go/envoy/api/v2/core"
    42  	envoy_api_v2_endpoint "github.com/cilium/proxy/go/envoy/api/v2/endpoint"
    43  	envoy_api_v2_listener "github.com/cilium/proxy/go/envoy/api/v2/listener"
    44  	envoy_api_v2_route "github.com/cilium/proxy/go/envoy/api/v2/route"
    45  	envoy_config_bootstrap_v2 "github.com/cilium/proxy/go/envoy/config/bootstrap/v2"
    46  	"github.com/golang/protobuf/proto"
    47  	"github.com/golang/protobuf/ptypes/duration"
    48  	"github.com/golang/protobuf/ptypes/struct"
    49  	"github.com/golang/protobuf/ptypes/wrappers"
    50  )
    51  
    52  var (
    53  	// allowAllPortNetworkPolicy is a PortNetworkPolicy that allows all traffic
    54  	// to any L4 port.
    55  	allowAllPortNetworkPolicy = []*cilium.PortNetworkPolicy{
    56  		// Allow all TCP traffic to any port.
    57  		{Protocol: envoy_api_v2_core.SocketAddress_TCP},
    58  		// Allow all UDP traffic to any port.
    59  		{Protocol: envoy_api_v2_core.SocketAddress_UDP},
    60  	}
    61  )
    62  
    63  const (
    64  	egressClusterName  = "egress-cluster"
    65  	ingressClusterName = "ingress-cluster"
    66  	EnvoyTimeout       = 300 * time.Second // must be smaller than endpoint.EndpointGenerationTimeout
    67  )
    68  
    69  type Listener struct {
    70  	// must hold the XDSServer.mutex when accessing 'count'
    71  	count uint
    72  
    73  	// mutex is needed when accessing the fields below.
    74  	// XDSServer.mutex is not needed, but if taken it must be taken before 'mutex'
    75  	mutex   lock.RWMutex
    76  	acked   bool
    77  	nacked  bool
    78  	waiters []*completion.Completion
    79  }
    80  
    81  // XDSServer provides a high-lever interface to manage resources published
    82  // using the xDS gRPC API.
    83  type XDSServer struct {
    84  	// socketPath is the path to the gRPC UNIX domain socket.
    85  	socketPath string
    86  
    87  	// listenerProto is a generic Envoy Listener protobuf. Immutable.
    88  	listenerProto *envoy_api_v2.Listener
    89  
    90  	// httpFilterChainProto is a generic Envoy HTTP connection manager filter chain protobuf. Immutable.
    91  	httpFilterChainProto *envoy_api_v2_listener.FilterChain
    92  
    93  	// tcpFilterChainProto is a generic Envoy TCP proxy filter chain protobuf. Immutable.
    94  	tcpFilterChainProto *envoy_api_v2_listener.FilterChain
    95  
    96  	// mutex protects accesses to the configuration resources below.
    97  	mutex lock.RWMutex
    98  
    99  	// listenerMutator publishes listener updates to Envoy proxies.
   100  	// Manages it's own locking
   101  	listenerMutator xds.AckingResourceMutator
   102  
   103  	// listeners is the set of names of listeners that have been added by
   104  	// calling AddListener.
   105  	// mutex must be held when accessing this.
   106  	// Value holds the number of redirects using the listener named by the key.
   107  	listeners map[string]*Listener
   108  
   109  	// networkPolicyCache publishes network policy configuration updates to
   110  	// Envoy proxies.
   111  	networkPolicyCache *xds.Cache
   112  
   113  	// NetworkPolicyMutator wraps networkPolicyCache to publish policy
   114  	// updates to Envoy proxies.
   115  	// Exported for testing only!
   116  	NetworkPolicyMutator xds.AckingResourceMutator
   117  
   118  	// networkPolicyEndpoints maps each network policy's name to the info on
   119  	// the local endpoint.
   120  	// mutex must be held when accessing this.
   121  	networkPolicyEndpoints map[string]logger.EndpointUpdater
   122  
   123  	// stopServer stops the xDS gRPC server.
   124  	stopServer context.CancelFunc
   125  }
   126  
   127  func getXDSPath(stateDir string) string {
   128  	return filepath.Join(stateDir, "xds.sock")
   129  }
   130  
   131  // StartXDSServer configures and starts the xDS GRPC server.
   132  func StartXDSServer(stateDir string) *XDSServer {
   133  	xdsPath := getXDSPath(stateDir)
   134  	accessLogPath := getAccessLogPath(stateDir)
   135  	denied403body := option.Config.HTTP403Message
   136  	requestTimeout := option.Config.HTTPRequestTimeout // seconds
   137  	idleTimeout := option.Config.HTTPIdleTimeout       // seconds
   138  	maxGRPCTimeout := option.Config.HTTPMaxGRPCTimeout // seconds
   139  	numRetries := option.Config.HTTPRetryCount
   140  	retryTimeout := option.Config.HTTPRetryTimeout //seconds
   141  
   142  	os.Remove(xdsPath)
   143  	socketListener, err := net.ListenUnix("unix", &net.UnixAddr{Name: xdsPath, Net: "unix"})
   144  	if err != nil {
   145  		log.WithError(err).Fatalf("Envoy: Failed to open xDS listen socket at %s", xdsPath)
   146  	}
   147  
   148  	// Make the socket accessible by non-root Envoy proxies, e.g. running in
   149  	// sidecar containers.
   150  	if err = os.Chmod(xdsPath, 0777); err != nil {
   151  		log.WithError(err).Fatalf("Envoy: Failed to change mode of xDS listen socket at %s", xdsPath)
   152  	}
   153  
   154  	ldsCache := xds.NewCache()
   155  	ldsMutator := xds.NewAckingResourceMutatorWrapper(ldsCache)
   156  	ldsConfig := &xds.ResourceTypeConfiguration{
   157  		Source:      ldsCache,
   158  		AckObserver: ldsMutator,
   159  	}
   160  
   161  	npdsCache := xds.NewCache()
   162  	npdsMutator := xds.NewAckingResourceMutatorWrapper(npdsCache)
   163  	npdsConfig := &xds.ResourceTypeConfiguration{
   164  		Source:      npdsCache,
   165  		AckObserver: npdsMutator,
   166  	}
   167  
   168  	nphdsConfig := &xds.ResourceTypeConfiguration{
   169  		Source:      NetworkPolicyHostsCache,
   170  		AckObserver: &NetworkPolicyHostsCache,
   171  	}
   172  
   173  	stopServer := startXDSGRPCServer(socketListener, ldsConfig, npdsConfig, nphdsConfig, 5*time.Second)
   174  
   175  	listenerProto := &envoy_api_v2.Listener{
   176  		Address: &envoy_api_v2_core.Address{
   177  			Address: &envoy_api_v2_core.Address_SocketAddress{
   178  				SocketAddress: &envoy_api_v2_core.SocketAddress{
   179  					Protocol:   envoy_api_v2_core.SocketAddress_TCP,
   180  					Address:    "::",
   181  					Ipv4Compat: true,
   182  					// PortSpecifier: &envoy_api_v2_core.SocketAddress_PortValue{0},
   183  				},
   184  			},
   185  		},
   186  		Transparent: &wrappers.BoolValue{Value: true},
   187  		SocketOptions: []*envoy_api_v2_core.SocketOption{{
   188  			Description: "Listener socket mark",
   189  			Level:       syscall.SOL_SOCKET,
   190  			Name:        syscall.SO_MARK,
   191  			Value:       &envoy_api_v2_core.SocketOption_IntValue{IntValue: 0xB00}, // egress
   192  			State:       envoy_api_v2_core.SocketOption_STATE_PREBIND,
   193  		}},
   194  		// FilterChains: []*envoy_api_v2_listener.FilterChain
   195  		ListenerFilters: []*envoy_api_v2_listener.ListenerFilter{{
   196  			Name: "cilium.bpf_metadata",
   197  			ConfigType: &envoy_api_v2_listener.ListenerFilter_Config{
   198  				Config: &structpb.Struct{Fields: map[string]*structpb.Value{
   199  					"is_ingress":                      {Kind: &structpb.Value_BoolValue{BoolValue: false}},
   200  					"may_use_original_source_address": {Kind: &structpb.Value_BoolValue{BoolValue: false}},
   201  					"bpf_root":                        {Kind: &structpb.Value_StringValue{StringValue: bpf.GetMapRoot()}},
   202  				}},
   203  			},
   204  		}},
   205  	}
   206  
   207  	httpFilterChainProto := &envoy_api_v2_listener.FilterChain{
   208  		Filters: []*envoy_api_v2_listener.Filter{{
   209  			Name: "cilium.network",
   210  		}, {
   211  			Name: "envoy.http_connection_manager",
   212  			ConfigType: &envoy_api_v2_listener.Filter_Config{
   213  				Config: &structpb.Struct{Fields: map[string]*structpb.Value{
   214  					"stat_prefix": {Kind: &structpb.Value_StringValue{StringValue: "proxy"}},
   215  					"http_filters": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
   216  						{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   217  							"name": {Kind: &structpb.Value_StringValue{StringValue: "cilium.l7policy"}},
   218  							"config": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   219  								"access_log_path": {Kind: &structpb.Value_StringValue{StringValue: accessLogPath}},
   220  								"denied_403_body": {Kind: &structpb.Value_StringValue{StringValue: denied403body}},
   221  							}}}},
   222  						}}}},
   223  						{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   224  							"name":   {Kind: &structpb.Value_StringValue{StringValue: "envoy.router"}},
   225  							"config": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{}}}},
   226  						}}}},
   227  					}}}},
   228  					"stream_idle_timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{}}}},
   229  					"route_config": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   230  						"virtual_hosts": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
   231  							{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   232  								"name": {Kind: &structpb.Value_StringValue{StringValue: "default_route"}},
   233  								"domains": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
   234  									{Kind: &structpb.Value_StringValue{StringValue: "*"}},
   235  								}}}},
   236  								"routes": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
   237  									{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   238  										"match": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   239  											"prefix": {Kind: &structpb.Value_StringValue{StringValue: "/"}},
   240  											"grpc":   {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{}}}},
   241  										}}}},
   242  										"route": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   243  											// "cluster":          {Kind: &structpb.Value_StringValue{StringValue: "cluster1"}},
   244  											"timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   245  												"seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(requestTimeout)}},
   246  											}}}},
   247  											"max_grpc_timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   248  												"seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(maxGRPCTimeout)}},
   249  											}}}},
   250  											"retry_policy": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   251  												"retry_on":    {Kind: &structpb.Value_StringValue{StringValue: "5xx"}},
   252  												"num_retries": {Kind: &structpb.Value_NumberValue{NumberValue: float64(numRetries)}},
   253  												"per_try_timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   254  													"seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(retryTimeout)}},
   255  												}}}},
   256  											}}}},
   257  										}}}},
   258  									}}}},
   259  									{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   260  										"match": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   261  											"prefix": {Kind: &structpb.Value_StringValue{StringValue: "/"}},
   262  										}}}},
   263  										"route": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   264  											// "cluster":          {Kind: &structpb.Value_StringValue{StringValue: "cluster1"}},
   265  											"timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   266  												"seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(requestTimeout)}},
   267  											}}}},
   268  											// "idle_timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   269  											// "seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(idleTimeout)}},
   270  											// }}}},
   271  											"retry_policy": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   272  												"retry_on":    {Kind: &structpb.Value_StringValue{StringValue: "5xx"}},
   273  												"num_retries": {Kind: &structpb.Value_NumberValue{NumberValue: float64(numRetries)}},
   274  												"per_try_timeout": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   275  													"seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(retryTimeout)}},
   276  												}}}},
   277  											}}}},
   278  										}}}},
   279  									}}}},
   280  								}}}},
   281  							}}}},
   282  						}}}},
   283  					}}}},
   284  				}},
   285  			},
   286  		}},
   287  	}
   288  
   289  	// Idle timeout can only be specified if non-zero
   290  	if idleTimeout > 0 {
   291  		httpFilterChainProto.Filters[1].ConfigType.(*envoy_api_v2_listener.Filter_Config).Config.Fields["route_config"].GetStructValue().Fields["virtual_hosts"].GetListValue().Values[0].GetStructValue().Fields["routes"].GetListValue().Values[1].GetStructValue().Fields["route"].GetStructValue().Fields["idle_timeout"] = &structpb.Value{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{"seconds": {Kind: &structpb.Value_NumberValue{NumberValue: float64(idleTimeout)}}}}}}
   292  	}
   293  
   294  	tcpFilterChainProto := &envoy_api_v2_listener.FilterChain{
   295  		Filters: []*envoy_api_v2_listener.Filter{{
   296  			Name: "cilium.network",
   297  			ConfigType: &envoy_api_v2_listener.Filter_Config{
   298  				Config: &structpb.Struct{Fields: map[string]*structpb.Value{
   299  					"proxylib": {Kind: &structpb.Value_StringValue{StringValue: "libcilium.so"}},
   300  					"proxylib_params": {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
   301  						"access-log-path": {Kind: &structpb.Value_StringValue{StringValue: accessLogPath}},
   302  						"xds-path":        {Kind: &structpb.Value_StringValue{StringValue: xdsPath}},
   303  					}}}},
   304  					// "l7_proto": {Kind: &structpb.Value_StringValue{StringValue: "parsername"}},
   305  					// "policy_name": {Kind: &structpb.Value_StringValue{StringValue: "1.2.3.4"}},
   306  				}},
   307  			},
   308  		}, {
   309  			Name: "envoy.tcp_proxy",
   310  			ConfigType: &envoy_api_v2_listener.Filter_Config{
   311  				Config: &structpb.Struct{Fields: map[string]*structpb.Value{
   312  					"stat_prefix": {Kind: &structpb.Value_StringValue{StringValue: "tcp_proxy"}},
   313  					// "cluster":     {Kind: &structpb.Value_StringValue{StringValue: "cluster1"}},
   314  				}},
   315  			},
   316  		}},
   317  	}
   318  
   319  	return &XDSServer{
   320  		socketPath:             xdsPath,
   321  		listenerProto:          listenerProto,
   322  		httpFilterChainProto:   httpFilterChainProto,
   323  		tcpFilterChainProto:    tcpFilterChainProto,
   324  		listenerMutator:        ldsMutator,
   325  		listeners:              make(map[string]*Listener),
   326  		networkPolicyCache:     npdsCache,
   327  		NetworkPolicyMutator:   npdsMutator,
   328  		networkPolicyEndpoints: make(map[string]logger.EndpointUpdater),
   329  		stopServer:             stopServer,
   330  	}
   331  }
   332  
   333  // AddListener adds a listener to a running Envoy proxy.
   334  func (s *XDSServer) AddListener(name string, kind policy.L7ParserType, port uint16, isIngress bool, mayUseOriginalSourceAddr bool, wg *completion.WaitGroup) {
   335  	log.Debugf("Envoy: %s AddListener %s (mayUseOriginalSourceAddr: %v)", kind, name, mayUseOriginalSourceAddr)
   336  
   337  	s.mutex.Lock()
   338  	listener := s.listeners[name]
   339  	if listener == nil {
   340  		listener = &Listener{}
   341  		s.listeners[name] = listener
   342  	}
   343  	listener.count++
   344  	listener.mutex.Lock() // needed for other than 'count'
   345  	if listener.count > 1 && !listener.nacked {
   346  		log.Debugf("Envoy: Reusing listener: %s", name)
   347  		if !listener.acked {
   348  			// Listener not acked yet, add a completion to the waiter's list
   349  			log.Debugf("Envoy: Waiting for a non-acknowledged reused listener: %s", name)
   350  			listener.waiters = append(listener.waiters, wg.AddCompletion())
   351  		}
   352  		listener.mutex.Unlock()
   353  		s.mutex.Unlock()
   354  		return
   355  	}
   356  	// Try again after a NACK, potentially with a different port number, etc.
   357  	if listener.nacked {
   358  		listener.acked = false
   359  		listener.nacked = false
   360  	}
   361  	listener.mutex.Unlock() // Listener locked again in callbacks below
   362  
   363  	clusterName := egressClusterName
   364  	if isIngress {
   365  		clusterName = ingressClusterName
   366  	}
   367  
   368  	// Fill in the listener-specific parts.
   369  	listenerConf := proto.Clone(s.listenerProto).(*envoy_api_v2.Listener)
   370  	if kind == policy.ParserTypeHTTP {
   371  		listenerConf.FilterChains = append(listenerConf.FilterChains, proto.Clone(s.httpFilterChainProto).(*envoy_api_v2_listener.FilterChain))
   372  		// listenerConf.FilterChains[0].Filters[1].ConfigType.(*envoy_api_v2_listener.Filter_Config).Config.Fields["http_filters"].GetListValue().Values[0].GetStructValue().Fields["config"].GetStructValue().Fields["policy_name"] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: endpointPolicyName}}
   373  		routes := listenerConf.FilterChains[0].Filters[1].ConfigType.(*envoy_api_v2_listener.Filter_Config).Config.Fields["route_config"].GetStructValue().Fields["virtual_hosts"].GetListValue().Values[0].GetStructValue().Fields["routes"].GetListValue().Values
   374  		routes[0].GetStructValue().Fields["route"].GetStructValue().Fields["cluster"] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: clusterName}}
   375  		routes[1].GetStructValue().Fields["route"].GetStructValue().Fields["cluster"] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: clusterName}}
   376  	} else {
   377  		listenerConf.FilterChains = append(listenerConf.FilterChains, proto.Clone(s.tcpFilterChainProto).(*envoy_api_v2_listener.FilterChain))
   378  		// listenerConf.FilterChains[0].Filters[0].ConfigType.(*envoy_api_v2_listener.Filter_Config).Config.Fields["policy_name"] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: endpointPolicyName}}
   379  		// listenerConf.FilterChains[0].Filters[0].ConfigType.(*envoy_api_v2_listener.Filter_Config).Config.Fields["l7_proto"] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: kind.String()}}
   380  		listenerConf.FilterChains[0].Filters[1].ConfigType.(*envoy_api_v2_listener.Filter_Config).Config.Fields["cluster"] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: clusterName}}
   381  	}
   382  
   383  	listenerConf.Name = name
   384  	listenerConf.Address.GetSocketAddress().PortSpecifier = &envoy_api_v2_core.SocketAddress_PortValue{PortValue: uint32(port)}
   385  	if isIngress {
   386  		listenerConf.SocketOptions[0].Value.(*envoy_api_v2_core.SocketOption_IntValue).IntValue = 0xA00 // Ingress socket mark
   387  		listenerConf.ListenerFilters[0].ConfigType.(*envoy_api_v2_listener.ListenerFilter_Config).Config.Fields["is_ingress"].GetKind().(*structpb.Value_BoolValue).BoolValue = true
   388  	}
   389  	if mayUseOriginalSourceAddr {
   390  		listenerConf.ListenerFilters[0].ConfigType.(*envoy_api_v2_listener.ListenerFilter_Config).Config.Fields["may_use_original_source_address"].GetKind().(*structpb.Value_BoolValue).BoolValue = true
   391  	}
   392  
   393  	s.listenerMutator.Upsert(ListenerTypeURL, name, listenerConf, []string{"127.0.0.1"}, wg,
   394  		func(err error) {
   395  			// listener might have already been removed, so we can't look again
   396  			// but we still need to complete all the completions in case
   397  			// someone is still waiting!
   398  			listener.mutex.Lock()
   399  			if err == nil {
   400  				// Allow future users to not need to wait
   401  				listener.acked = true
   402  			} else {
   403  				// Prevent further reuse of a failed listener
   404  				listener.nacked = true
   405  			}
   406  			// Pass the completion result to all the additional waiters.
   407  			for _, waiter := range listener.waiters {
   408  				waiter.Complete(err)
   409  			}
   410  			listener.waiters = nil
   411  			listener.mutex.Unlock()
   412  		})
   413  	s.mutex.Unlock()
   414  }
   415  
   416  // RemoveListener removes an existing Envoy Listener.
   417  func (s *XDSServer) RemoveListener(name string, wg *completion.WaitGroup) xds.AckingResourceMutatorRevertFunc {
   418  	log.Debugf("Envoy: removeListener %s", name)
   419  
   420  	var listenerRevertFunc func(*completion.Completion)
   421  
   422  	s.mutex.Lock()
   423  	listener, ok := s.listeners[name]
   424  	if ok && listener != nil {
   425  		listener.count--
   426  		if listener.count == 0 {
   427  			delete(s.listeners, name)
   428  			listenerRevertFunc = s.listenerMutator.Delete(ListenerTypeURL, name, []string{"127.0.0.1"}, wg, nil)
   429  		}
   430  	} else {
   431  		// Bail out if this listener does not exist
   432  		log.Fatalf("Envoy: Attempt to remove non-existent listener: %s", name)
   433  	}
   434  	s.mutex.Unlock()
   435  
   436  	return func(completion *completion.Completion) {
   437  		s.mutex.Lock()
   438  		if listenerRevertFunc != nil {
   439  			listenerRevertFunc(completion)
   440  		}
   441  		listener.count++
   442  		s.listeners[name] = listener
   443  		s.mutex.Unlock()
   444  	}
   445  }
   446  
   447  func (s *XDSServer) stop() {
   448  	s.stopServer()
   449  	os.Remove(s.socketPath)
   450  }
   451  
   452  func getL7Rule(l7 *api.PortRuleL7) *cilium.L7NetworkPolicyRule {
   453  	rule := &cilium.L7NetworkPolicyRule{Rule: make(map[string]string, len(*l7))}
   454  
   455  	for k, v := range *l7 {
   456  		rule.Rule[k] = v
   457  	}
   458  
   459  	return rule // No ruleRef
   460  }
   461  
   462  func getHTTPRule(h *api.PortRuleHTTP) (headers []*envoy_api_v2_route.HeaderMatcher, ruleRef string) {
   463  	// Count the number of header matches we need
   464  	cnt := len(h.Headers)
   465  	if h.Path != "" {
   466  		cnt++
   467  	}
   468  	if h.Method != "" {
   469  		cnt++
   470  	}
   471  	if h.Host != "" {
   472  		cnt++
   473  	}
   474  
   475  	headers = make([]*envoy_api_v2_route.HeaderMatcher, 0, cnt)
   476  	if h.Path != "" {
   477  		headers = append(headers, &envoy_api_v2_route.HeaderMatcher{Name: ":path",
   478  			HeaderMatchSpecifier: &envoy_api_v2_route.HeaderMatcher_RegexMatch{RegexMatch: h.Path}})
   479  		ruleRef = `PathRegexp("` + h.Path + `")`
   480  	}
   481  	if h.Method != "" {
   482  		headers = append(headers, &envoy_api_v2_route.HeaderMatcher{Name: ":method",
   483  			HeaderMatchSpecifier: &envoy_api_v2_route.HeaderMatcher_RegexMatch{RegexMatch: h.Method}})
   484  		if ruleRef != "" {
   485  			ruleRef += " && "
   486  		}
   487  		ruleRef += `MethodRegexp("` + h.Method + `")`
   488  	}
   489  
   490  	if h.Host != "" {
   491  		headers = append(headers, &envoy_api_v2_route.HeaderMatcher{Name: ":authority",
   492  			HeaderMatchSpecifier: &envoy_api_v2_route.HeaderMatcher_RegexMatch{RegexMatch: h.Host}})
   493  		if ruleRef != "" {
   494  			ruleRef += " && "
   495  		}
   496  		ruleRef += `HostRegexp("` + h.Host + `")`
   497  	}
   498  	for _, hdr := range h.Headers {
   499  		strs := strings.SplitN(hdr, " ", 2)
   500  		if ruleRef != "" {
   501  			ruleRef += " && "
   502  		}
   503  		ruleRef += `Header("`
   504  		if len(strs) == 2 {
   505  			// Remove ':' in "X-Key: true"
   506  			key := strings.TrimRight(strs[0], ":")
   507  			// Header presence and matching (literal) value needed.
   508  			headers = append(headers, &envoy_api_v2_route.HeaderMatcher{Name: key,
   509  				HeaderMatchSpecifier: &envoy_api_v2_route.HeaderMatcher_ExactMatch{ExactMatch: strs[1]}})
   510  			ruleRef += key + `","` + strs[1]
   511  		} else {
   512  			// Only header presence needed
   513  			headers = append(headers, &envoy_api_v2_route.HeaderMatcher{Name: strs[0],
   514  				HeaderMatchSpecifier: &envoy_api_v2_route.HeaderMatcher_PresentMatch{PresentMatch: true}})
   515  			ruleRef += strs[0]
   516  		}
   517  		ruleRef += `")`
   518  	}
   519  	if len(headers) == 0 {
   520  		headers = nil
   521  	} else {
   522  		SortHeaderMatchers(headers)
   523  	}
   524  	return
   525  }
   526  
   527  func createBootstrap(filePath string, name, cluster, version string, xdsSock, egressClusterName, ingressClusterName string, adminPath string) {
   528  	connectTimeout := int64(option.Config.ProxyConnectTimeout) // in seconds
   529  
   530  	bs := &envoy_config_bootstrap_v2.Bootstrap{
   531  		Node: &envoy_api_v2_core.Node{Id: name, Cluster: cluster, Metadata: nil, Locality: nil, BuildVersion: version},
   532  		StaticResources: &envoy_config_bootstrap_v2.Bootstrap_StaticResources{
   533  			Clusters: []*envoy_api_v2.Cluster{
   534  				{
   535  					Name:                 egressClusterName,
   536  					ClusterDiscoveryType: &envoy_api_v2.Cluster_Type{Type: envoy_api_v2.Cluster_ORIGINAL_DST},
   537  					ConnectTimeout:       &duration.Duration{Seconds: connectTimeout, Nanos: 0},
   538  					CleanupInterval:      &duration.Duration{Seconds: connectTimeout, Nanos: 500000000},
   539  					LbPolicy:             envoy_api_v2.Cluster_ORIGINAL_DST_LB,
   540  					ProtocolSelection:    envoy_api_v2.Cluster_USE_DOWNSTREAM_PROTOCOL,
   541  				},
   542  				{
   543  					Name:                 ingressClusterName,
   544  					ClusterDiscoveryType: &envoy_api_v2.Cluster_Type{Type: envoy_api_v2.Cluster_ORIGINAL_DST},
   545  					ConnectTimeout:       &duration.Duration{Seconds: connectTimeout, Nanos: 0},
   546  					CleanupInterval:      &duration.Duration{Seconds: connectTimeout, Nanos: 500000000},
   547  					LbPolicy:             envoy_api_v2.Cluster_ORIGINAL_DST_LB,
   548  					ProtocolSelection:    envoy_api_v2.Cluster_USE_DOWNSTREAM_PROTOCOL,
   549  				},
   550  				{
   551  					Name:                 "xds-grpc-cilium",
   552  					ClusterDiscoveryType: &envoy_api_v2.Cluster_Type{Type: envoy_api_v2.Cluster_STATIC},
   553  					ConnectTimeout:       &duration.Duration{Seconds: connectTimeout, Nanos: 0},
   554  					LbPolicy:             envoy_api_v2.Cluster_ROUND_ROBIN,
   555  					LoadAssignment: &envoy_api_v2.ClusterLoadAssignment{
   556  						ClusterName: "xds-grpc-cilium",
   557  						Endpoints: []*envoy_api_v2_endpoint.LocalityLbEndpoints{{
   558  							LbEndpoints: []*envoy_api_v2_endpoint.LbEndpoint{{
   559  								HostIdentifier: &envoy_api_v2_endpoint.LbEndpoint_Endpoint{
   560  									Endpoint: &envoy_api_v2_endpoint.Endpoint{
   561  										Address: &envoy_api_v2_core.Address{
   562  											Address: &envoy_api_v2_core.Address_Pipe{
   563  												Pipe: &envoy_api_v2_core.Pipe{Path: xdsSock}},
   564  										},
   565  									},
   566  								},
   567  							}},
   568  						}},
   569  					},
   570  					Http2ProtocolOptions: &envoy_api_v2_core.Http2ProtocolOptions{},
   571  				},
   572  			},
   573  		},
   574  		DynamicResources: &envoy_config_bootstrap_v2.Bootstrap_DynamicResources{
   575  			LdsConfig: &envoy_api_v2_core.ConfigSource{
   576  				ConfigSourceSpecifier: &envoy_api_v2_core.ConfigSource_ApiConfigSource{
   577  					ApiConfigSource: &envoy_api_v2_core.ApiConfigSource{
   578  						ApiType: envoy_api_v2_core.ApiConfigSource_GRPC,
   579  						GrpcServices: []*envoy_api_v2_core.GrpcService{
   580  							{
   581  								TargetSpecifier: &envoy_api_v2_core.GrpcService_EnvoyGrpc_{
   582  									EnvoyGrpc: &envoy_api_v2_core.GrpcService_EnvoyGrpc{
   583  										ClusterName: "xds-grpc-cilium",
   584  									},
   585  								},
   586  							},
   587  						},
   588  					},
   589  				},
   590  			},
   591  		},
   592  		Admin: &envoy_config_bootstrap_v2.Admin{
   593  			AccessLogPath: "/dev/null",
   594  			Address: &envoy_api_v2_core.Address{
   595  				Address: &envoy_api_v2_core.Address_Pipe{
   596  					Pipe: &envoy_api_v2_core.Pipe{Path: adminPath},
   597  				},
   598  			},
   599  		},
   600  	}
   601  
   602  	log.Debugf("Envoy: Bootstrap: %s", bs)
   603  	data, err := proto.Marshal(bs)
   604  	if err != nil {
   605  		log.WithError(err).Fatal("Envoy: Error marshaling Envoy bootstrap")
   606  	}
   607  	err = ioutil.WriteFile(filePath, data, 0644)
   608  	if err != nil {
   609  		log.WithError(err).Fatal("Envoy: Error writing Envoy bootstrap file")
   610  	}
   611  }
   612  
   613  func getPortNetworkPolicyRule(sel policy.CachedSelector, l7Parser policy.L7ParserType, l7Rules api.L7Rules) *cilium.PortNetworkPolicyRule {
   614  	// Optimize the policy if the endpoint selector is a wildcard by
   615  	// keeping remote policies list empty to match all remote policies.
   616  	var remotePolicies []uint64
   617  	if !sel.IsWildcard() {
   618  		for _, id := range sel.GetSelections() {
   619  			remotePolicies = append(remotePolicies, uint64(id))
   620  		}
   621  
   622  		// No remote policies would match this rule. Discard it.
   623  		if len(remotePolicies) == 0 {
   624  			return nil
   625  		}
   626  
   627  		sort.Slice(remotePolicies, func(i, j int) bool {
   628  			return remotePolicies[i] < remotePolicies[j]
   629  		})
   630  	}
   631  
   632  	r := &cilium.PortNetworkPolicyRule{
   633  		RemotePolicies: remotePolicies,
   634  	}
   635  
   636  	switch l7Parser {
   637  	case policy.ParserTypeHTTP:
   638  		if len(l7Rules.HTTP) > 0 { // Just cautious. This should never be false.
   639  			httpRules := make([]*cilium.HttpNetworkPolicyRule, 0, len(l7Rules.HTTP))
   640  			for _, l7 := range l7Rules.HTTP {
   641  				headers, _ := getHTTPRule(&l7)
   642  				httpRules = append(httpRules, &cilium.HttpNetworkPolicyRule{Headers: headers})
   643  			}
   644  			SortHTTPNetworkPolicyRules(httpRules)
   645  			r.L7 = &cilium.PortNetworkPolicyRule_HttpRules{
   646  				HttpRules: &cilium.HttpNetworkPolicyRules{
   647  					HttpRules: httpRules,
   648  				},
   649  			}
   650  		}
   651  	case policy.ParserTypeKafka:
   652  		// TODO: Support Kafka. For now, just ignore any Kafka L7 rule.
   653  
   654  	case policy.ParserTypeDNS:
   655  		// TODO: Support DNS. For now, just ignore any DNS L7 rule.
   656  
   657  	default:
   658  		// Assume unknown parser types use a Key-Value Pair policy
   659  		if len(l7Rules.L7) > 0 {
   660  			kvpRules := make([]*cilium.L7NetworkPolicyRule, 0, len(l7Rules.L7))
   661  			for _, l7 := range l7Rules.L7 {
   662  				kvpRules = append(kvpRules, getL7Rule(&l7))
   663  			}
   664  			// L7 rules are not sorted
   665  			r.L7Proto = l7Parser.String()
   666  			r.L7 = &cilium.PortNetworkPolicyRule_L7Rules{
   667  				L7Rules: &cilium.L7NetworkPolicyRules{
   668  					L7Rules: kvpRules,
   669  				},
   670  			}
   671  		}
   672  	}
   673  
   674  	return r
   675  }
   676  
   677  func getDirectionNetworkPolicy(l4Policy policy.L4PolicyMap, policyEnforced bool) []*cilium.PortNetworkPolicy {
   678  	if !policyEnforced {
   679  		// Return an allow-all policy.
   680  		return allowAllPortNetworkPolicy
   681  	}
   682  
   683  	if len(l4Policy) == 0 {
   684  		return nil
   685  	}
   686  
   687  	PerPortPolicies := make([]*cilium.PortNetworkPolicy, 0, len(l4Policy))
   688  
   689  	for _, l4 := range l4Policy {
   690  		var protocol envoy_api_v2_core.SocketAddress_Protocol
   691  		switch l4.Protocol {
   692  		case api.ProtoTCP:
   693  			protocol = envoy_api_v2_core.SocketAddress_TCP
   694  		case api.ProtoUDP:
   695  			protocol = envoy_api_v2_core.SocketAddress_UDP
   696  		}
   697  
   698  		pnp := &cilium.PortNetworkPolicy{
   699  			Port:     uint32(l4.Port),
   700  			Protocol: protocol,
   701  			Rules:    make([]*cilium.PortNetworkPolicyRule, 0, len(l4.L7RulesPerEp)),
   702  		}
   703  
   704  		allowAll := false
   705  		for sel, l7 := range l4.L7RulesPerEp {
   706  			rule := getPortNetworkPolicyRule(sel, l4.L7Parser, l7)
   707  			if rule != nil {
   708  				if len(rule.RemotePolicies) == 0 && rule.L7 == nil {
   709  					// Got an allow-all rule, which would short-circuit all of
   710  					// the other rules. Just set no rules, which has the same
   711  					// effect of allowing all.
   712  					allowAll = true
   713  					pnp.Rules = nil
   714  					break
   715  				}
   716  
   717  				pnp.Rules = append(pnp.Rules, rule)
   718  			}
   719  		}
   720  
   721  		// No rule for this port matches any remote identity.
   722  		// This means that no traffic was explicitly allowed for this port.
   723  		// In this case, just don't generate any PortNetworkPolicy for this
   724  		// port.
   725  		if !allowAll && len(pnp.Rules) == 0 {
   726  			continue
   727  		}
   728  
   729  		SortPortNetworkPolicyRules(pnp.Rules)
   730  
   731  		PerPortPolicies = append(PerPortPolicies, pnp)
   732  	}
   733  
   734  	if len(PerPortPolicies) == 0 {
   735  		return nil
   736  	}
   737  
   738  	SortPortNetworkPolicies(PerPortPolicies)
   739  
   740  	return PerPortPolicies
   741  }
   742  
   743  // getNetworkPolicy converts a network policy into a cilium.NetworkPolicy.
   744  func getNetworkPolicy(name string, id identity.NumericIdentity, conntrackName string, policy *policy.L4Policy,
   745  	ingressPolicyEnforced, egressPolicyEnforced bool) *cilium.NetworkPolicy {
   746  	p := &cilium.NetworkPolicy{
   747  		Name:             name,
   748  		Policy:           uint64(id),
   749  		ConntrackMapName: conntrackName,
   750  	}
   751  
   752  	// If no policy, deny all traffic. Otherwise, convert the policies for ingress and egress.
   753  	if policy != nil {
   754  		p.IngressPerPortPolicies = getDirectionNetworkPolicy(policy.Ingress, ingressPolicyEnforced)
   755  		p.EgressPerPortPolicies = getDirectionNetworkPolicy(policy.Egress, egressPolicyEnforced)
   756  	}
   757  
   758  	return p
   759  }
   760  
   761  // return the Envoy proxy node IDs that need to ACK the policy.
   762  func getNodeIDs(ep logger.EndpointUpdater, policy *policy.L4Policy) []string {
   763  	nodeIDs := make([]string, 0, 1)
   764  	if ep.HasSidecarProxy() {
   765  		// Istio sidecars have the Cilium bpf metadata filter
   766  		// statically configured running the NPDS client, so
   767  		// we may unconditionally wait for ACKs from the
   768  		// sidecars.
   769  		// Sidecar's IPv4 address is used as the node ID.
   770  		ipv4 := ep.GetIPv4Address()
   771  		if ipv4 == "" {
   772  			log.Error("Envoy: Sidecar proxy has no IPv4 address")
   773  		} else {
   774  			nodeIDs = append(nodeIDs, ipv4)
   775  		}
   776  	} else {
   777  		// Host proxy uses "127.0.0.1" as the nodeID
   778  		nodeIDs = append(nodeIDs, "127.0.0.1")
   779  	}
   780  	// Require additional ACK from proxylib if policy has proxylib redirects
   781  	// Note that if a previous policy had a proxylib redirect and this one does not,
   782  	// we only wait for the ACK from the main Envoy node ID.
   783  	if policy.HasProxylibRedirect() {
   784  		// Proxylib uses "127.0.0.2" as the nodeID
   785  		nodeIDs = append(nodeIDs, "127.0.0.2")
   786  	}
   787  	return nodeIDs
   788  }
   789  
   790  // UpdateNetworkPolicy adds or updates a network policy in the set published
   791  // to L7 proxies.
   792  // When the proxy acknowledges the network policy update, it will result in
   793  // a subsequent call to the endpoint's OnProxyPolicyUpdate() function.
   794  func (s *XDSServer) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy,
   795  	ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
   796  
   797  	s.mutex.Lock()
   798  	defer s.mutex.Unlock()
   799  
   800  	// First, validate all policies
   801  	ips := []string{
   802  		ep.GetIPv6Address(),
   803  		ep.GetIPv4Address(),
   804  	}
   805  	var policies []*cilium.NetworkPolicy
   806  	for _, ip := range ips {
   807  		if ip == "" {
   808  			continue
   809  		}
   810  		networkPolicy := getNetworkPolicy(ip, ep.GetIdentity(), ep.ConntrackName(), policy,
   811  			ingressPolicyEnforced, egressPolicyEnforced)
   812  		err := networkPolicy.Validate()
   813  		if err != nil {
   814  			return fmt.Errorf("error validating generated NetworkPolicy for %s: %s", ip, err), nil
   815  		}
   816  		policies = append(policies, networkPolicy)
   817  	}
   818  
   819  	nodeIDs := getNodeIDs(ep, policy)
   820  
   821  	// If there are no listeners configured, the local node's Envoy proxy won't
   822  	// query for network policies and therefore will never ACK them, and we'd
   823  	// wait forever.
   824  	if !ep.HasSidecarProxy() {
   825  		if len(s.listeners) == 0 {
   826  			wg = nil
   827  		}
   828  	}
   829  
   830  	// When successful, push them into the cache.
   831  	revertFuncs := make([]xds.AckingResourceMutatorRevertFunc, 0, len(policies))
   832  	revertUpdatedNetworkPolicyEndpoints := make(map[string]logger.EndpointUpdater, len(policies))
   833  	for _, p := range policies {
   834  		var callback func(error)
   835  		if policy != nil {
   836  			policyRevision := policy.Revision
   837  			callback = func(err error) {
   838  				if err == nil {
   839  					go ep.OnProxyPolicyUpdate(policyRevision)
   840  				}
   841  			}
   842  		}
   843  		revertFuncs = append(revertFuncs, s.NetworkPolicyMutator.Upsert(NetworkPolicyTypeURL, p.Name, p, nodeIDs, wg, callback))
   844  		revertUpdatedNetworkPolicyEndpoints[p.Name] = s.networkPolicyEndpoints[p.Name]
   845  		s.networkPolicyEndpoints[p.Name] = ep
   846  	}
   847  
   848  	return nil, func() error {
   849  		log.Debug("Reverting xDS network policy update")
   850  
   851  		s.mutex.Lock()
   852  		defer s.mutex.Unlock()
   853  
   854  		for name, ep := range revertUpdatedNetworkPolicyEndpoints {
   855  			if ep == nil {
   856  				delete(s.networkPolicyEndpoints, name)
   857  			} else {
   858  				s.networkPolicyEndpoints[name] = ep
   859  			}
   860  		}
   861  
   862  		// Don't wait for an ACK for the reverted xDS updates.
   863  		// This is best-effort.
   864  		for _, revertFunc := range revertFuncs {
   865  			revertFunc(completion.NewCompletion(nil, nil))
   866  		}
   867  
   868  		log.Debug("Finished reverting xDS network policy update")
   869  
   870  		return nil
   871  	}
   872  }
   873  
   874  // UseCurrentNetworkPolicy inserts a Completion to the WaitGroup if the current network policy has not yet been acked.
   875  // 'wg' may not be nil.
   876  func (s *XDSServer) UseCurrentNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, wg *completion.WaitGroup) {
   877  	s.mutex.Lock()
   878  	defer s.mutex.Unlock()
   879  
   880  	// If there are no listeners configured, the local node's Envoy proxy won't
   881  	// query for network policies and therefore will never ACK them, and we'd
   882  	// wait forever.
   883  	if !ep.HasSidecarProxy() && len(s.listeners) == 0 {
   884  		return
   885  	}
   886  
   887  	nodeIDs := getNodeIDs(ep, policy)
   888  	s.NetworkPolicyMutator.UseCurrent(NetworkPolicyTypeURL, nodeIDs, wg)
   889  }
   890  
   891  // RemoveNetworkPolicy removes network policies relevant to the specified
   892  // endpoint from the set published to L7 proxies, and stops listening for
   893  // acks for policies on this endpoint.
   894  func (s *XDSServer) RemoveNetworkPolicy(ep logger.EndpointInfoSource) {
   895  	s.mutex.Lock()
   896  	defer s.mutex.Unlock()
   897  
   898  	name := ep.GetIPv6Address()
   899  	if name != "" {
   900  		s.networkPolicyCache.Delete(NetworkPolicyTypeURL, name)
   901  		delete(s.networkPolicyEndpoints, name)
   902  	}
   903  	name = ep.GetIPv4Address()
   904  	if name != "" {
   905  		s.networkPolicyCache.Delete(NetworkPolicyTypeURL, name)
   906  		delete(s.networkPolicyEndpoints, name)
   907  		// Delete node resources held in the cache for the endpoint (e.g., sidecar)
   908  		s.NetworkPolicyMutator.DeleteNode(name)
   909  	}
   910  }
   911  
   912  // RemoveAllNetworkPolicies removes all network policies from the set published
   913  // to L7 proxies.
   914  func (s *XDSServer) RemoveAllNetworkPolicies() {
   915  	s.networkPolicyCache.Clear(NetworkPolicyTypeURL)
   916  }
   917  
   918  // GetNetworkPolicies returns the current version of the network policies with
   919  // the given names.
   920  // If resourceNames is empty, all resources are returned.
   921  func (s *XDSServer) GetNetworkPolicies(resourceNames []string) (map[string]*cilium.NetworkPolicy, error) {
   922  	resources, err := s.networkPolicyCache.GetResources(context.Background(), NetworkPolicyTypeURL, 0, "", resourceNames)
   923  	if err != nil {
   924  		return nil, err
   925  	}
   926  	networkPolicies := make(map[string]*cilium.NetworkPolicy, len(resources.Resources))
   927  	for _, res := range resources.Resources {
   928  		networkPolicy := res.(*cilium.NetworkPolicy)
   929  		networkPolicies[networkPolicy.Name] = networkPolicy
   930  	}
   931  	return networkPolicies, nil
   932  }
   933  
   934  // getLocalEndpoint returns the endpoint info for the local endpoint on which
   935  // the network policy of the given name if enforced, or nil if not found.
   936  func (s *XDSServer) getLocalEndpoint(networkPolicyName string) logger.EndpointUpdater {
   937  	s.mutex.RLock()
   938  	defer s.mutex.RUnlock()
   939  
   940  	return s.networkPolicyEndpoints[networkPolicyName]
   941  }