github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/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/FlowerWrong/netstack/tcpip/header"
    22  	"github.com/FlowerWrong/netstack/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  // IsAlive returns true as long as the connection is established(Alive)
   110  // or connecting state.
   111  func (t *TCB) IsAlive() bool {
   112  	return !t.inbound.rstSeen && !t.outbound.rstSeen && (!t.inbound.closed() || !t.outbound.closed())
   113  }
   114  
   115  // OutboundSendSequenceNumber returns the snd.NXT for the outbound stream.
   116  func (t *TCB) OutboundSendSequenceNumber() seqnum.Value {
   117  	return t.outbound.nxt
   118  }
   119  
   120  // InboundSendSequenceNumber returns the snd.NXT for the inbound stream.
   121  func (t *TCB) InboundSendSequenceNumber() seqnum.Value {
   122  	return t.inbound.nxt
   123  }
   124  
   125  // adapResult modifies the supplied "Result" according to the state of the TCB;
   126  // if r is anything other than "Alive", or if one of the streams isn't closed
   127  // yet, it is returned unmodified. Otherwise it's converted to either
   128  // ClosedBySelf or ClosedByPeer depending on which stream was closed first.
   129  func (t *TCB) adaptResult(r Result) Result {
   130  	// Check the unmodified case.
   131  	if r != ResultAlive || !t.inbound.closed() || !t.outbound.closed() {
   132  		return r
   133  	}
   134  
   135  	// Find out which was closed first.
   136  	if t.firstFin == &t.outbound {
   137  		return ResultClosedBySelf
   138  	}
   139  
   140  	return ResultClosedByPeer
   141  }
   142  
   143  // synSentStateInbound is the state handler for inbound segments when the
   144  // connection is in SYN-SENT state.
   145  func synSentStateInbound(t *TCB, tcp header.TCP) Result {
   146  	flags := tcp.Flags()
   147  	ackPresent := flags&header.TCPFlagAck != 0
   148  	ack := seqnum.Value(tcp.AckNumber())
   149  
   150  	// Ignore segment if ack is present but not acceptable.
   151  	if ackPresent && !(ack-1).InRange(t.outbound.una, t.outbound.nxt) {
   152  		return ResultConnecting
   153  	}
   154  
   155  	// If reset is specified, we will let the packet through no matter what
   156  	// but we will also destroy the connection if the ACK is present (and
   157  	// implicitly acceptable).
   158  	if flags&header.TCPFlagRst != 0 {
   159  		if ackPresent {
   160  			t.inbound.rstSeen = true
   161  			return ResultReset
   162  		}
   163  		return ResultConnecting
   164  	}
   165  
   166  	// Ignore segment if SYN is not set.
   167  	if flags&header.TCPFlagSyn == 0 {
   168  		return ResultConnecting
   169  	}
   170  
   171  	// Update state informed by this SYN.
   172  	irs := seqnum.Value(tcp.SequenceNumber())
   173  	t.inbound.una = irs
   174  	t.inbound.nxt = irs.Add(logicalLen(tcp))
   175  	t.inbound.end += irs
   176  
   177  	t.outbound.end = t.outbound.una.Add(seqnum.Size(tcp.WindowSize()))
   178  
   179  	// If the ACK was set (it is acceptable), update our unacknowledgement
   180  	// tracking.
   181  	if ackPresent {
   182  		// Advance the "una" and "end" indices of the outbound stream.
   183  		if t.outbound.una.LessThan(ack) {
   184  			t.outbound.una = ack
   185  		}
   186  
   187  		if end := ack.Add(seqnum.Size(tcp.WindowSize())); t.outbound.end.LessThan(end) {
   188  			t.outbound.end = end
   189  		}
   190  	}
   191  
   192  	// Update handlers so that new calls will be handled by new state.
   193  	t.handlerInbound = allOtherInbound
   194  	t.handlerOutbound = allOtherOutbound
   195  
   196  	return ResultAlive
   197  }
   198  
   199  // synSentStateOutbound is the state handler for outbound segments when the
   200  // connection is in SYN-SENT state.
   201  func synSentStateOutbound(t *TCB, tcp header.TCP) Result {
   202  	// Drop outbound segments that aren't retransmits of the original one.
   203  	if tcp.Flags() != header.TCPFlagSyn ||
   204  		tcp.SequenceNumber() != uint32(t.outbound.una) {
   205  		return ResultDrop
   206  	}
   207  
   208  	// Update the receive window. We only remember the largest value seen.
   209  	if wnd := seqnum.Value(tcp.WindowSize()); wnd > t.inbound.end {
   210  		t.inbound.end = wnd
   211  	}
   212  
   213  	return ResultConnecting
   214  }
   215  
   216  // update updates the state of inbound and outbound streams, given the supplied
   217  // inbound segment. For outbound segments, this same function can be called with
   218  // swapped inbound/outbound streams.
   219  func update(tcp header.TCP, inbound, outbound *stream, firstFin **stream) Result {
   220  	// Ignore segments out of the window.
   221  	s := seqnum.Value(tcp.SequenceNumber())
   222  	if !inbound.acceptable(s, dataLen(tcp)) {
   223  		return ResultAlive
   224  	}
   225  
   226  	flags := tcp.Flags()
   227  	if flags&header.TCPFlagRst != 0 {
   228  		inbound.rstSeen = true
   229  		return ResultReset
   230  	}
   231  
   232  	// Ignore segments that don't have the ACK flag, and those with the SYN
   233  	// flag.
   234  	if flags&header.TCPFlagAck == 0 || flags&header.TCPFlagSyn != 0 {
   235  		return ResultAlive
   236  	}
   237  
   238  	// Ignore segments that acknowledge not yet sent data.
   239  	ack := seqnum.Value(tcp.AckNumber())
   240  	if outbound.nxt.LessThan(ack) {
   241  		return ResultAlive
   242  	}
   243  
   244  	// Advance the "una" and "end" indices of the outbound stream.
   245  	if outbound.una.LessThan(ack) {
   246  		outbound.una = ack
   247  	}
   248  
   249  	if end := ack.Add(seqnum.Size(tcp.WindowSize())); outbound.end.LessThan(end) {
   250  		outbound.end = end
   251  	}
   252  
   253  	// Advance the "nxt" index of the inbound stream.
   254  	end := s.Add(logicalLen(tcp))
   255  	if inbound.nxt.LessThan(end) {
   256  		inbound.nxt = end
   257  	}
   258  
   259  	// Note the index of the FIN segment. And stash away a pointer to the
   260  	// first stream to see a FIN.
   261  	if flags&header.TCPFlagFin != 0 && !inbound.finSeen {
   262  		inbound.finSeen = true
   263  		inbound.fin = end - 1
   264  
   265  		if *firstFin == nil {
   266  			*firstFin = inbound
   267  		}
   268  	}
   269  
   270  	return ResultAlive
   271  }
   272  
   273  // allOtherInbound is the state handler for inbound segments in all states
   274  // except SYN-SENT.
   275  func allOtherInbound(t *TCB, tcp header.TCP) Result {
   276  	return t.adaptResult(update(tcp, &t.inbound, &t.outbound, &t.firstFin))
   277  }
   278  
   279  // allOtherOutbound is the state handler for outbound segments in all states
   280  // except SYN-SENT.
   281  func allOtherOutbound(t *TCB, tcp header.TCP) Result {
   282  	return t.adaptResult(update(tcp, &t.outbound, &t.inbound, &t.firstFin))
   283  }
   284  
   285  // streams holds the state of a TCP unidirectional stream.
   286  type stream struct {
   287  	// The interval [una, end) is the allowed interval as defined by the
   288  	// receiver, i.e., anything less than una has already been acknowledged
   289  	// and anything greater than or equal to end is beyond the receiver
   290  	// window. The interval [una, nxt) is the acknowledgable range, whose
   291  	// right edge indicates the sequence number of the next byte to be sent
   292  	// by the sender, i.e., anything greater than or equal to nxt hasn't
   293  	// been sent yet.
   294  	una seqnum.Value
   295  	nxt seqnum.Value
   296  	end seqnum.Value
   297  
   298  	// finSeen indicates if a FIN has already been sent on this stream.
   299  	finSeen bool
   300  
   301  	// fin is the sequence number of the FIN. It is only valid after finSeen
   302  	// is set to true.
   303  	fin seqnum.Value
   304  
   305  	// rstSeen indicates if a RST has already been sent on this stream.
   306  	rstSeen bool
   307  }
   308  
   309  // acceptable determines if the segment with the given sequence number and data
   310  // length is acceptable, i.e., if it's within the [una, end) window or, in case
   311  // the window is zero, if it's a packet with no payload and sequence number
   312  // equal to una.
   313  func (s *stream) acceptable(segSeq seqnum.Value, segLen seqnum.Size) bool {
   314  	wnd := s.una.Size(s.end)
   315  	if wnd == 0 {
   316  		return segLen == 0 && segSeq == s.una
   317  	}
   318  
   319  	// Make sure [segSeq, seqSeq+segLen) is non-empty.
   320  	if segLen == 0 {
   321  		segLen = 1
   322  	}
   323  
   324  	return seqnum.Overlap(s.una, wnd, segSeq, segLen)
   325  }
   326  
   327  // closed determines if the stream has already been closed. This happens when
   328  // a FIN has been set by the sender and acknowledged by the receiver.
   329  func (s *stream) closed() bool {
   330  	return s.finSeen && s.fin.LessThan(s.una)
   331  }
   332  
   333  // dataLen returns the length of the TCP segment payload.
   334  func dataLen(tcp header.TCP) seqnum.Size {
   335  	return seqnum.Size(len(tcp) - int(tcp.DataOffset()))
   336  }
   337  
   338  // logicalLen calculates the logical length of the TCP segment.
   339  func logicalLen(tcp header.TCP) seqnum.Size {
   340  	l := dataLen(tcp)
   341  	flags := tcp.Flags()
   342  	if flags&header.TCPFlagSyn != 0 {
   343  		l++
   344  	}
   345  	if flags&header.TCPFlagFin != 0 {
   346  		l++
   347  	}
   348  	return l
   349  }