google.golang.org/grpc@v1.62.1/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/xds/internal/xdsclient/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 modeCallback: params.ModeCallback, 88 isUnspecifiedAddr: params.Listener.Addr().(*net.TCPAddr).IP.IsUnspecified(), 89 90 mode: connectivity.ServingModeNotServing, 91 closed: grpcsync.NewEvent(), 92 } 93 lw.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[xds-server-listener %p] ", lw)) 94 95 // Serve() verifies that Addr() returns a valid TCPAddr. So, it is safe to 96 // ignore the error from SplitHostPort(). 97 lisAddr := lw.Listener.Addr().String() 98 lw.addr, lw.port, _ = net.SplitHostPort(lisAddr) 99 100 lw.rdsHandler = newRDSHandler(lw.handleRDSUpdate, lw.xdsC, lw.logger) 101 lw.cancelWatch = xdsresource.WatchListener(lw.xdsC, lw.name, &ldsWatcher{ 102 parent: lw, 103 logger: lw.logger, 104 name: lw.name, 105 }) 106 return lw 107 } 108 109 // listenerWrapper wraps the net.Listener associated with the listening address 110 // passed to Serve(). It also contains all other state associated with this 111 // particular invocation of Serve(). 112 type listenerWrapper struct { 113 net.Listener 114 logger *internalgrpclog.PrefixLogger 115 116 name string 117 xdsC XDSClient 118 cancelWatch func() 119 modeCallback ServingModeCallback 120 121 // Set to true if the listener is bound to the IP_ANY address (which is 122 // "0.0.0.0" for IPv4 and "::" for IPv6). 123 isUnspecifiedAddr bool 124 // Listening address and port. Used to validate the socket address in the 125 // Listener resource received from the control plane. 126 addr, port string 127 128 // A small race exists in the XDSClient code between the receipt of an xDS 129 // response and the user cancelling the associated watch. In this window, 130 // the registered callback may be invoked after the watch is canceled, and 131 // the user is expected to work around this. This event signifies that the 132 // listener is closed (and hence the watch is cancelled), and we drop any 133 // updates received in the callback if this event has fired. 134 closed *grpcsync.Event 135 136 // mu guards access to the current serving mode and the active filter chain 137 // manager. 138 mu sync.RWMutex 139 // Current serving mode. 140 mode connectivity.ServingMode 141 // Filter chain manager currently serving. 142 activeFilterChainManager *xdsresource.FilterChainManager 143 // conns accepted with configuration from activeFilterChainManager. 144 conns []*connWrapper 145 146 // These fields are read/written to in the context of xDS updates, which are 147 // guaranteed to be emitted synchronously from the xDS Client. Thus, they do 148 // not need further synchronization. 149 150 // Pending filter chain manager. Will go active once rdsHandler has received 151 // all the RDS resources this filter chain manager needs. 152 pendingFilterChainManager *xdsresource.FilterChainManager 153 154 // rdsHandler is used for any dynamic RDS resources specified in a LDS 155 // update. 156 rdsHandler *rdsHandler 157 } 158 159 func (l *listenerWrapper) handleLDSUpdate(update xdsresource.ListenerUpdate) { 160 ilc := update.InboundListenerCfg 161 // Make sure that the socket address on the received Listener resource 162 // matches the address of the net.Listener passed to us by the user. This 163 // check is done here instead of at the XDSClient layer because of the 164 // following couple of reasons: 165 // - XDSClient cannot know the listening address of every listener in the 166 // system, and hence cannot perform this check. 167 // - this is a very context-dependent check and only the server has the 168 // appropriate context to perform this check. 169 // 170 // What this means is that the XDSClient has ACKed a resource which can push 171 // the server into a "not serving" mode. This is not ideal, but this is 172 // what we have decided to do. 173 if ilc.Address != l.addr || ilc.Port != l.port { 174 l.mu.Lock() 175 l.switchModeLocked(connectivity.ServingModeNotServing, fmt.Errorf("address (%s:%s) in Listener update does not match listening address: (%s:%s)", ilc.Address, ilc.Port, l.addr, l.port)) 176 l.mu.Unlock() 177 return 178 } 179 180 l.pendingFilterChainManager = ilc.FilterChains 181 l.rdsHandler.updateRouteNamesToWatch(ilc.FilterChains.RouteConfigNames) 182 183 if l.rdsHandler.determineRouteConfigurationReady() { 184 l.maybeUpdateFilterChains() 185 } 186 } 187 188 // maybeUpdateFilterChains swaps in the pending filter chain manager to the 189 // active one if the pending filter chain manager is present. If a swap occurs, 190 // it also drains (gracefully stops) any connections that were accepted on the 191 // old active filter chain manager, and puts this listener in state SERVING. 192 // Must be called within an xDS Client Callback. 193 func (l *listenerWrapper) maybeUpdateFilterChains() { 194 if l.pendingFilterChainManager == nil { 195 // Nothing to update, return early. 196 return 197 } 198 199 l.mu.Lock() 200 l.switchModeLocked(connectivity.ServingModeServing, nil) 201 // "Updates to a Listener cause all older connections on that Listener to be 202 // gracefully shut down with a grace period of 10 minutes for long-lived 203 // RPC's, such that clients will reconnect and have the updated 204 // configuration apply." - A36 205 var connsToClose []*connWrapper 206 if l.activeFilterChainManager != nil { // If there is a filter chain manager to clean up. 207 connsToClose = l.conns 208 l.conns = nil 209 } 210 l.activeFilterChainManager = l.pendingFilterChainManager 211 l.pendingFilterChainManager = nil 212 l.instantiateFilterChainRoutingConfigurationsLocked() 213 l.mu.Unlock() 214 go func() { 215 for _, conn := range connsToClose { 216 conn.Drain() 217 } 218 }() 219 220 } 221 222 // handleRDSUpdate rebuilds any routing configuration server side for any filter 223 // chains that point to this RDS, and potentially makes pending lds 224 // configuration to swap to be active. 225 func (l *listenerWrapper) handleRDSUpdate(routeName string, rcu rdsWatcherUpdate) { 226 // Update any filter chains that point to this route configuration. 227 if l.activeFilterChainManager != nil { 228 for _, fc := range l.activeFilterChainManager.FilterChains() { 229 if fc.RouteConfigName != routeName { 230 continue 231 } 232 if rcu.err != nil && rcu.data == nil { // Either NACK before update, or resource not found triggers this conditional. 233 fc.UsableRouteConfiguration.Store(&xdsresource.UsableRouteConfiguration{ 234 Err: rcu.err, 235 }) 236 continue 237 } 238 fc.UsableRouteConfiguration.Store(fc.ConstructUsableRouteConfiguration(*rcu.data)) 239 } 240 } 241 if l.rdsHandler.determineRouteConfigurationReady() { 242 l.maybeUpdateFilterChains() 243 } 244 } 245 246 // instantiateFilterChainRoutingConfigurationsLocked instantiates all of the 247 // routing configuration for the newly active filter chains. For any inline 248 // route configurations, uses that, otherwise uses cached rdsHandler updates. 249 // Must be called within an xDS Client Callback. 250 func (l *listenerWrapper) instantiateFilterChainRoutingConfigurationsLocked() { 251 for _, fc := range l.activeFilterChainManager.FilterChains() { 252 if fc.InlineRouteConfig != nil { 253 fc.UsableRouteConfiguration.Store(fc.ConstructUsableRouteConfiguration(*fc.InlineRouteConfig)) // Can't race with an RPC coming in but no harm making atomic. 254 continue 255 } // Inline configuration constructed once here, will remain for lifetime of filter chain. 256 rcu := l.rdsHandler.updates[fc.RouteConfigName] 257 if rcu.err != nil && rcu.data == nil { 258 fc.UsableRouteConfiguration.Store(&xdsresource.UsableRouteConfiguration{Err: rcu.err}) 259 continue 260 } 261 fc.UsableRouteConfiguration.Store(fc.ConstructUsableRouteConfiguration(*rcu.data)) // Can't race with an RPC coming in but no harm making atomic. 262 } 263 } 264 265 // Accept blocks on an Accept() on the underlying listener, and wraps the 266 // returned net.connWrapper with the configured certificate providers. 267 func (l *listenerWrapper) Accept() (net.Conn, error) { 268 var retries int 269 for { 270 conn, err := l.Listener.Accept() 271 if err != nil { 272 // Temporary() method is implemented by certain error types returned 273 // from the net package, and it is useful for us to not shutdown the 274 // server in these conditions. The listen queue being full is one 275 // such case. 276 if ne, ok := err.(interface{ Temporary() bool }); !ok || !ne.Temporary() { 277 return nil, err 278 } 279 retries++ 280 timer := time.NewTimer(backoffFunc(retries)) 281 select { 282 case <-timer.C: 283 case <-l.closed.Done(): 284 timer.Stop() 285 // Continuing here will cause us to call Accept() again 286 // which will return a non-temporary error. 287 continue 288 } 289 continue 290 } 291 // Reset retries after a successful Accept(). 292 retries = 0 293 294 // Since the net.Conn represents an incoming connection, the source and 295 // destination address can be retrieved from the local address and 296 // remote address of the net.Conn respectively. 297 destAddr, ok1 := conn.LocalAddr().(*net.TCPAddr) 298 srcAddr, ok2 := conn.RemoteAddr().(*net.TCPAddr) 299 if !ok1 || !ok2 { 300 // If the incoming connection is not a TCP connection, which is 301 // really unexpected since we check whether the provided listener is 302 // a TCP listener in Serve(), we return an error which would cause 303 // us to stop serving. 304 return nil, fmt.Errorf("received connection with non-TCP address (local: %T, remote %T)", conn.LocalAddr(), conn.RemoteAddr()) 305 } 306 307 l.mu.RLock() 308 if l.mode == connectivity.ServingModeNotServing { 309 // Close connections as soon as we accept them when we are in 310 // "not-serving" mode. Since we accept a net.Listener from the user 311 // in Serve(), we cannot close the listener when we move to 312 // "not-serving". Closing the connection immediately upon accepting 313 // is one of the other ways to implement the "not-serving" mode as 314 // outlined in gRFC A36. 315 l.mu.RUnlock() 316 conn.Close() 317 continue 318 } 319 320 fc, err := l.activeFilterChainManager.Lookup(xdsresource.FilterChainLookupParams{ 321 IsUnspecifiedListener: l.isUnspecifiedAddr, 322 DestAddr: destAddr.IP, 323 SourceAddr: srcAddr.IP, 324 SourcePort: srcAddr.Port, 325 }) 326 if err != nil { 327 l.mu.RUnlock() 328 // When a matching filter chain is not found, we close the 329 // connection right away, but do not return an error back to 330 // `grpc.Serve()` from where this Accept() was invoked. Returning an 331 // error to `grpc.Serve()` causes the server to shutdown. If we want 332 // to avoid the server from shutting down, we would need to return 333 // an error type which implements the `Temporary() bool` method, 334 // which is invoked by `grpc.Serve()` to see if the returned error 335 // represents a temporary condition. In the case of a temporary 336 // error, `grpc.Serve()` method sleeps for a small duration and 337 // therefore ends up blocking all connection attempts during that 338 // time frame, which is also not ideal for an error like this. 339 l.logger.Warningf("Connection from %s to %s failed to find any matching filter chain", conn.RemoteAddr(), conn.LocalAddr()) 340 conn.Close() 341 continue 342 } 343 cw := &connWrapper{Conn: conn, filterChain: fc, parent: l, urc: fc.UsableRouteConfiguration} 344 l.conns = append(l.conns, cw) 345 l.mu.RUnlock() 346 return cw, nil 347 } 348 } 349 350 // Close closes the underlying listener. It also cancels the xDS watch 351 // registered in Serve() and closes any certificate provider instances created 352 // based on security configuration received in the LDS response. 353 func (l *listenerWrapper) Close() error { 354 l.closed.Fire() 355 l.Listener.Close() 356 if l.cancelWatch != nil { 357 l.cancelWatch() 358 } 359 l.rdsHandler.close() 360 return nil 361 } 362 363 // switchModeLocked switches the current mode of the listener wrapper. It also 364 // gracefully closes any connections if the listener wrapper transitions into 365 // not serving. If the serving mode has changed, it invokes the registered mode 366 // change callback. 367 func (l *listenerWrapper) switchModeLocked(newMode connectivity.ServingMode, err error) { 368 if l.mode == newMode && l.mode == connectivity.ServingModeServing { 369 // Redundant updates are suppressed only when we are SERVING and the new 370 // mode is also SERVING. In the other case (where we are NOT_SERVING and the 371 // new mode is also NOT_SERVING), the update is not suppressed as: 372 // 1. the error may have change 373 // 2. it provides a timestamp of the last backoff attempt 374 return 375 } 376 l.mode = newMode 377 if l.mode == connectivity.ServingModeNotServing { 378 connsToClose := l.conns 379 l.conns = nil 380 go func() { 381 for _, conn := range connsToClose { 382 conn.Drain() 383 } 384 }() 385 } 386 // The XdsServer API will allow applications to register a "serving state" 387 // callback to be invoked when the server begins serving and when the 388 // server encounters errors that force it to be "not serving". If "not 389 // serving", the callback must be provided error information, for 390 // debugging use by developers - A36. 391 if l.modeCallback != nil { 392 l.modeCallback(l.Listener.Addr(), newMode, err) 393 } 394 } 395 396 func (l *listenerWrapper) onLDSResourceDoesNotExist(err error) { 397 l.mu.Lock() 398 defer l.mu.Unlock() 399 l.switchModeLocked(connectivity.ServingModeNotServing, err) 400 l.activeFilterChainManager = nil 401 l.pendingFilterChainManager = nil 402 l.rdsHandler.updateRouteNamesToWatch(make(map[string]bool)) 403 } 404 405 // ldsWatcher implements the xdsresource.ListenerWatcher interface and is 406 // passed to the WatchListener API. 407 type ldsWatcher struct { 408 parent *listenerWrapper 409 logger *internalgrpclog.PrefixLogger 410 name string 411 } 412 413 func (lw *ldsWatcher) OnUpdate(update *xdsresource.ListenerResourceData) { 414 if lw.parent.closed.HasFired() { 415 lw.logger.Warningf("Resource %q received update: %#v after listener was closed", lw.name, update) 416 return 417 } 418 if lw.logger.V(2) { 419 lw.logger.Infof("LDS watch for resource %q received update: %#v", lw.name, update.Resource) 420 } 421 lw.parent.handleLDSUpdate(update.Resource) 422 } 423 424 func (lw *ldsWatcher) OnError(err error) { 425 if lw.parent.closed.HasFired() { 426 lw.logger.Warningf("Resource %q received error: %v after listener was closed", lw.name, err) 427 return 428 } 429 if lw.logger.V(2) { 430 lw.logger.Infof("LDS watch for resource %q reported error: %v", lw.name, err) 431 } 432 // For errors which are anything other than "resource-not-found", we 433 // continue to use the old configuration. 434 } 435 436 func (lw *ldsWatcher) OnResourceDoesNotExist() { 437 if lw.parent.closed.HasFired() { 438 lw.logger.Warningf("Resource %q received resource-does-not-exist error after listener was closed", lw.name) 439 return 440 } 441 if lw.logger.V(2) { 442 lw.logger.Infof("LDS watch for resource %q reported resource-does-not-exist error", lw.name) 443 } 444 445 err := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource name %q of type Listener not found in received response", lw.name) 446 lw.parent.onLDSResourceDoesNotExist(err) 447 }