inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/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  	"inet.af/netstack/tcpip/header"
    22  	"inet.af/netstack/tcpip/seqnum"
    23  )
    24  
    25  // Result is returned when the state of a TCB is updated in response to a
    26  // 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  	// ResultClosedByResponder indicates that the connection was gracefully
    44  	// closed, and the reply stream was closed first.
    45  	ResultClosedByResponder
    46  
    47  	// ResultClosedByOriginator indicates that the connection was gracefully
    48  	// closed, and the original stream was closed first.
    49  	ResultClosedByOriginator
    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  	reply    stream
    56  	original stream
    57  
    58  	// State handlers.
    59  	handlerReply    func(*TCB, header.TCP) Result
    60  	handlerOriginal 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.handlerReply = synSentStateReply
    72  	t.handlerOriginal = synSentStateOriginal
    73  
    74  	iss := seqnum.Value(initialSyn.SequenceNumber())
    75  	t.original.una = iss
    76  	t.original.nxt = iss.Add(logicalLen(initialSyn))
    77  	t.original.end = t.original.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 server.
    82  	t.reply.una = 0
    83  	t.reply.nxt = 0
    84  	t.reply.end = seqnum.Value(initialSyn.WindowSize())
    85  	t.state = ResultConnecting
    86  	return t.state
    87  }
    88  
    89  // UpdateStateReply updates the state of the TCB based on the supplied reply
    90  // segment.
    91  func (t *TCB) UpdateStateReply(tcp header.TCP) Result {
    92  	st := t.handlerReply(t, tcp)
    93  	if st != ResultDrop {
    94  		t.state = st
    95  	}
    96  	return st
    97  }
    98  
    99  // UpdateStateOriginal updates the state of the TCB based on the supplied
   100  // original segment.
   101  func (t *TCB) UpdateStateOriginal(tcp header.TCP) Result {
   102  	st := t.handlerOriginal(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.reply.rstSeen && !t.original.rstSeen && (!t.reply.closed() || !t.original.closed())
   118  }
   119  
   120  // OriginalSendSequenceNumber returns the snd.NXT for the original stream.
   121  func (t *TCB) OriginalSendSequenceNumber() seqnum.Value {
   122  	return t.original.nxt
   123  }
   124  
   125  // ReplySendSequenceNumber returns the snd.NXT for the reply stream.
   126  func (t *TCB) ReplySendSequenceNumber() seqnum.Value {
   127  	return t.reply.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  // ClosedByOriginator or ClosedByResponder depending on which stream was closed
   134  // first.
   135  func (t *TCB) adaptResult(r Result) Result {
   136  	// Check the unmodified case.
   137  	if r != ResultAlive || !t.reply.closed() || !t.original.closed() {
   138  		return r
   139  	}
   140  
   141  	// Find out which was closed first.
   142  	if t.firstFin == &t.original {
   143  		return ResultClosedByOriginator
   144  	}
   145  
   146  	return ResultClosedByResponder
   147  }
   148  
   149  // synSentStateReply is the state handler for reply segments when the
   150  // connection is in SYN-SENT state.
   151  func synSentStateReply(t *TCB, tcp header.TCP) Result {
   152  	flags := tcp.Flags()
   153  	ackPresent := flags&header.TCPFlagAck != 0
   154  	ack := seqnum.Value(tcp.AckNumber())
   155  
   156  	// Ignore segment if ack is present but not acceptable.
   157  	if ackPresent && !(ack-1).InRange(t.original.una, t.original.nxt) {
   158  		return ResultConnecting
   159  	}
   160  
   161  	// If reset is specified, we will let the packet through no matter what
   162  	// but we will also destroy the connection if the ACK is present (and
   163  	// implicitly acceptable).
   164  	if flags&header.TCPFlagRst != 0 {
   165  		if ackPresent {
   166  			t.reply.rstSeen = true
   167  			return ResultReset
   168  		}
   169  		return ResultConnecting
   170  	}
   171  
   172  	// Ignore segment if SYN is not set.
   173  	if flags&header.TCPFlagSyn == 0 {
   174  		return ResultConnecting
   175  	}
   176  
   177  	// Update state informed by this SYN.
   178  	irs := seqnum.Value(tcp.SequenceNumber())
   179  	t.reply.una = irs
   180  	t.reply.nxt = irs.Add(logicalLen(tcp))
   181  	t.reply.end += irs
   182  
   183  	t.original.end = t.original.una.Add(seqnum.Size(tcp.WindowSize()))
   184  
   185  	// If the ACK was set (it is acceptable), update our unacknowledgement
   186  	// tracking.
   187  	if ackPresent {
   188  		// Advance the "una" and "end" indices of the original stream.
   189  		if t.original.una.LessThan(ack) {
   190  			t.original.una = ack
   191  		}
   192  
   193  		if end := ack.Add(seqnum.Size(tcp.WindowSize())); t.original.end.LessThan(end) {
   194  			t.original.end = end
   195  		}
   196  	}
   197  
   198  	// Update handlers so that new calls will be handled by new state.
   199  	t.handlerReply = allOtherReply
   200  	t.handlerOriginal = allOtherOriginal
   201  
   202  	return ResultAlive
   203  }
   204  
   205  // synSentStateOriginal is the state handler for original segments when the
   206  // connection is in SYN-SENT state.
   207  func synSentStateOriginal(t *TCB, tcp header.TCP) Result {
   208  	// Drop original segments that aren't retransmits of the original one.
   209  	if tcp.Flags() != header.TCPFlagSyn ||
   210  		tcp.SequenceNumber() != uint32(t.original.una) {
   211  		return ResultDrop
   212  	}
   213  
   214  	// Update the receive window. We only remember the largest value seen.
   215  	if wnd := seqnum.Value(tcp.WindowSize()); wnd > t.reply.end {
   216  		t.reply.end = wnd
   217  	}
   218  
   219  	return ResultConnecting
   220  }
   221  
   222  // update updates the state of reply and original streams, given the supplied
   223  // reply segment. For original segments, this same function can be called with
   224  // swapped reply/original streams.
   225  func update(tcp header.TCP, reply, original *stream, firstFin **stream) Result {
   226  	// Ignore segments out of the window.
   227  	s := seqnum.Value(tcp.SequenceNumber())
   228  	if !reply.acceptable(s, dataLen(tcp)) {
   229  		return ResultAlive
   230  	}
   231  
   232  	flags := tcp.Flags()
   233  	if flags&header.TCPFlagRst != 0 {
   234  		reply.rstSeen = true
   235  		return ResultReset
   236  	}
   237  
   238  	// Ignore segments that don't have the ACK flag, and those with the SYN
   239  	// flag.
   240  	if flags&header.TCPFlagAck == 0 || flags&header.TCPFlagSyn != 0 {
   241  		return ResultAlive
   242  	}
   243  
   244  	// Ignore segments that acknowledge not yet sent data.
   245  	ack := seqnum.Value(tcp.AckNumber())
   246  	if original.nxt.LessThan(ack) {
   247  		return ResultAlive
   248  	}
   249  
   250  	// Advance the "una" and "end" indices of the original stream.
   251  	if original.una.LessThan(ack) {
   252  		original.una = ack
   253  	}
   254  
   255  	if end := ack.Add(seqnum.Size(tcp.WindowSize())); original.end.LessThan(end) {
   256  		original.end = end
   257  	}
   258  
   259  	// Advance the "nxt" index of the reply stream.
   260  	end := s.Add(logicalLen(tcp))
   261  	if reply.nxt.LessThan(end) {
   262  		reply.nxt = end
   263  	}
   264  
   265  	// Note the index of the FIN segment. And stash away a pointer to the
   266  	// first stream to see a FIN.
   267  	if flags&header.TCPFlagFin != 0 && !reply.finSeen {
   268  		reply.finSeen = true
   269  		reply.fin = end - 1
   270  
   271  		if *firstFin == nil {
   272  			*firstFin = reply
   273  		}
   274  	}
   275  
   276  	return ResultAlive
   277  }
   278  
   279  // allOtherReply is the state handler for reply segments in all states
   280  // except SYN-SENT.
   281  func allOtherReply(t *TCB, tcp header.TCP) Result {
   282  	return t.adaptResult(update(tcp, &t.reply, &t.original, &t.firstFin))
   283  }
   284  
   285  // allOtherOriginal is the state handler for original segments in all states
   286  // except SYN-SENT.
   287  func allOtherOriginal(t *TCB, tcp header.TCP) Result {
   288  	return t.adaptResult(update(tcp, &t.original, &t.reply, &t.firstFin))
   289  }
   290  
   291  // streams holds the state of a TCP unidirectional stream.
   292  type stream struct {
   293  	// The interval [una, end) is the allowed interval as defined by the
   294  	// receiver, i.e., anything less than una has already been acknowledged
   295  	// and anything greater than or equal to end is beyond the receiver
   296  	// window. The interval [una, nxt) is the acknowledgable range, whose
   297  	// right edge indicates the sequence number of the next byte to be sent
   298  	// by the sender, i.e., anything greater than or equal to nxt hasn't
   299  	// been sent yet.
   300  	una seqnum.Value
   301  	nxt seqnum.Value
   302  	end seqnum.Value
   303  
   304  	// finSeen indicates if a FIN has already been sent on this stream.
   305  	finSeen bool
   306  
   307  	// fin is the sequence number of the FIN. It is only valid after finSeen
   308  	// is set to true.
   309  	fin seqnum.Value
   310  
   311  	// rstSeen indicates if a RST has already been sent on this stream.
   312  	rstSeen bool
   313  }
   314  
   315  // acceptable determines if the segment with the given sequence number and data
   316  // length is acceptable, i.e., if it's within the [una, end) window or, in case
   317  // the window is zero, if it's a packet with no payload and sequence number
   318  // equal to una.
   319  func (s *stream) acceptable(segSeq seqnum.Value, segLen seqnum.Size) bool {
   320  	return header.Acceptable(segSeq, segLen, s.una, s.end)
   321  }
   322  
   323  // closed determines if the stream has already been closed. This happens when
   324  // a FIN has been set by the sender and acknowledged by the receiver.
   325  func (s *stream) closed() bool {
   326  	return s.finSeen && s.fin.LessThan(s.una)
   327  }
   328  
   329  // dataLen returns the length of the TCP segment payload.
   330  func dataLen(tcp header.TCP) seqnum.Size {
   331  	return seqnum.Size(len(tcp) - int(tcp.DataOffset()))
   332  }
   333  
   334  // logicalLen calculates the logical length of the TCP segment.
   335  func logicalLen(tcp header.TCP) seqnum.Size {
   336  	l := dataLen(tcp)
   337  	flags := tcp.Flags()
   338  	if flags&header.TCPFlagSyn != 0 {
   339  		l++
   340  	}
   341  	if flags&header.TCPFlagFin != 0 {
   342  		l++
   343  	}
   344  	return l
   345  }
   346  
   347  // IsEmpty returns true if tcb is not initialized.
   348  func (t *TCB) IsEmpty() bool {
   349  	if t.reply != (stream{}) || t.original != (stream{}) {
   350  		return false
   351  	}
   352  
   353  	if t.firstFin != nil || t.state != ResultDrop {
   354  		return false
   355  	}
   356  
   357  	return true
   358  }