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 }