2019-04-29 21:25:05 +00:00
// Copyright 2018 The gVisor Authors.
2018-07-09 21:03:03 +00:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2018-04-27 17:37:02 +00:00
package tcp
import (
"fmt"
2018-05-11 23:27:50 +00:00
"sync"
2018-07-10 16:22:37 +00:00
"time"
2018-04-27 17:37:02 +00:00
2019-06-13 23:49:09 +00:00
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
2018-04-27 17:37:02 +00:00
)
2018-05-11 23:27:50 +00:00
func ( e * endpoint ) drainSegmentLocked ( ) {
// Drain only up to once.
if e . drainDone != nil {
return
}
e . drainDone = make ( chan struct { } )
e . undrain = make ( chan struct { } )
e . mu . Unlock ( )
2018-07-10 16:22:37 +00:00
e . notifyProtocolGoroutine ( notifyDrain )
2018-05-11 23:27:50 +00:00
<- e . drainDone
e . mu . Lock ( )
}
2018-04-27 17:37:02 +00:00
// beforeSave is invoked by stateify.
func ( e * endpoint ) beforeSave ( ) {
// Stop incoming packets.
e . segmentQueue . setLimit ( 0 )
2018-05-11 23:27:50 +00:00
e . mu . Lock ( )
defer e . mu . Unlock ( )
2018-04-27 17:37:02 +00:00
switch e . state {
2019-06-06 22:03:44 +00:00
case StateInitial , StateBound :
case StateEstablished , StateSynSent , StateSynRecv , StateFinWait1 , StateFinWait2 , StateTimeWait , StateCloseWait , StateLastAck , StateClosing :
2018-07-10 20:53:39 +00:00
if e . route . Capabilities ( ) & stack . CapabilitySaveRestore == 0 {
2018-07-30 22:42:01 +00:00
if e . route . Capabilities ( ) & stack . CapabilityDisconnectOk == 0 {
panic ( tcpip . ErrSaveRejection { fmt . Errorf ( "endpoint cannot be saved in connected state: local %v:%d, remote %v:%d" , e . id . LocalAddress , e . id . LocalPort , e . id . RemoteAddress , e . id . RemotePort ) } )
}
e . resetConnectionLocked ( tcpip . ErrConnectionAborted )
e . mu . Unlock ( )
e . Close ( )
e . mu . Lock ( )
2018-07-10 20:53:39 +00:00
}
if ! e . workerRunning {
2018-07-30 22:42:01 +00:00
// The endpoint must be in acceptedChan or has been just
// disconnected and closed.
2018-07-10 16:22:37 +00:00
break
2018-04-27 17:37:02 +00:00
}
2018-07-10 20:53:39 +00:00
fallthrough
2019-06-06 22:03:44 +00:00
case StateListen , StateConnecting :
2018-05-11 23:27:50 +00:00
e . drainSegmentLocked ( )
2019-06-06 22:03:44 +00:00
if e . state != StateClose && e . state != StateError {
2018-07-10 16:22:37 +00:00
if ! e . workerRunning {
panic ( "endpoint has no worker running in listen, connecting, or connected state" )
}
2018-05-11 23:27:50 +00:00
break
}
fallthrough
2019-06-06 22:03:44 +00:00
case StateError , StateClose :
for e . state == StateError && e . workerRunning {
2018-07-10 16:22:37 +00:00
e . mu . Unlock ( )
time . Sleep ( 100 * time . Millisecond )
e . mu . Lock ( )
}
2018-06-05 22:43:38 +00:00
if e . workerRunning {
2018-07-10 16:22:37 +00:00
panic ( "endpoint still has worker running in closed or error state" )
2018-06-05 22:43:38 +00:00
}
2018-04-27 17:37:02 +00:00
default :
panic ( fmt . Sprintf ( "endpoint in unknown state %v" , e . state ) )
}
2018-07-10 16:22:37 +00:00
if e . waiterQueue != nil && ! e . waiterQueue . IsEmpty ( ) {
panic ( "endpoint still has waiters upon save" )
}
2019-06-06 22:03:44 +00:00
if e . state != StateClose && ! ( ( e . state == StateBound || e . state == StateListen ) == e . isPortReserved ) {
2018-12-05 23:01:41 +00:00
panic ( "endpoints which are not in the closed state must have a reserved port IFF they are in bound or listen state" )
2018-07-10 16:22:37 +00:00
}
2018-07-12 20:48:18 +00:00
}
2018-07-10 16:22:37 +00:00
2018-07-12 20:48:18 +00:00
// saveAcceptedChan is invoked by stateify.
func ( e * endpoint ) saveAcceptedChan ( ) [ ] * endpoint {
if e . acceptedChan == nil {
return nil
}
acceptedEndpoints := make ( [ ] * endpoint , len ( e . acceptedChan ) , cap ( e . acceptedChan ) )
2018-12-05 23:01:41 +00:00
for i := 0 ; i < len ( acceptedEndpoints ) ; i ++ {
select {
case ep := <- e . acceptedChan :
acceptedEndpoints [ i ] = ep
default :
panic ( "endpoint acceptedChan buffer got consumed by background context" )
}
2018-07-12 20:48:18 +00:00
}
2018-12-05 23:01:41 +00:00
for i := 0 ; i < len ( acceptedEndpoints ) ; i ++ {
select {
case e . acceptedChan <- acceptedEndpoints [ i ] :
default :
panic ( "endpoint acceptedChan buffer got populated by background context" )
}
2018-07-12 20:48:18 +00:00
}
return acceptedEndpoints
}
// loadAcceptedChan is invoked by stateify.
func ( e * endpoint ) loadAcceptedChan ( acceptedEndpoints [ ] * endpoint ) {
if cap ( acceptedEndpoints ) > 0 {
e . acceptedChan = make ( chan * endpoint , cap ( acceptedEndpoints ) )
for _ , ep := range acceptedEndpoints {
e . acceptedChan <- ep
2018-07-10 16:22:37 +00:00
}
}
}
// saveState is invoked by stateify.
2019-06-06 22:03:44 +00:00
func ( e * endpoint ) saveState ( ) EndpointState {
2018-07-10 16:22:37 +00:00
return e . state
}
// Endpoint loading must be done in the following ordering by their state, to
// avoid dangling connecting w/o listening peer, and to avoid conflicts in port
// reservation.
var connectedLoading sync . WaitGroup
var listenLoading sync . WaitGroup
var connectingLoading sync . WaitGroup
// Bound endpoint loading happens last.
// loadState is invoked by stateify.
2019-06-06 22:03:44 +00:00
func ( e * endpoint ) loadState ( state EndpointState ) {
2018-07-10 16:22:37 +00:00
// This is to ensure that the loading wait groups include all applicable
// endpoints before any asynchronous calls to the Wait() methods.
switch state {
2019-06-06 22:03:44 +00:00
case StateEstablished , StateFinWait1 , StateFinWait2 , StateTimeWait , StateCloseWait , StateLastAck , StateClosing :
2018-07-10 16:22:37 +00:00
connectedLoading . Add ( 1 )
2019-06-06 22:03:44 +00:00
case StateListen :
2018-07-10 16:22:37 +00:00
listenLoading . Add ( 1 )
2019-06-06 22:03:44 +00:00
case StateConnecting , StateSynSent , StateSynRecv :
2018-07-10 16:22:37 +00:00
connectingLoading . Add ( 1 )
}
e . state = state
2018-04-27 17:37:02 +00:00
}
// afterLoad is invoked by stateify.
func ( e * endpoint ) afterLoad ( ) {
e . stack = stack . StackFromEnv
2019-05-31 23:16:24 +00:00
e . segmentQueue . setLimit ( MaxUnprocessedSegments )
2018-05-11 23:27:50 +00:00
e . workMu . Init ( )
2018-04-27 17:37:02 +00:00
2018-05-11 23:27:50 +00:00
state := e . state
switch state {
2019-06-06 22:03:44 +00:00
case StateInitial , StateBound , StateListen , StateConnecting , StateEstablished :
2018-04-27 17:37:02 +00:00
var ss SendBufferSizeOption
if err := e . stack . TransportProtocolOption ( ProtocolNumber , & ss ) ; err == nil {
if e . sndBufSize < ss . Min || e . sndBufSize > ss . Max {
panic ( fmt . Sprintf ( "endpoint.sndBufSize %d is outside the min and max allowed [%d, %d]" , e . sndBufSize , ss . Min , ss . Max ) )
}
if e . rcvBufSize < ss . Min || e . rcvBufSize > ss . Max {
panic ( fmt . Sprintf ( "endpoint.rcvBufSize %d is outside the min and max allowed [%d, %d]" , e . rcvBufSize , ss . Min , ss . Max ) )
}
}
}
2018-07-10 16:22:37 +00:00
bind := func ( ) {
2019-06-06 22:03:44 +00:00
e . state = StateInitial
2018-07-10 16:22:37 +00:00
if len ( e . bindAddress ) == 0 {
e . bindAddress = e . id . LocalAddress
}
2019-03-05 22:52:35 +00:00
if err := e . Bind ( tcpip . FullAddress { Addr : e . bindAddress , Port : e . id . LocalPort } ) ; err != nil {
2018-05-11 23:27:50 +00:00
panic ( "endpoint binding failed: " + err . String ( ) )
}
}
switch state {
2019-06-06 22:03:44 +00:00
case StateEstablished , StateFinWait1 , StateFinWait2 , StateTimeWait , StateCloseWait , StateLastAck , StateClosing :
2018-07-10 16:22:37 +00:00
bind ( )
if len ( e . connectingAddress ) == 0 {
// This endpoint is accepted by netstack but not yet by
// the app. If the endpoint is IPv6 but the remote
// address is IPv4, we need to connect as IPv6 so that
// dual-stack mode can be properly activated.
if e . netProto == header . IPv6ProtocolNumber && len ( e . id . RemoteAddress ) != header . IPv6AddressSize {
e . connectingAddress = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" + e . id . RemoteAddress
} else {
e . connectingAddress = e . id . RemoteAddress
}
2018-05-11 23:27:50 +00:00
}
2019-02-25 23:18:55 +00:00
// Reset the scoreboard to reinitialize the sack information as
// we do not restore SACK information.
e . scoreboard . Reset ( )
2018-07-10 16:22:37 +00:00
if err := e . connect ( tcpip . FullAddress { NIC : e . boundNICID , Addr : e . connectingAddress , Port : e . id . RemotePort } , false , e . workerRunning ) ; err != tcpip . ErrConnectStarted {
2018-05-11 23:27:50 +00:00
panic ( "endpoint connecting failed: " + err . String ( ) )
}
2018-07-10 16:22:37 +00:00
connectedLoading . Done ( )
2019-06-06 22:03:44 +00:00
case StateListen :
2018-07-10 16:22:37 +00:00
tcpip . AsyncLoading . Add ( 1 )
go func ( ) {
connectedLoading . Wait ( )
bind ( )
backlog := cap ( e . acceptedChan )
if err := e . Listen ( backlog ) ; err != nil {
panic ( "endpoint listening failed: " + err . String ( ) )
}
listenLoading . Done ( )
tcpip . AsyncLoading . Done ( )
} ( )
2019-06-06 22:03:44 +00:00
case StateConnecting , StateSynSent , StateSynRecv :
2018-07-10 16:22:37 +00:00
tcpip . AsyncLoading . Add ( 1 )
go func ( ) {
connectedLoading . Wait ( )
listenLoading . Wait ( )
bind ( )
if err := e . Connect ( tcpip . FullAddress { NIC : e . boundNICID , Addr : e . connectingAddress , Port : e . id . RemotePort } ) ; err != tcpip . ErrConnectStarted {
panic ( "endpoint connecting failed: " + err . String ( ) )
}
connectingLoading . Done ( )
tcpip . AsyncLoading . Done ( )
} ( )
2019-06-06 22:03:44 +00:00
case StateBound :
2018-07-10 16:22:37 +00:00
tcpip . AsyncLoading . Add ( 1 )
go func ( ) {
connectedLoading . Wait ( )
listenLoading . Wait ( )
connectingLoading . Wait ( )
bind ( )
tcpip . AsyncLoading . Done ( )
} ( )
2019-06-06 22:03:44 +00:00
case StateClose :
2018-12-05 23:01:41 +00:00
if e . isPortReserved {
tcpip . AsyncLoading . Add ( 1 )
go func ( ) {
connectedLoading . Wait ( )
listenLoading . Wait ( )
connectingLoading . Wait ( )
bind ( )
2019-06-06 22:03:44 +00:00
e . state = StateClose
2018-12-05 23:01:41 +00:00
tcpip . AsyncLoading . Done ( )
} ( )
}
fallthrough
2019-06-06 22:03:44 +00:00
case StateError :
2018-07-10 16:22:37 +00:00
tcpip . DeleteDanglingEndpoint ( e )
2018-05-11 23:27:50 +00:00
}
2018-04-27 17:37:02 +00:00
}
2018-05-11 23:27:50 +00:00
// saveLastError is invoked by stateify.
func ( e * endpoint ) saveLastError ( ) string {
if e . lastError == nil {
return ""
}
return e . lastError . String ( )
}
// loadLastError is invoked by stateify.
func ( e * endpoint ) loadLastError ( s string ) {
if s == "" {
return
}
e . lastError = loadError ( s )
}
// saveHardError is invoked by stateify.
func ( e * endpoint ) saveHardError ( ) string {
if e . hardError == nil {
return ""
}
return e . hardError . String ( )
}
// loadHardError is invoked by stateify.
func ( e * endpoint ) loadHardError ( s string ) {
if s == "" {
return
}
e . hardError = loadError ( s )
}
var messageToError map [ string ] * tcpip . Error
var populate sync . Once
func loadError ( s string ) * tcpip . Error {
populate . Do ( func ( ) {
var errors = [ ] * tcpip . Error {
tcpip . ErrUnknownProtocol ,
tcpip . ErrUnknownNICID ,
2019-03-08 23:48:16 +00:00
tcpip . ErrUnknownDevice ,
2018-05-11 23:27:50 +00:00
tcpip . ErrUnknownProtocolOption ,
tcpip . ErrDuplicateNICID ,
tcpip . ErrDuplicateAddress ,
tcpip . ErrNoRoute ,
tcpip . ErrBadLinkEndpoint ,
tcpip . ErrAlreadyBound ,
tcpip . ErrInvalidEndpointState ,
tcpip . ErrAlreadyConnecting ,
tcpip . ErrAlreadyConnected ,
tcpip . ErrNoPortAvailable ,
tcpip . ErrPortInUse ,
tcpip . ErrBadLocalAddress ,
tcpip . ErrClosedForSend ,
tcpip . ErrClosedForReceive ,
tcpip . ErrWouldBlock ,
tcpip . ErrConnectionRefused ,
tcpip . ErrTimeout ,
tcpip . ErrAborted ,
tcpip . ErrConnectStarted ,
tcpip . ErrDestinationRequired ,
tcpip . ErrNotSupported ,
tcpip . ErrQueueSizeNotSupported ,
tcpip . ErrNotConnected ,
tcpip . ErrConnectionReset ,
tcpip . ErrConnectionAborted ,
tcpip . ErrNoSuchFile ,
tcpip . ErrInvalidOptionValue ,
tcpip . ErrNoLinkAddress ,
tcpip . ErrBadAddress ,
2018-05-17 19:49:16 +00:00
tcpip . ErrNetworkUnreachable ,
2018-10-10 21:09:24 +00:00
tcpip . ErrMessageTooLong ,
tcpip . ErrNoBufferSpace ,
2019-02-20 20:53:07 +00:00
tcpip . ErrBroadcastDisabled ,
2019-04-26 23:50:35 +00:00
tcpip . ErrNotPermitted ,
2018-05-11 23:27:50 +00:00
}
messageToError = make ( map [ string ] * tcpip . Error )
for _ , e := range errors {
if messageToError [ e . String ( ) ] != nil {
panic ( "tcpip errors with duplicated message: " + e . String ( ) )
}
messageToError [ e . String ( ) ] = e
}
} )
e , ok := messageToError [ s ]
if ! ok {
panic ( "unknown error message: " + s )
}
return e
}