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 }