github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go (about)

     1  // Copyright 2018 The gVisor Authors.
     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 tcpconntrack implements a TCP connection tracking object. It allows
    16  // users with access to a segment stream to figure out when a connection is
    17  // established, reset, and closed (and in the last case, who closed first).
    18  package tcpconntrack
    19  
    20  import (
    21  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    22  	"github.com/SagerNet/gvisor/pkg/tcpip/seqnum"
    23  )
    24  
    25  // Result is returned when the state of a TCB is updated in response to an
    26  // inbound or outbound segment.
    27  type Result int
    28  
    29  const (
    30  	// ResultDrop indicates that the segment should be dropped.
    31  	ResultDrop Result = iota
    32  
    33  	// ResultConnecting indicates that the connection remains in a
    34  	// connecting state.
    35  	ResultConnecting
    36  
    37  	// ResultAlive indicates that the connection remains alive (connected).
    38  	ResultAlive
    39  
    40  	// ResultReset indicates that the connection was reset.
    41  	ResultReset
    42  
    43  	// ResultClosedByPeer indicates that the connection was gracefully
    44  	// closed, and the inbound stream was closed first.
    45  	ResultClosedByPeer
    46  
    47  	// ResultClosedBySelf indicates that the connection was gracefully
    48  	// closed, and the outbound stream was closed first.
    49  	ResultClosedBySelf
    50  )
    51  
    52  // TCB is a TCP Control Block. It holds state necessary to keep track of a TCP
    53  // connection and inform the caller when the connection has been closed.
    54  type TCB struct {
    55  	inbound  stream
    56  	outbound stream
    57  
    58  	// State handlers.
    59  	handlerInbound  func(*TCB, header.TCP) Result
    60  	handlerOutbound func(*TCB, header.TCP) Result
    61  
    62  	// firstFin holds a pointer to the first stream to send a FIN.
    63  	firstFin *stream
    64  
    65  	// state is the current state of the stream.
    66  	state Result
    67  }
    68  
    69  // Init initializes the state of the TCB according to the initial SYN.
    70  func (t *TCB) Init(initialSyn header.TCP) Result {
    71  	t.handlerInbound = synSentStateInbound
    72  	t.handlerOutbound = synSentStateOutbound
    73  
    74  	iss := seqnum.Value(initialSyn.SequenceNumber())
    75  	t.outbound.una = iss
    76  	t.outbound.nxt = iss.Add(logicalLen(initialSyn))
    77  	t.outbound.end = t.outbound.nxt
    78  
    79  	// Even though "end" is a sequence number, we don't know the initial
    80  	// receive sequence number yet, so we store the window size until we get
    81  	// a SYN from the peer.
    82  	t.inbound.una = 0
    83  	t.inbound.nxt = 0
    84  	t.inbound.end = seqnum.Value(initialSyn.WindowSize())
    85  	t.state = ResultConnecting
    86  	return t.state
    87  }
    88  
    89  // UpdateStateInbound updates the state of the TCB based on the supplied inbound
    90  // segment.
    91  func (t *TCB) UpdateStateInbound(tcp header.TCP) Result {
    92  	st := t.handlerInbound(t, tcp)
    93  	if st != ResultDrop {
    94  		t.state = st
    95  	}
    96  	return st
    97  }
    98  
    99  // UpdateStateOutbound updates the state of the TCB based on the supplied
   100  // outbound segment.
   101  func (t *TCB) UpdateStateOutbound(tcp header.TCP) Result {
   102  	st := t.handlerOutbound(t, tcp)
   103  	if st != ResultDrop {
   104  		t.state = st
   105  	}
   106  	return st
   107  }
   108  
   109  // State returns the current state of the TCB.
   110  func (t *TCB) State() Result {
   111  	return t.state
   112  }
   113  
   114  // IsAlive returns true as long as the connection is established(Alive)
   115  // or connecting state.
   116  func (t *TCB) IsAlive() bool {
   117  	return !t.inbound.rstSeen && !t.outbound.rstSeen && (!t.inbound.closed() || !t.outbound.closed())
   118  }
   119  
   120  // OutboundSendSequenceNumber returns the snd.NXT for the outbound stream.
   121  func (t *TCB) OutboundSendSequenceNumber() seqnum.Value {
   122  	return t.outbound.nxt
   123  }
   124  
   125  // InboundSendSequenceNumber returns the snd.NXT for the inbound stream.
   126  func (t *TCB) InboundSendSequenceNumber() seqnum.Value {
   127  	return t.inbound.nxt
   128  }
   129  
   130  // adapResult modifies the supplied "Result" according to the state of the TCB;
   131  // if r is anything other than "Alive", or if one of the streams isn't closed
   132  // yet, it is returned unmodified. Otherwise it's converted to either
   133  // ClosedBySelf or ClosedByPeer depending on which stream was closed first.
   134  func (t *TCB) adaptResult(r Result) Result {
   135  	// Check the unmodified case.
   136  	if r != ResultAlive || !t.inbound.closed() || !t.outbound.closed() {
   137  		return r
   138  	}
   139  
   140  	// Find out which was closed first.
   141  	if t.firstFin == &t.outbound {
   142  		return ResultClosedBySelf
   143  	}
   144  
   145  	return ResultClosedByPeer
   146  }
   147  
   148  // synSentStateInbound is the state handler for inbound segments when the
   149  // connection is in SYN-SENT state.
   150  func synSentStateInbound(t *TCB, tcp header.TCP) Result {
   151  	flags := tcp.Flags()
   152  	ackPresent := flags&header.TCPFlagAck != 0
   153  	ack := seqnum.Value(tcp.AckNumber())
   154  
   155  	// Ignore segment if ack is present but not acceptable.
   156  	if ackPresent && !(ack-1).InRange(t.outbound.una, t.outbound.nxt) {
   157  		return ResultConnecting
   158  	}
   159  
   160  	// If reset is specified, we will let the packet through no matter what
   161  	// but we will also destroy the connection if the ACK is present (and
   162  	// implicitly acceptable).
   163  	if flags&header.TCPFlagRst != 0 {
   164  		if ackPresent {
   165  			t.inbound.rstSeen = true
   166  			return ResultReset
   167  		}
   168  		return ResultConnecting
   169  	}
   170  
   171  	// Ignore segment if SYN is not set.
   172  	if flags&header.TCPFlagSyn == 0 {
   173  		return ResultConnecting
   174  	}
   175  
   176  	// Update state informed by this SYN.
   177  	irs := seqnum.Value(tcp.SequenceNumber())
   178  	t.inbound.una = irs
   179  	t.inbound.nxt = irs.Add(logicalLen(tcp))
   180  	t.inbound.end += irs
   181  
   182  	t.outbound.end = t.outbound.una.Add(seqnum.Size(tcp.WindowSize()))
   183  
   184  	// If the ACK was set (it is acceptable), update our unacknowledgement
   185  	// tracking.
   186  	if ackPresent {
   187  		// Advance the "una" and "end" indices of the outbound stream.
   188  		if t.outbound.una.LessThan(ack) {
   189  			t.outbound.una = ack
   190  		}
   191  
   192  		if end := ack.Add(seqnum.Size(tcp.WindowSize())); t.outbound.end.LessThan(end) {
   193  			t.outbound.end = end
   194  		}
   195  	}
   196  
   197  	// Update handlers so that new calls will be handled by new state.
   198  	t.handlerInbound = allOtherInbound
   199  	t.handlerOutbound = allOtherOutbound
   200  
   201  	return ResultAlive
   202  }
   203  
   204  // synSentStateOutbound is the state handler for outbound segments when the
   205  // connection is in SYN-SENT state.
   206  func synSentStateOutbound(t *TCB, tcp header.TCP) Result {
   207  	// Drop outbound segments that aren't retransmits of the original one.
   208  	if tcp.Flags() != header.TCPFlagSyn ||
   209  		tcp.SequenceNumber() != uint32(t.outbound.una) {
   210  		return ResultDrop
   211  	}
   212  
   213  	// Update the receive window. We only remember the largest value seen.
   214  	if wnd := seqnum.Value(tcp.WindowSize()); wnd > t.inbound.end {
   215  		t.inbound.end = wnd
   216  	}
   217  
   218  	return ResultConnecting
   219  }
   220  
   221  // update updates the state of inbound and outbound streams, given the supplied
   222  // inbound segment. For outbound segments, this same function can be called with
   223  // swapped inbound/outbound streams.
   224  func update(tcp header.TCP, inbound, outbound *stream, firstFin **stream) Result {
   225  	// Ignore segments out of the window.
   226  	s := seqnum.Value(tcp.SequenceNumber())
   227  	if !inbound.acceptable(s, dataLen(tcp)) {
   228  		return ResultAlive
   229  	}
   230  
   231  	flags := tcp.Flags()
   232  	if flags&header.TCPFlagRst != 0 {
   233  		inbound.rstSeen = true
   234  		return ResultReset
   235  	}
   236  
   237  	// Ignore segments that don't have the ACK flag, and those with the SYN
   238  	// flag.
   239  	if flags&header.TCPFlagAck == 0 || flags&header.TCPFlagSyn != 0 {
   240  		return ResultAlive
   241  	}
   242  
   243  	// Ignore segments that acknowledge not yet sent data.
   244  	ack := seqnum.Value(tcp.AckNumber())
   245  	if outbound.nxt.LessThan(ack) {
   246  		return ResultAlive
   247  	}
   248  
   249  	// Advance the "una" and "end" indices of the outbound stream.
   250  	if outbound.una.LessThan(ack) {
   251  		outbound.una = ack
   252  	}
   253  
   254  	if end := ack.Add(seqnum.Size(tcp.WindowSize())); outbound.end.LessThan(end) {
   255  		outbound.end = end
   256  	}
   257  
   258  	// Advance the "nxt" index of the inbound stream.
   259  	end := s.Add(logicalLen(tcp))
   260  	if inbound.nxt.LessThan(end) {
   261  		inbound.nxt = end
   262  	}
   263  
   264  	// Note the index of the FIN segment. And stash away a pointer to the
   265  	// first stream to see a FIN.
   266  	if flags&header.TCPFlagFin != 0 && !inbound.finSeen {
   267  		inbound.finSeen = true
   268  		inbound.fin = end - 1
   269  
   270  		if *firstFin == nil {
   271  			*firstFin = inbound
   272  		}
   273  	}
   274  
   275  	return ResultAlive
   276  }
   277  
   278  // allOtherInbound is the state handler for inbound segments in all states
   279  // except SYN-SENT.
   280  func allOtherInbound(t *TCB, tcp header.TCP) Result {
   281  	return t.adaptResult(update(tcp, &t.inbound, &t.outbound, &t.firstFin))
   282  }
   283  
   284  // allOtherOutbound is the state handler for outbound segments in all states
   285  // except SYN-SENT.
   286  func allOtherOutbound(t *TCB, tcp header.TCP) Result {
   287  	return t.adaptResult(update(tcp, &t.outbound, &t.inbound, &t.firstFin))
   288  }
   289  
   290  // streams holds the state of a TCP unidirectional stream.
   291  type stream struct {
   292  	// The interval [una, end) is the allowed interval as defined by the
   293  	// receiver, i.e., anything less than una has already been acknowledged
   294  	// and anything greater than or equal to end is beyond the receiver
   295  	// window. The interval [una, nxt) is the acknowledgable range, whose
   296  	// right edge indicates the sequence number of the next byte to be sent
   297  	// by the sender, i.e., anything greater than or equal to nxt hasn't
   298  	// been sent yet.
   299  	una seqnum.Value
   300  	nxt seqnum.Value
   301  	end seqnum.Value
   302  
   303  	// finSeen indicates if a FIN has already been sent on this stream.
   304  	finSeen bool
   305  
   306  	// fin is the sequence number of the FIN. It is only valid after finSeen
   307  	// is set to true.
   308  	fin seqnum.Value
   309  
   310  	// rstSeen indicates if a RST has already been sent on this stream.
   311  	rstSeen bool
   312  }
   313  
   314  // acceptable determines if the segment with the given sequence number and data
   315  // length is acceptable, i.e., if it's within the [una, end) window or, in case
   316  // the window is zero, if it's a packet with no payload and sequence number
   317  // equal to una.
   318  func (s *stream) acceptable(segSeq seqnum.Value, segLen seqnum.Size) bool {
   319  	return header.Acceptable(segSeq, segLen, s.una, s.end)
   320  }
   321  
   322  // closed determines if the stream has already been closed. This happens when
   323  // a FIN has been set by the sender and acknowledged by the receiver.
   324  func (s *stream) closed() bool {
   325  	return s.finSeen && s.fin.LessThan(s.una)
   326  }
   327  
   328  // dataLen returns the length of the TCP segment payload.
   329  func dataLen(tcp header.TCP) seqnum.Size {
   330  	return seqnum.Size(len(tcp) - int(tcp.DataOffset()))
   331  }
   332  
   333  // logicalLen calculates the logical length of the TCP segment.
   334  func logicalLen(tcp header.TCP) seqnum.Size {
   335  	l := dataLen(tcp)
   336  	flags := tcp.Flags()
   337  	if flags&header.TCPFlagSyn != 0 {
   338  		l++
   339  	}
   340  	if flags&header.TCPFlagFin != 0 {
   341  		l++
   342  	}
   343  	return l
   344  }
   345  
   346  // IsEmpty returns true if tcb is not initialized.
   347  func (t *TCB) IsEmpty() bool {
   348  	if t.inbound != (stream{}) || t.outbound != (stream{}) {
   349  		return false
   350  	}
   351  
   352  	if t.firstFin != nil || t.state != ResultDrop {
   353  		return false
   354  	}
   355  
   356  	return true
   357  }