google.golang.org/grpc@v1.72.2/xds/internal/server/listener_wrapper.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package server contains internal server-side functionality used by the public
    20  // facing xds package.
    21  package server
    22  
    23  import (
    24  	"fmt"
    25  	"net"
    26  	"sync"
    27  	"time"
    28  
    29  	"google.golang.org/grpc/backoff"
    30  	"google.golang.org/grpc/connectivity"
    31  	"google.golang.org/grpc/grpclog"
    32  	internalbackoff "google.golang.org/grpc/internal/backoff"
    33  	internalgrpclog "google.golang.org/grpc/internal/grpclog"
    34  	"google.golang.org/grpc/internal/grpcsync"
    35  	"google.golang.org/grpc/internal/xds/bootstrap"
    36  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    37  )
    38  
    39  var (
    40  	logger = grpclog.Component("xds")
    41  
    42  	// Backoff strategy for temporary errors received from Accept(). If this
    43  	// needs to be configurable, we can inject it through ListenerWrapperParams.
    44  	bs = internalbackoff.Exponential{Config: backoff.Config{
    45  		BaseDelay:  5 * time.Millisecond,
    46  		Multiplier: 2.0,
    47  		MaxDelay:   1 * time.Second,
    48  	}}
    49  	backoffFunc = bs.Backoff
    50  )
    51  
    52  // ServingModeCallback is the callback that users can register to get notified
    53  // about the server's serving mode changes. The callback is invoked with the
    54  // address of the listener and its new mode. The err parameter is set to a
    55  // non-nil error if the server has transitioned into not-serving mode.
    56  type ServingModeCallback func(addr net.Addr, mode connectivity.ServingMode, err error)
    57  
    58  // XDSClient wraps the methods on the XDSClient which are required by
    59  // the listenerWrapper.
    60  type XDSClient interface {
    61  	WatchResource(rType xdsresource.Type, resourceName string, watcher xdsresource.ResourceWatcher) (cancel func())
    62  	BootstrapConfig() *bootstrap.Config
    63  }
    64  
    65  // ListenerWrapperParams wraps parameters required to create a listenerWrapper.
    66  type ListenerWrapperParams struct {
    67  	// Listener is the net.Listener passed by the user that is to be wrapped.
    68  	Listener net.Listener
    69  	// ListenerResourceName is the xDS Listener resource to request.
    70  	ListenerResourceName string
    71  	// XDSClient provides the functionality from the XDSClient required here.
    72  	XDSClient XDSClient
    73  	// ModeCallback is the callback to invoke when the serving mode changes.
    74  	ModeCallback ServingModeCallback
    75  }
    76  
    77  // NewListenerWrapper creates a new listenerWrapper with params. It returns a
    78  // net.Listener. It starts in state not serving, which triggers Accept() +
    79  // Close() on any incoming connections.
    80  //
    81  // Only TCP listeners are supported.
    82  func NewListenerWrapper(params ListenerWrapperParams) net.Listener {
    83  	lw := &listenerWrapper{
    84  		Listener:          params.Listener,
    85  		name:              params.ListenerResourceName,
    86  		xdsC:              params.XDSClient,
    87  		xdsNodeID:         params.XDSClient.BootstrapConfig().Node().GetId(),
    88  		modeCallback:      params.ModeCallback,
    89  		isUnspecifiedAddr: params.Listener.Addr().(*net.TCPAddr).IP.IsUnspecified(),
    90  		conns:             make(map[*connWrapper]bool),
    91  
    92  		mode:   connectivity.ServingModeNotServing,
    93  		closed: grpcsync.NewEvent(),
    94  	}
    95  	lw.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[xds-server-listener %p] ", lw))
    96  
    97  	// Serve() verifies that Addr() returns a valid TCPAddr. So, it is safe to
    98  	// ignore the error from SplitHostPort().
    99  	lisAddr := lw.Listener.Addr().String()
   100  	lw.addr, lw.port, _ = net.SplitHostPort(lisAddr)
   101  
   102  	lw.rdsHandler = newRDSHandler(lw.handleRDSUpdate, lw.xdsC, lw.logger)
   103  	lw.cancelWatch = xdsresource.WatchListener(lw.xdsC, lw.name, &ldsWatcher{
   104  		parent: lw,
   105  		logger: lw.logger,
   106  		name:   lw.name,
   107  	})
   108  	return lw
   109  }
   110  
   111  // listenerWrapper wraps the net.Listener associated with the listening address
   112  // passed to Serve(). It also contains all other state associated with this
   113  // particular invocation of Serve().
   114  type listenerWrapper struct {
   115  	net.Listener
   116  	logger *internalgrpclog.PrefixLogger
   117  
   118  	name         string
   119  	xdsC         XDSClient
   120  	xdsNodeID    string
   121  	cancelWatch  func()
   122  	modeCallback ServingModeCallback
   123  
   124  	// Set to true if the listener is bound to the IP_ANY address (which is
   125  	// "0.0.0.0" for IPv4 and "::" for IPv6).
   126  	isUnspecifiedAddr bool
   127  	// Listening address and port. Used to validate the socket address in the
   128  	// Listener resource received from the control plane.
   129  	addr, port string
   130  
   131  	// A small race exists in the XDSClient code between the receipt of an xDS
   132  	// response and the user cancelling the associated watch. In this window,
   133  	// the registered callback may be invoked after the watch is canceled, and
   134  	// the user is expected to work around this. This event signifies that the
   135  	// listener is closed (and hence the watch is cancelled), and we drop any
   136  	// updates received in the callback if this event has fired.
   137  	closed *grpcsync.Event
   138  
   139  	// mu guards access to the current serving mode and the active filter chain
   140  	// manager.
   141  	mu sync.Mutex
   142  	// Current serving mode.
   143  	mode connectivity.ServingMode
   144  	// Filter chain manager currently serving.
   145  	activeFilterChainManager *xdsresource.FilterChainManager
   146  	// conns accepted with configuration from activeFilterChainManager.
   147  	conns map[*connWrapper]bool
   148  
   149  	// These fields are read/written to in the context of xDS updates, which are
   150  	// guaranteed to be emitted synchronously from the xDS Client. Thus, they do
   151  	// not need further synchronization.
   152  
   153  	// Pending filter chain manager. Will go active once rdsHandler has received
   154  	// all the RDS resources this filter chain manager needs.
   155  	pendingFilterChainManager *xdsresource.FilterChainManager
   156  
   157  	// rdsHandler is used for any dynamic RDS resources specified in a LDS
   158  	// update.
   159  	rdsHandler *rdsHandler
   160  }
   161  
   162  func (l *listenerWrapper) handleLDSUpdate(update xdsresource.ListenerUpdate) {
   163  	ilc := update.InboundListenerCfg
   164  	// Make sure that the socket address on the received Listener resource
   165  	// matches the address of the net.Listener passed to us by the user. This
   166  	// check is done here instead of at the XDSClient layer because of the
   167  	// following couple of reasons:
   168  	// - XDSClient cannot know the listening address of every listener in the
   169  	//   system, and hence cannot perform this check.
   170  	// - this is a very context-dependent check and only the server has the
   171  	//   appropriate context to perform this check.
   172  	//
   173  	// What this means is that the XDSClient has ACKed a resource which can push
   174  	// the server into a "not serving" mode. This is not ideal, but this is
   175  	// what we have decided to do.
   176  	if ilc.Address != l.addr || ilc.Port != l.port {
   177  		l.mu.Lock()
   178  		err := l.annotateErrorWithNodeID(fmt.Errorf("address (%s:%s) in Listener update does not match listening address: (%s:%s)", ilc.Address, ilc.Port, l.addr, l.port))
   179  		l.switchModeLocked(connectivity.ServingModeNotServing, err)
   180  		l.mu.Unlock()
   181  		return
   182  	}
   183  
   184  	l.pendingFilterChainManager = ilc.FilterChains
   185  	l.rdsHandler.updateRouteNamesToWatch(ilc.FilterChains.RouteConfigNames)
   186  
   187  	if l.rdsHandler.determineRouteConfigurationReady() {
   188  		l.maybeUpdateFilterChains()
   189  	}
   190  }
   191  
   192  // maybeUpdateFilterChains swaps in the pending filter chain manager to the
   193  // active one if the pending filter chain manager is present. If a swap occurs,
   194  // it also drains (gracefully stops) any connections that were accepted on the
   195  // old active filter chain manager, and puts this listener in state SERVING.
   196  // Must be called within an xDS Client Callback.
   197  func (l *listenerWrapper) maybeUpdateFilterChains() {
   198  	if l.pendingFilterChainManager == nil {
   199  		// Nothing to update, return early.
   200  		return
   201  	}
   202  
   203  	l.mu.Lock()
   204  	l.switchModeLocked(connectivity.ServingModeServing, nil)
   205  	// "Updates to a Listener cause all older connections on that Listener to be
   206  	// gracefully shut down with a grace period of 10 minutes for long-lived
   207  	// RPC's, such that clients will reconnect and have the updated
   208  	// configuration apply." - A36
   209  	connsToClose := l.conns
   210  	l.conns = make(map[*connWrapper]bool)
   211  	l.activeFilterChainManager = l.pendingFilterChainManager
   212  	l.pendingFilterChainManager = nil
   213  	l.instantiateFilterChainRoutingConfigurationsLocked()
   214  	l.mu.Unlock()
   215  	go func() {
   216  		for conn := range connsToClose {
   217  			conn.Drain()
   218  		}
   219  	}()
   220  
   221  }
   222  
   223  // handleRDSUpdate rebuilds any routing configuration server side for any filter
   224  // chains that point to this RDS, and potentially makes pending lds
   225  // configuration to swap to be active.
   226  func (l *listenerWrapper) handleRDSUpdate(routeName string, rcu rdsWatcherUpdate) {
   227  	// Update any filter chains that point to this route configuration.
   228  	if l.activeFilterChainManager != nil {
   229  		for _, fc := range l.activeFilterChainManager.FilterChains() {
   230  			if fc.RouteConfigName != routeName {
   231  				continue
   232  			}
   233  			if rcu.err != nil && rcu.data == nil { // Either NACK before update, or resource not found triggers this conditional.
   234  				urc := &xdsresource.UsableRouteConfiguration{Err: rcu.err}
   235  				urc.NodeID = l.xdsNodeID
   236  				fc.UsableRouteConfiguration.Store(urc)
   237  				continue
   238  			}
   239  			urc := fc.ConstructUsableRouteConfiguration(*rcu.data)
   240  			urc.NodeID = l.xdsNodeID
   241  			fc.UsableRouteConfiguration.Store(urc)
   242  		}
   243  	}
   244  	if l.rdsHandler.determineRouteConfigurationReady() {
   245  		l.maybeUpdateFilterChains()
   246  	}
   247  }
   248  
   249  // instantiateFilterChainRoutingConfigurationsLocked instantiates all of the
   250  // routing configuration for the newly active filter chains. For any inline
   251  // route configurations, uses that, otherwise uses cached rdsHandler updates.
   252  // Must be called within an xDS Client Callback.
   253  func (l *listenerWrapper) instantiateFilterChainRoutingConfigurationsLocked() {
   254  	for _, fc := range l.activeFilterChainManager.FilterChains() {
   255  		if fc.InlineRouteConfig != nil {
   256  			urc := fc.ConstructUsableRouteConfiguration(*fc.InlineRouteConfig)
   257  			urc.NodeID = l.xdsNodeID
   258  			fc.UsableRouteConfiguration.Store(urc) // Can't race with an RPC coming in but no harm making atomic.
   259  			continue
   260  		} // Inline configuration constructed once here, will remain for lifetime of filter chain.
   261  		rcu := l.rdsHandler.updates[fc.RouteConfigName]
   262  		if rcu.err != nil && rcu.data == nil {
   263  			urc := &xdsresource.UsableRouteConfiguration{Err: rcu.err}
   264  			urc.NodeID = l.xdsNodeID
   265  			fc.UsableRouteConfiguration.Store(urc)
   266  			continue
   267  		}
   268  		urc := fc.ConstructUsableRouteConfiguration(*rcu.data)
   269  		urc.NodeID = l.xdsNodeID
   270  		fc.UsableRouteConfiguration.Store(urc) // Can't race with an RPC coming in but no harm making atomic.
   271  	}
   272  }
   273  
   274  // Accept blocks on an Accept() on the underlying listener, and wraps the
   275  // returned net.connWrapper with the configured certificate providers.
   276  func (l *listenerWrapper) Accept() (net.Conn, error) {
   277  	var retries int
   278  	for {
   279  		conn, err := l.Listener.Accept()
   280  		if err != nil {
   281  			// Temporary() method is implemented by certain error types returned
   282  			// from the net package, and it is useful for us to not shutdown the
   283  			// server in these conditions. The listen queue being full is one
   284  			// such case.
   285  			if ne, ok := err.(interface{ Temporary() bool }); !ok || !ne.Temporary() {
   286  				return nil, err
   287  			}
   288  			retries++
   289  			timer := time.NewTimer(backoffFunc(retries))
   290  			select {
   291  			case <-timer.C:
   292  			case <-l.closed.Done():
   293  				timer.Stop()
   294  				// Continuing here will cause us to call Accept() again
   295  				// which will return a non-temporary error.
   296  				continue
   297  			}
   298  			continue
   299  		}
   300  		// Reset retries after a successful Accept().
   301  		retries = 0
   302  
   303  		// Since the net.Conn represents an incoming connection, the source and
   304  		// destination address can be retrieved from the local address and
   305  		// remote address of the net.Conn respectively.
   306  		destAddr, ok1 := conn.LocalAddr().(*net.TCPAddr)
   307  		srcAddr, ok2 := conn.RemoteAddr().(*net.TCPAddr)
   308  		if !ok1 || !ok2 {
   309  			// If the incoming connection is not a TCP connection, which is
   310  			// really unexpected since we check whether the provided listener is
   311  			// a TCP listener in Serve(), we return an error which would cause
   312  			// us to stop serving.
   313  			return nil, fmt.Errorf("received connection with non-TCP address (local: %T, remote %T)", conn.LocalAddr(), conn.RemoteAddr())
   314  		}
   315  
   316  		l.mu.Lock()
   317  		if l.mode == connectivity.ServingModeNotServing {
   318  			// Close connections as soon as we accept them when we are in
   319  			// "not-serving" mode. Since we accept a net.Listener from the user
   320  			// in Serve(), we cannot close the listener when we move to
   321  			// "not-serving". Closing the connection immediately upon accepting
   322  			// is one of the other ways to implement the "not-serving" mode as
   323  			// outlined in gRFC A36.
   324  			l.mu.Unlock()
   325  			conn.Close()
   326  			continue
   327  		}
   328  
   329  		fc, err := l.activeFilterChainManager.Lookup(xdsresource.FilterChainLookupParams{
   330  			IsUnspecifiedListener: l.isUnspecifiedAddr,
   331  			DestAddr:              destAddr.IP,
   332  			SourceAddr:            srcAddr.IP,
   333  			SourcePort:            srcAddr.Port,
   334  		})
   335  		if err != nil {
   336  			l.mu.Unlock()
   337  			// When a matching filter chain is not found, we close the
   338  			// connection right away, but do not return an error back to
   339  			// `grpc.Serve()` from where this Accept() was invoked. Returning an
   340  			// error to `grpc.Serve()` causes the server to shutdown. If we want
   341  			// to avoid the server from shutting down, we would need to return
   342  			// an error type which implements the `Temporary() bool` method,
   343  			// which is invoked by `grpc.Serve()` to see if the returned error
   344  			// represents a temporary condition. In the case of a temporary
   345  			// error, `grpc.Serve()` method sleeps for a small duration and
   346  			// therefore ends up blocking all connection attempts during that
   347  			// time frame, which is also not ideal for an error like this.
   348  			l.logger.Warningf("Connection from %s to %s failed to find any matching filter chain", conn.RemoteAddr(), conn.LocalAddr())
   349  			conn.Close()
   350  			continue
   351  		}
   352  		cw := &connWrapper{Conn: conn, filterChain: fc, parent: l, urc: fc.UsableRouteConfiguration}
   353  		l.conns[cw] = true
   354  		l.mu.Unlock()
   355  		return cw, nil
   356  	}
   357  }
   358  
   359  func (l *listenerWrapper) removeConn(conn *connWrapper) {
   360  	l.mu.Lock()
   361  	defer l.mu.Unlock()
   362  	delete(l.conns, conn)
   363  }
   364  
   365  // Close closes the underlying listener. It also cancels the xDS watch
   366  // registered in Serve() and closes any certificate provider instances created
   367  // based on security configuration received in the LDS response.
   368  func (l *listenerWrapper) Close() error {
   369  	l.closed.Fire()
   370  	l.Listener.Close()
   371  	if l.cancelWatch != nil {
   372  		l.cancelWatch()
   373  	}
   374  	l.rdsHandler.close()
   375  	return nil
   376  }
   377  
   378  // switchModeLocked switches the current mode of the listener wrapper. It also
   379  // gracefully closes any connections if the listener wrapper transitions into
   380  // not serving. If the serving mode has changed, it invokes the registered mode
   381  // change callback.
   382  func (l *listenerWrapper) switchModeLocked(newMode connectivity.ServingMode, err error) {
   383  	if l.mode == newMode && l.mode == connectivity.ServingModeServing {
   384  		// Redundant updates are suppressed only when we are SERVING and the new
   385  		// mode is also SERVING. In the other case (where we are NOT_SERVING and the
   386  		// new mode is also NOT_SERVING), the update is not suppressed as:
   387  		//   1. the error may have change
   388  		//   2. it provides a timestamp of the last backoff attempt
   389  		return
   390  	}
   391  	l.mode = newMode
   392  	if l.mode == connectivity.ServingModeNotServing {
   393  		connsToClose := l.conns
   394  		l.conns = make(map[*connWrapper]bool)
   395  		go func() {
   396  			for conn := range connsToClose {
   397  				conn.Drain()
   398  			}
   399  		}()
   400  	}
   401  	// The XdsServer API will allow applications to register a "serving state"
   402  	// callback to be invoked when the server begins serving and when the
   403  	// server encounters errors that force it to be "not serving". If "not
   404  	// serving", the callback must be provided error information, for
   405  	// debugging use by developers - A36.
   406  	if l.modeCallback != nil {
   407  		l.modeCallback(l.Listener.Addr(), newMode, err)
   408  	}
   409  }
   410  
   411  func (l *listenerWrapper) annotateErrorWithNodeID(err error) error {
   412  	return fmt.Errorf("[xDS node id: %v]: %w", l.xdsNodeID, err)
   413  }
   414  
   415  func (l *listenerWrapper) onLDSResourceDoesNotExist(err error) {
   416  	l.mu.Lock()
   417  	defer l.mu.Unlock()
   418  	l.switchModeLocked(connectivity.ServingModeNotServing, l.annotateErrorWithNodeID(err))
   419  	l.activeFilterChainManager = nil
   420  	l.pendingFilterChainManager = nil
   421  	l.rdsHandler.updateRouteNamesToWatch(make(map[string]bool))
   422  }
   423  
   424  // ldsWatcher implements the xdsresource.ListenerWatcher interface and is
   425  // passed to the WatchListener API.
   426  type ldsWatcher struct {
   427  	parent *listenerWrapper
   428  	logger *internalgrpclog.PrefixLogger
   429  	name   string
   430  }
   431  
   432  func (lw *ldsWatcher) OnUpdate(update *xdsresource.ListenerResourceData, onDone xdsresource.OnDoneFunc) {
   433  	defer onDone()
   434  	if lw.parent.closed.HasFired() {
   435  		lw.logger.Warningf("Resource %q received update: %#v after listener was closed", lw.name, update)
   436  		return
   437  	}
   438  	if lw.logger.V(2) {
   439  		lw.logger.Infof("LDS watch for resource %q received update: %#v", lw.name, update.Resource)
   440  	}
   441  	lw.parent.handleLDSUpdate(update.Resource)
   442  }
   443  
   444  func (lw *ldsWatcher) OnError(err error, onDone xdsresource.OnDoneFunc) {
   445  	defer onDone()
   446  	if lw.parent.closed.HasFired() {
   447  		lw.logger.Warningf("Resource %q received error: %v after listener was closed", lw.name, err)
   448  		return
   449  	}
   450  	if lw.logger.V(2) {
   451  		lw.logger.Infof("LDS watch for resource %q reported error: %v", lw.name, err)
   452  	}
   453  	// For errors which are anything other than "resource-not-found", we
   454  	// continue to use the old configuration.
   455  }
   456  
   457  func (lw *ldsWatcher) OnResourceDoesNotExist(onDone xdsresource.OnDoneFunc) {
   458  	defer onDone()
   459  	if lw.parent.closed.HasFired() {
   460  		lw.logger.Warningf("Resource %q received resource-does-not-exist error after listener was closed", lw.name)
   461  		return
   462  	}
   463  	lw.logger.Warningf("LDS watch for resource %q reported resource-does-not-exist error", lw.name)
   464  
   465  	err := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource name %q of type Listener not found in received response", lw.name)
   466  	lw.parent.onLDSResourceDoesNotExist(err)
   467  }