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 }